- On January 1, 2020 this library will no longer support Python 2 on the latest released version. - Previously released library versions will continue to be available. For more information please + As of January 1, 2020 this library no longer supports Python 2 on the latest released version. + Library versions released prior to that date will continue to be available. For more information please visit Python 2 support on Google Cloud.
{% block body %} {% endblock %} diff --git a/docs/conf.py b/docs/conf.py index 0a78149..ca73421 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("..")) -__version__ = "0.1.0" +__version__ = "" # -- General configuration ------------------------------------------------ @@ -38,21 +38,18 @@ "sphinx.ext.napoleon", "sphinx.ext.todo", "sphinx.ext.viewcode", + "recommonmark", ] # autodoc/autosummary flags autoclass_content = "both" -autodoc_default_flags = ["members"] +autodoc_default_options = {"members": True} autosummary_generate = True # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] -# Allow markdown includes (so releases.md can include CHANGLEOG.md) -# http://www.sphinx-doc.org/en/master/markdown.html -source_parsers = {".md": "recommonmark.parser.CommonMarkParser"} - # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] @@ -66,7 +63,7 @@ # General information about the project. project = u"google-cloud-core" -copyright = u"2017, Google" +copyright = u"2019, Google" author = u"Google APIs" # The version info for the project you're documenting, acts as replacement for @@ -133,9 +130,9 @@ # further. For a list of options available for each theme, see the # documentation. html_theme_options = { - "description": "Google Cloud Client Libraries for Python", + "description": "Google Cloud Client Libraries for google-cloud-core", "github_user": "googleapis", - "github_repo": "google-cloud-python", + "github_repo": "python-cloud-core", "github_banner": True, "font_family": "'Roboto', Georgia, sans", "head_font_family": "'Roboto', Georgia, serif", @@ -293,7 +290,13 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, "google-cloud-core", u"google-cloud-core Documentation", [author], 1) + ( + master_doc, + "google-cloud-core", + u"google-cloud-core Documentation", + [author], + 1, + ) ] # If true, show URL addresses after external links. @@ -312,7 +315,7 @@ u"google-cloud-core Documentation", author, "google-cloud-core", - "GAPIC library for the {metadata.shortName} v1beta1 service", + "google-cloud-core Library", "APIs", ) ] @@ -333,14 +336,13 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { "python": ("http://python.readthedocs.org/en/latest/", None), - "gax": ("https://gax-python.readthedocs.org/en/latest/", None), "google-auth": ("https://google-auth.readthedocs.io/en/stable", None), - "google-gax": ("https://gax-python.readthedocs.io/en/latest/", None), - "google.api_core": ("https://googleapis.dev/python/google-api-core/latest", None), + "google.api_core": ( + "https://googleapis.dev/python/google-api-core/latest/", + None, + ), "grpc": ("https://grpc.io/grpc/python/", None), - "requests": ("https://requests.kennethreitz.org/en/stable/", None), - "fastavro": ("https://fastavro.readthedocs.io/en/stable/", None), - "pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None), + } diff --git a/google/cloud/client.py b/google/cloud/client.py index f204ba8..6b9117f 100644 --- a/google/cloud/client.py +++ b/google/cloud/client.py @@ -20,6 +20,8 @@ import six +import google.api_core.client_options +import google.api_core.exceptions import google.auth import google.auth.credentials import google.auth.transport.requests @@ -102,6 +104,8 @@ class Client(_ClientFactoryMixin): (Optional) The OAuth2 Credentials to use for this client. If not passed (and if no ``_http`` object is passed), falls back to the default inferred from the environment. + client_options (google.api_core.client_options.ClientOptions): + (Optional) Custom options for the client. _http (requests.Session): (Optional) HTTP object to make requests. Can be any object that defines ``request()`` with the same interface as @@ -123,16 +127,35 @@ class Client(_ClientFactoryMixin): Needs to be set by subclasses. """ - def __init__(self, credentials=None, _http=None): - if credentials is not None and not isinstance( - credentials, google.auth.credentials.Credentials - ): + def __init__(self, credentials=None, _http=None, client_options=None): + if isinstance(client_options, dict): + client_options = google.api_core.client_options.from_dict(client_options) + if client_options is None: + client_options = google.api_core.client_options.ClientOptions() + + if credentials and client_options.credentials_file: + raise google.api_core.exceptions.DuplicateCredentialArgs( + "'credentials' and 'client_options.credentials_file' are mutually exclusive.") + + if credentials and not isinstance(credentials, google.auth.credentials.Credentials): raise ValueError(_GOOGLE_AUTH_CREDENTIALS_HELP) - if credentials is None and _http is None: - credentials, _ = google.auth.default() + + scopes = client_options.scopes or self.SCOPE + + # if no http is provided, credentials must exist + if not _http and credentials is None: + if client_options.credentials_file: + credentials, _ = google.auth.load_credentials_from_file( + client_options.credentials_file, scopes=scopes) + else: + credentials, _ = google.auth.default(scopes=scopes) + self._credentials = google.auth.credentials.with_scopes_if_required( - credentials, self.SCOPE - ) + credentials, scopes=scopes) + + if client_options.quota_project_id: + self._credentials = self._credentials.with_quota_project(client_options.quota_project_id) + self._http_internal = _http def __getstate__(self): @@ -222,6 +245,6 @@ class ClientWithProject(Client, _ClientProjectMixin): _SET_PROJECT = True # Used by from_service_account_json() - def __init__(self, project=None, credentials=None, _http=None): + def __init__(self, project=None, credentials=None, client_options=None, _http=None): _ClientProjectMixin.__init__(self, project=project) - Client.__init__(self, credentials=credentials, _http=_http) + Client.__init__(self, credentials=credentials, client_options=client_options, _http=_http) diff --git a/noxfile.py b/noxfile.py index 5ac660e..9856032 100644 --- a/noxfile.py +++ b/noxfile.py @@ -19,8 +19,6 @@ import nox -LOCAL_DEPS = (os.path.join("..", "api_core"),) - def default(session): """Default unit test session. @@ -32,8 +30,6 @@ def default(session): # Install all test dependencies, then install local packages in-place. session.install("mock", "pytest", "pytest-cov", "grpcio >= 1.0.2") - for local_dep in LOCAL_DEPS: - session.install("-e", local_dep) session.install("-e", ".") # Run py.test against the unit tests. @@ -45,13 +41,13 @@ def default(session): "--cov-append", "--cov-config=.coveragerc", "--cov-report=", - "--cov-fail-under=97", + "--cov-fail-under=0", os.path.join("tests", "unit"), *session.posargs ) -@nox.session(python=["2.7", "3.5", "3.6", "3.7"]) +@nox.session(python=["2.7", "3.5", "3.6", "3.7", "3.8"]) def unit(session): """Default unit test session.""" default(session) @@ -108,4 +104,4 @@ def docs(session): os.path.join("docs", "_build", "doctrees", ""), os.path.join("docs", ""), os.path.join("docs", "_build", "html", ""), - ) \ No newline at end of file + ) diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..4fa9493 --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "config:base", ":preserveSemverRanges" + ] +} diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh new file mode 100755 index 0000000..ff599eb --- /dev/null +++ b/scripts/decrypt-secrets.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT=$( dirname "$DIR" ) + +# Work from the project root. +cd $ROOT + +# Use SECRET_MANAGER_PROJECT if set, fallback to cloud-devrel-kokoro-resources. +PROJECT_ID="${SECRET_MANAGER_PROJECT:-cloud-devrel-kokoro-resources}" + +gcloud secrets versions access latest --secret="python-docs-samples-test-env" \ + > testing/test-env.sh +gcloud secrets versions access latest \ + --secret="python-docs-samples-service-account" \ + > testing/service-account.json +gcloud secrets versions access latest \ + --secret="python-docs-samples-client-secrets" \ + > testing/client-secrets.json \ No newline at end of file diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py new file mode 100644 index 0000000..d309d6e --- /dev/null +++ b/scripts/readme-gen/readme_gen.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +# Copyright 2016 Google Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generates READMEs using configuration defined in yaml.""" + +import argparse +import io +import os +import subprocess + +import jinja2 +import yaml + + +jinja_env = jinja2.Environment( + trim_blocks=True, + loader=jinja2.FileSystemLoader( + os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates')))) + +README_TMPL = jinja_env.get_template('README.tmpl.rst') + + +def get_help(file): + return subprocess.check_output(['python', file, '--help']).decode() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('source') + parser.add_argument('--destination', default='README.rst') + + args = parser.parse_args() + + source = os.path.abspath(args.source) + root = os.path.dirname(source) + destination = os.path.join(root, args.destination) + + jinja_env.globals['get_help'] = get_help + + with io.open(source, 'r') as f: + config = yaml.load(f) + + # This allows get_help to execute in the right directory. + os.chdir(root) + + output = README_TMPL.render(config) + + with io.open(destination, 'w') as f: + f.write(output) + + +if __name__ == '__main__': + main() diff --git a/scripts/readme-gen/templates/README.tmpl.rst b/scripts/readme-gen/templates/README.tmpl.rst new file mode 100644 index 0000000..4fd2397 --- /dev/null +++ b/scripts/readme-gen/templates/README.tmpl.rst @@ -0,0 +1,87 @@ +{# The following line is a lie. BUT! Once jinja2 is done with it, it will + become truth! #} +.. This file is automatically generated. Do not edit this file directly. + +{{product.name}} Python Samples +=============================================================================== + +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/README.rst + + +This directory contains samples for {{product.name}}. {{product.description}} + +{{description}} + +.. _{{product.name}}: {{product.url}} + +{% if required_api_url %} +To run the sample, you need to enable the API at: {{required_api_url}} +{% endif %} + +{% if required_role %} +To run the sample, you need to have `{{required_role}}` role. +{% endif %} + +{{other_required_steps}} + +{% if setup %} +Setup +------------------------------------------------------------------------------- + +{% for section in setup %} + +{% include section + '.tmpl.rst' %} + +{% endfor %} +{% endif %} + +{% if samples %} +Samples +------------------------------------------------------------------------------- + +{% for sample in samples %} +{{sample.name}} ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +{% if not sample.hide_cloudshell_button %} +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/{{sample.file}},{{folder}}/README.rst +{% endif %} + + +{{sample.description}} + +To run this sample: + +.. code-block:: bash + + $ python {{sample.file}} +{% if sample.show_help %} + + {{get_help(sample.file)|indent}} +{% endif %} + + +{% endfor %} +{% endif %} + +{% if cloud_client_library %} + +The client library +------------------------------------------------------------------------------- + +This sample uses the `Google Cloud Client Library for Python`_. +You can read the documentation for more details on API usage and use GitHub +to `browse the source`_ and `report issues`_. + +.. _Google Cloud Client Library for Python: + https://googlecloudplatform.github.io/google-cloud-python/ +.. _browse the source: + https://github.com/GoogleCloudPlatform/google-cloud-python +.. _report issues: + https://github.com/GoogleCloudPlatform/google-cloud-python/issues + +{% endif %} + +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/scripts/readme-gen/templates/auth.tmpl.rst b/scripts/readme-gen/templates/auth.tmpl.rst new file mode 100644 index 0000000..1446b94 --- /dev/null +++ b/scripts/readme-gen/templates/auth.tmpl.rst @@ -0,0 +1,9 @@ +Authentication +++++++++++++++ + +This sample requires you to have authentication setup. Refer to the +`Authentication Getting Started Guide`_ for instructions on setting up +credentials for applications. + +.. _Authentication Getting Started Guide: + https://cloud.google.com/docs/authentication/getting-started diff --git a/scripts/readme-gen/templates/auth_api_key.tmpl.rst b/scripts/readme-gen/templates/auth_api_key.tmpl.rst new file mode 100644 index 0000000..11957ce --- /dev/null +++ b/scripts/readme-gen/templates/auth_api_key.tmpl.rst @@ -0,0 +1,14 @@ +Authentication +++++++++++++++ + +Authentication for this service is done via an `API Key`_. To obtain an API +Key: + +1. Open the `Cloud Platform Console`_ +2. Make sure that billing is enabled for your project. +3. From the **Credentials** page, create a new **API Key** or use an existing + one for your project. + +.. _API Key: + https://developers.google.com/api-client-library/python/guide/aaa_apikeys +.. _Cloud Console: https://console.cloud.google.com/project?_ diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst new file mode 100644 index 0000000..a0406db --- /dev/null +++ b/scripts/readme-gen/templates/install_deps.tmpl.rst @@ -0,0 +1,29 @@ +Install Dependencies +++++++++++++++++++++ + +#. Clone python-docs-samples and change directory to the sample directory you want to use. + + .. code-block:: bash + + $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git + +#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions. + + .. _Python Development Environment Setup Guide: + https://cloud.google.com/python/setup + +#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. + + .. code-block:: bash + + $ virtualenv env + $ source env/bin/activate + +#. Install the dependencies needed to run the samples. + + .. code-block:: bash + + $ pip install -r requirements.txt + +.. _pip: https://pip.pypa.io/ +.. _virtualenv: https://virtualenv.pypa.io/ diff --git a/scripts/readme-gen/templates/install_portaudio.tmpl.rst b/scripts/readme-gen/templates/install_portaudio.tmpl.rst new file mode 100644 index 0000000..5ea33d1 --- /dev/null +++ b/scripts/readme-gen/templates/install_portaudio.tmpl.rst @@ -0,0 +1,35 @@ +Install PortAudio ++++++++++++++++++ + +Install `PortAudio`_. This is required by the `PyAudio`_ library to stream +audio from your computer's microphone. PyAudio depends on PortAudio for cross-platform compatibility, and is installed differently depending on the +platform. + +* For Mac OS X, you can use `Homebrew`_:: + + brew install portaudio + + **Note**: if you encounter an error when running `pip install` that indicates + it can't find `portaudio.h`, try running `pip install` with the following + flags:: + + pip install --global-option='build_ext' \ + --global-option='-I/usr/local/include' \ + --global-option='-L/usr/local/lib' \ + pyaudio + +* For Debian / Ubuntu Linux:: + + apt-get install portaudio19-dev python-all-dev + +* Windows may work without having to install PortAudio explicitly (it will get + installed with PyAudio). + +For more details, see the `PyAudio installation`_ page. + + +.. _PyAudio: https://people.csail.mit.edu/hubert/pyaudio/ +.. _PortAudio: http://www.portaudio.com/ +.. _PyAudio installation: + https://people.csail.mit.edu/hubert/pyaudio/#downloads +.. _Homebrew: http://brew.sh diff --git a/setup.py b/setup.py index 3a04a44..eea8633 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ name = "google-cloud-core" description = "Google Cloud API client core library" -version = "1.3.0" +version = "1.4.0" # Should be one of: # 'Development Status :: 3 - Alpha' # 'Development Status :: 4 - Beta' @@ -60,7 +60,7 @@ author="Google LLC", author_email="googleapis-packages@google.com", license="Apache 2.0", - url="https://github.com/GoogleCloudPlatform/google-cloud-python", + url="https://github.com/googleapis/python-cloud-core", classifiers=[ release_status, "Intended Audience :: Developers", @@ -71,7 +71,8 @@ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", - 'Programming Language :: Python :: 3.7', + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Operating System :: OS Independent", "Topic :: Internet", ], @@ -80,7 +81,7 @@ namespace_packages=namespaces, install_requires=dependencies, extras_require=extras, - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', + python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", include_package_data=True, zip_safe=False, ) diff --git a/synth.metadata b/synth.metadata new file mode 100644 index 0000000..77b4f46 --- /dev/null +++ b/synth.metadata @@ -0,0 +1,18 @@ +{ + "sources": [ + { + "git": { + "name": ".", + "remote": "https://github.com/googleapis/python-cloud-core.git", + "sha": "5076ce7330e594fa0e1223e92eef61629508be71" + } + }, + { + "git": { + "name": "synthtool", + "remote": "https://github.com/googleapis/synthtool.git", + "sha": "303271797a360f8a439203413f13a160f2f5b3b4" + } + } + ] +} \ No newline at end of file diff --git a/synth.py b/synth.py new file mode 100644 index 0000000..29a5f77 --- /dev/null +++ b/synth.py @@ -0,0 +1,37 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This script is used to synthesize generated parts of this library.""" + +import re + +import synthtool as s +from synthtool import gcp + +common = gcp.CommonTemplates() + +# ---------------------------------------------------------------------------- +# Add templated files +# ---------------------------------------------------------------------------- +templated_files = common.py_library(cov_level=100) +s.move( + templated_files, + excludes=[ + "docs/multiprocessing.rst", + "noxfile.py", + ".flake8", + ".coveragerc", + "setup.cfg", + ], +) diff --git a/testing/.gitignore b/testing/.gitignore new file mode 100644 index 0000000..b05fbd6 --- /dev/null +++ b/testing/.gitignore @@ -0,0 +1,3 @@ +test-env.sh +service-account.json +client-secrets.json \ No newline at end of file diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 7aec69f..59e2ea0 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -50,14 +50,14 @@ def _make_one(self, *args, **kw): def test_unpickleable(self): import pickle - CREDENTIALS = _make_credentials() + credentials = _make_credentials() HTTP = object() - client_obj = self._make_one(credentials=CREDENTIALS, _http=HTTP) + client_obj = self._make_one(credentials=credentials, _http=HTTP) with self.assertRaises(pickle.PicklingError): pickle.dumps(client_obj) - def test_constructor_defaults(self): + def test_ctor_defaults(self): credentials = _make_credentials() patch = mock.patch("google.auth.default", return_value=(credentials, None)) @@ -66,9 +66,9 @@ def test_constructor_defaults(self): self.assertIs(client_obj._credentials, credentials) self.assertIsNone(client_obj._http_internal) - default.assert_called_once_with() + default.assert_called_once_with(scopes=None) - def test_constructor_explicit(self): + def test_ctor_explicit(self): credentials = _make_credentials() http = mock.sentinel.http client_obj = self._make_one(credentials=credentials, _http=http) @@ -76,12 +76,71 @@ def test_constructor_explicit(self): self.assertIs(client_obj._credentials, credentials) self.assertIs(client_obj._http_internal, http) - def test_constructor_bad_credentials(self): + def test_ctor_client_options_w_conflicting_creds(self): + from google.api_core.exceptions import DuplicateCredentialArgs + + credentials = _make_credentials() + client_options = {'credentials_file': '/path/to/creds.json'} + with self.assertRaises(DuplicateCredentialArgs): + self._make_one(credentials=credentials, client_options=client_options) + + def test_ctor_bad_credentials(self): credentials = mock.sentinel.credentials with self.assertRaises(ValueError): self._make_one(credentials=credentials) + def test_ctor_client_options_w_creds_file_scopes(self): + credentials = _make_credentials() + credentials_file = '/path/to/creds.json' + scopes = ['SCOPE1', 'SCOPE2'] + client_options = {'credentials_file': credentials_file, 'scopes': scopes} + + patch = mock.patch("google.auth.load_credentials_from_file", return_value=(credentials, None)) + with patch as load_credentials_from_file: + client_obj = self._make_one(client_options=client_options) + + self.assertIs(client_obj._credentials, credentials) + self.assertIsNone(client_obj._http_internal) + load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes) + + def test_ctor_client_options_w_quota_project(self): + credentials = _make_credentials() + quota_project_id = 'quota-project-123' + client_options = {'quota_project_id': quota_project_id} + + client_obj = self._make_one(credentials=credentials, client_options=client_options) + + self.assertIs(client_obj._credentials, credentials.with_quota_project.return_value) + credentials.with_quota_project.assert_called_once_with(quota_project_id) + + def test_ctor__http_property_existing(self): + credentials = _make_credentials() + http = object() + client = self._make_one(credentials=credentials, _http=http) + self.assertIs(client._http_internal, http) + self.assertIs(client._http, http) + + def test_ctor__http_property_new(self): + from google.cloud.client import _CREDENTIALS_REFRESH_TIMEOUT + + credentials = _make_credentials() + client = self._make_one(credentials=credentials) + self.assertIsNone(client._http_internal) + + authorized_session_patch = mock.patch( + "google.auth.transport.requests.AuthorizedSession", + return_value=mock.sentinel.http, + ) + with authorized_session_patch as AuthorizedSession: + self.assertIs(client._http, mock.sentinel.http) + # Check the mock. + AuthorizedSession.assert_called_once_with(credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT) + # Make sure the cached value is used on subsequent access. + self.assertIs(client._http_internal, mock.sentinel.http) + self.assertIs(client._http, mock.sentinel.http) + self.assertEqual(AuthorizedSession.call_count, 1) + def test_from_service_account_json(self): from google.cloud import _helpers @@ -114,32 +173,6 @@ def test_from_service_account_json_bad_args(self): mock.sentinel.filename, credentials=mock.sentinel.credentials ) - def test__http_property_existing(self): - credentials = _make_credentials() - http = object() - client = self._make_one(credentials=credentials, _http=http) - self.assertIs(client._http_internal, http) - self.assertIs(client._http, http) - - def test__http_property_new(self): - from google.cloud.client import _CREDENTIALS_REFRESH_TIMEOUT - credentials = _make_credentials() - client = self._make_one(credentials=credentials) - self.assertIsNone(client._http_internal) - - authorized_session_patch = mock.patch( - "google.auth.transport.requests.AuthorizedSession", - return_value=mock.sentinel.http, - ) - with authorized_session_patch as AuthorizedSession: - self.assertIs(client._http, mock.sentinel.http) - # Check the mock. - AuthorizedSession.assert_called_once_with(credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT) - # Make sure the cached value is used on subsequent access. - self.assertIs(client._http_internal, mock.sentinel.http) - self.assertIs(client._http, mock.sentinel.http) - self.assertEqual(AuthorizedSession.call_count, 1) - class TestClientWithProject(unittest.TestCase): @staticmethod @@ -167,7 +200,7 @@ def test_constructor_defaults(self): self.assertEqual(client_obj.project, project) self.assertIs(client_obj._credentials, credentials) self.assertIsNone(client_obj._http_internal) - default.assert_called_once_with() + default.assert_called_once_with(scopes=None) _determine_default_project.assert_called_once_with(None) def test_constructor_missing_project(self):