Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-google-cloud-core for openSUSE:Factory checked in at 2021-08-03 22:48:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-google-cloud-core (Old) and /work/SRC/openSUSE:Factory/.python-google-cloud-core.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-google-cloud-core" Tue Aug 3 22:48:39 2021 rev:9 rq:909842 version:1.7.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-google-cloud-core/python-google-cloud-core.changes 2021-04-14 10:11:23.241535054 +0200 +++ /work/SRC/openSUSE:Factory/.python-google-cloud-core.new.1899/python-google-cloud-core.changes 2021-08-03 22:48:53.380486097 +0200 @@ -1,0 +2,9 @@ +Tue Aug 3 00:43:52 UTC 2021 - Steve Kowalik <steven.kowa...@suse.com> + +- Update to 1.7.2: + * pass kwargs through in 'from_service_account_json' (#109) (efc4bbf) + * add 'Client.close' (#100) (7387fc8) + * add 'Client.from_service_account_info' factory (#54) (7e59360) +- Actually use %pytest macro for testing + +------------------------------------------------------------------- Old: ---- google-cloud-core-1.6.0.tar.gz New: ---- google-cloud-core-1.7.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-google-cloud-core.spec ++++++ --- /var/tmp/diff_new_pack.FvZaO8/_old 2021-08-03 22:48:53.828485557 +0200 +++ /var/tmp/diff_new_pack.FvZaO8/_new 2021-08-03 22:48:53.832485552 +0200 @@ -19,11 +19,10 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %bcond_without python2 Name: python-google-cloud-core -Version: 1.6.0 +Version: 1.7.2 Release: 0 Summary: Google Cloud API client core library License: Apache-2.0 -Group: Development/Languages/Python URL: https://github.com/googleapis/python-cloud-core Source: https://files.pythonhosted.org/packages/source/g/google-cloud-core/google-cloud-core-%{version}.tar.gz BuildRequires: %{python_module google-api-core >= 1.21.0} @@ -64,7 +63,7 @@ %python_expand %fdupes %{buildroot}%{$python_sitelib} %check -%python_exec setup.py test +%pytest %files %{python_files} %license LICENSE ++++++ google-cloud-core-1.6.0.tar.gz -> google-cloud-core-1.7.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.6.0/MANIFEST.in new/google-cloud-core-1.7.2/MANIFEST.in --- old/google-cloud-core-1.6.0/MANIFEST.in 2021-02-06 02:19:50.000000000 +0100 +++ new/google-cloud-core-1.7.2/MANIFEST.in 2021-07-27 20:05:41.000000000 +0200 @@ -16,10 +16,10 @@ # Generated by synthtool. DO NOT EDIT! include README.rst LICENSE -recursive-include google *.json *.proto +recursive-include google *.json *.proto py.typed recursive-include tests * global-exclude *.py[co] global-exclude __pycache__ # Exclude scripts for samples readmegen -prune scripts/readme-gen \ No newline at end of file +prune scripts/readme-gen diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.6.0/PKG-INFO new/google-cloud-core-1.7.2/PKG-INFO --- old/google-cloud-core-1.6.0/PKG-INFO 2021-02-06 02:23:12.442483000 +0100 +++ new/google-cloud-core-1.7.2/PKG-INFO 2021-07-27 20:08:24.116366000 +0200 @@ -1,51 +1,11 @@ Metadata-Version: 2.1 Name: google-cloud-core -Version: 1.6.0 +Version: 1.7.2 Summary: Google Cloud API client core library Home-page: https://github.com/googleapis/python-cloud-core Author: Google LLC Author-email: googleapis-packa...@google.com License: Apache 2.0 -Description: Core Helpers for Google Cloud Python Client Library - =================================================== - - |pypi| |versions| - - This library is not meant to stand-alone. Instead it defines - common helpers (e.g. base ``Client`` classes) used by all of the - ``google-cloud-*`` packages. - - - - `Documentation`_ - - .. |pypi| image:: https://img.shields.io/pypi/v/google-cloud-core.svg - :target: https://pypi.org/project/google-cloud-core/ - .. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-core.svg - :target: https://pypi.org/project/google-cloud-core/ - .. _Documentation: https://googleapis.dev/python/google-cloud-core/latest - - Quick Start - ----------- - - .. code-block:: console - - $ pip install --upgrade google-cloud-core - - For more information on setting up your Python development environment, - such as installing ``pip`` and ``virtualenv`` on your system, please refer - to `Python Development Environment Setup Guide`_ for Google Cloud Platform. - - .. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup - - - Supported Python Versions - ------------------------- - Python >= 3.6 - - Deprecated Python Versions - -------------------------- - Python == 2.7. Python 2.7 support will be removed on January 1, 2020. - Platform: Posix; MacOS X; Windows Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers @@ -62,3 +22,46 @@ Classifier: Topic :: Internet Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.* Provides-Extra: grpc +License-File: LICENSE + +Core Helpers for Google Cloud Python Client Library +=================================================== + +|pypi| |versions| + +This library is not meant to stand-alone. Instead it defines +common helpers (e.g. base ``Client`` classes) used by all of the +``google-cloud-*`` packages. + + +- `Documentation`_ + +.. |pypi| image:: https://img.shields.io/pypi/v/google-cloud-core.svg + :target: https://pypi.org/project/google-cloud-core/ +.. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-core.svg + :target: https://pypi.org/project/google-cloud-core/ +.. _Documentation: https://googleapis.dev/python/google-cloud-core/latest + +Quick Start +----------- + +.. code-block:: console + + $ pip install --upgrade google-cloud-core + +For more information on setting up your Python development environment, +such as installing ``pip`` and ``virtualenv`` on your system, please refer +to `Python Development Environment Setup Guide`_ for Google Cloud Platform. + +.. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup + + +Supported Python Versions +------------------------- +Python >= 3.6 + +Deprecated Python Versions +-------------------------- +Python == 2.7. Python 2.7 support will be removed on January 1, 2020. + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.6.0/google/cloud/_http.py new/google-cloud-core-1.7.2/google/cloud/_http.py --- old/google-cloud-core-1.6.0/google/cloud/_http.py 2021-02-06 02:19:50.000000000 +0100 +++ new/google-cloud-core-1.7.2/google/cloud/_http.py 2021-07-27 20:05:41.000000000 +0200 @@ -15,9 +15,10 @@ """Shared implementation of connections to API servers.""" import collections + try: import collections.abc as collections_abc -except ImportError: +except ImportError: # Python2 import collections as collections_abc import json import os @@ -34,9 +35,7 @@ API_BASE_URL = "https://www.googleapis.com" """The base of the API call URL.""" -DEFAULT_USER_AGENT = "gcloud-python/{0}".format( - version.__version__ -) +DEFAULT_USER_AGENT = "gcloud-python/{0}".format(version.__version__) """The user agent for google-cloud-python requests.""" CLIENT_INFO_HEADER = "X-Goog-API-Client" @@ -83,14 +82,12 @@ :rtype: str :returns: user agent """ - warnings.warn( - _USER_AGENT_ALL_CAPS_DEPRECATED, DeprecationWarning, stacklevel=2) + warnings.warn(_USER_AGENT_ALL_CAPS_DEPRECATED, DeprecationWarning, stacklevel=2) return self.user_agent @USER_AGENT.setter def USER_AGENT(self, value): - warnings.warn( - _USER_AGENT_ALL_CAPS_DEPRECATED, DeprecationWarning, stacklevel=2) + warnings.warn(_USER_AGENT_ALL_CAPS_DEPRECATED, DeprecationWarning, stacklevel=2) self.user_agent = value @property @@ -114,13 +111,15 @@ :returns: header keys / values """ warnings.warn( - _EXTRA_HEADERS_ALL_CAPS_DEPRECATED, DeprecationWarning, stacklevel=2) + _EXTRA_HEADERS_ALL_CAPS_DEPRECATED, DeprecationWarning, stacklevel=2 + ) return self.extra_headers @_EXTRA_HEADERS.setter def _EXTRA_HEADERS(self, value): warnings.warn( - _EXTRA_HEADERS_ALL_CAPS_DEPRECATED, DeprecationWarning, stacklevel=2) + _EXTRA_HEADERS_ALL_CAPS_DEPRECATED, DeprecationWarning, stacklevel=2 + ) self.extra_headers = value @property @@ -222,7 +221,9 @@ url_to_use = self.API_BASE_URL else: if self.ALLOW_AUTO_SWITCH_TO_MTLS_URL: - url_to_use = self.API_BASE_MTLS_URL if self.http.is_mtls else self.API_BASE_URL + url_to_use = ( + self.API_BASE_MTLS_URL if self.http.is_mtls else self.API_BASE_URL + ) else: url_to_use = self.API_BASE_URL return url_to_use diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.6.0/google/cloud/client.py new/google-cloud-core-1.7.2/google/cloud/client.py --- old/google-cloud-core-1.6.0/google/cloud/client.py 2021-02-06 02:19:50.000000000 +0100 +++ new/google-cloud-core-1.7.2/google/cloud/client.py 2021-07-27 20:05:41.000000000 +0200 @@ -52,6 +52,36 @@ _SET_PROJECT = False @classmethod + def from_service_account_info(cls, info, *args, **kwargs): + """Factory to retrieve JSON credentials while creating client. + + :type info: str + :param info: + The JSON object with a private key and other credentials + information (downloaded from the Google APIs console). + + :type args: tuple + :param args: Remaining positional arguments to pass to constructor. + + :param kwargs: Remaining keyword arguments to pass to constructor. + + :rtype: :class:`_ClientFactoryMixin` + :returns: The client created with the retrieved JSON credentials. + :raises TypeError: if there is a conflict with the kwargs + and the credentials created by the factory. + """ + if "credentials" in kwargs: + raise TypeError("credentials must not be in keyword arguments") + + credentials = service_account.Credentials.from_service_account_info(info) + if cls._SET_PROJECT: + if "project" not in kwargs: + kwargs["project"] = info.get("project_id") + + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod def from_service_account_json(cls, json_credentials_path, *args, **kwargs): """Factory to retrieve JSON credentials while creating client. @@ -73,19 +103,10 @@ :raises TypeError: if there is a conflict with the kwargs and the credentials created by the factory. """ - if "credentials" in kwargs: - raise TypeError("credentials must not be in keyword arguments") with io.open(json_credentials_path, "r", encoding="utf-8") as json_fi: credentials_info = json.load(json_fi) - credentials = service_account.Credentials.from_service_account_info( - credentials_info - ) - if cls._SET_PROJECT: - if "project" not in kwargs: - kwargs["project"] = credentials_info.get("project_id") - kwargs["credentials"] = credentials - return cls(*args, **kwargs) + return cls.from_service_account_info(credentials_info, *args, **kwargs) class Client(_ClientFactoryMixin): @@ -137,9 +158,12 @@ if credentials and client_options.credentials_file: raise google.api_core.exceptions.DuplicateCredentialArgs( - "'credentials' and 'client_options.credentials_file' are mutually exclusive.") + "'credentials' and 'client_options.credentials_file' are mutually exclusive." + ) - if credentials and not isinstance(credentials, google.auth.credentials.Credentials): + if credentials and not isinstance( + credentials, google.auth.credentials.Credentials + ): raise ValueError(_GOOGLE_AUTH_CREDENTIALS_HELP) scopes = client_options.scopes or self.SCOPE @@ -148,15 +172,19 @@ 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) + client_options.credentials_file, scopes=scopes + ) else: credentials, _ = google.auth.default(scopes=scopes) self._credentials = google.auth.credentials.with_scopes_if_required( - credentials, scopes=scopes) + credentials, scopes=scopes + ) if client_options.quota_project_id: - self._credentials = self._credentials.with_quota_project(client_options.quota_project_id) + self._credentials = self._credentials.with_quota_project( + client_options.quota_project_id + ) self._http_internal = _http self._client_cert_source = client_options.client_cert_source @@ -181,12 +209,26 @@ """ if self._http_internal is None: self._http_internal = google.auth.transport.requests.AuthorizedSession( - self._credentials, - refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT, + self._credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT, ) self._http_internal.configure_mtls_channel(self._client_cert_source) return self._http_internal + def close(self): + """Clean up transport, if set. + + Suggested use: + + .. code-block:: python + + import contextlib + + with contextlib.closing(client): # closes on exit + do_something_with(client) + """ + if self._http_internal is not None: + self._http_internal.close() + class _ClientProjectMixin(object): """Mixin to allow setting the project on the client. @@ -212,8 +254,7 @@ # https://github.com/googleapis/python-cloud-core/issues/27 if project is None: project = os.getenv( - environment_vars.PROJECT, - os.getenv(environment_vars.LEGACY_PROJECT), + environment_vars.PROJECT, os.getenv(environment_vars.LEGACY_PROJECT), ) # Project set on explicit credentials overrides discovery from @@ -275,4 +316,6 @@ def __init__(self, project=None, credentials=None, client_options=None, _http=None): _ClientProjectMixin.__init__(self, project=project, credentials=credentials) - Client.__init__(self, credentials=credentials, client_options=client_options, _http=_http) + Client.__init__( + self, credentials=credentials, client_options=client_options, _http=_http + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.6.0/google/cloud/version.py new/google-cloud-core-1.7.2/google/cloud/version.py --- old/google-cloud-core-1.6.0/google/cloud/version.py 2021-02-06 02:19:50.000000000 +0100 +++ new/google-cloud-core-1.7.2/google/cloud/version.py 2021-07-27 20:05:41.000000000 +0200 @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.6.0" +__version__ = "1.7.2" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.6.0/google_cloud_core.egg-info/PKG-INFO new/google-cloud-core-1.7.2/google_cloud_core.egg-info/PKG-INFO --- old/google-cloud-core-1.6.0/google_cloud_core.egg-info/PKG-INFO 2021-02-06 02:23:12.000000000 +0100 +++ new/google-cloud-core-1.7.2/google_cloud_core.egg-info/PKG-INFO 2021-07-27 20:08:23.000000000 +0200 @@ -1,51 +1,11 @@ Metadata-Version: 2.1 Name: google-cloud-core -Version: 1.6.0 +Version: 1.7.2 Summary: Google Cloud API client core library Home-page: https://github.com/googleapis/python-cloud-core Author: Google LLC Author-email: googleapis-packa...@google.com License: Apache 2.0 -Description: Core Helpers for Google Cloud Python Client Library - =================================================== - - |pypi| |versions| - - This library is not meant to stand-alone. Instead it defines - common helpers (e.g. base ``Client`` classes) used by all of the - ``google-cloud-*`` packages. - - - - `Documentation`_ - - .. |pypi| image:: https://img.shields.io/pypi/v/google-cloud-core.svg - :target: https://pypi.org/project/google-cloud-core/ - .. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-core.svg - :target: https://pypi.org/project/google-cloud-core/ - .. _Documentation: https://googleapis.dev/python/google-cloud-core/latest - - Quick Start - ----------- - - .. code-block:: console - - $ pip install --upgrade google-cloud-core - - For more information on setting up your Python development environment, - such as installing ``pip`` and ``virtualenv`` on your system, please refer - to `Python Development Environment Setup Guide`_ for Google Cloud Platform. - - .. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup - - - Supported Python Versions - ------------------------- - Python >= 3.6 - - Deprecated Python Versions - -------------------------- - Python == 2.7. Python 2.7 support will be removed on January 1, 2020. - Platform: Posix; MacOS X; Windows Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers @@ -62,3 +22,46 @@ Classifier: Topic :: Internet Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.* Provides-Extra: grpc +License-File: LICENSE + +Core Helpers for Google Cloud Python Client Library +=================================================== + +|pypi| |versions| + +This library is not meant to stand-alone. Instead it defines +common helpers (e.g. base ``Client`` classes) used by all of the +``google-cloud-*`` packages. + + +- `Documentation`_ + +.. |pypi| image:: https://img.shields.io/pypi/v/google-cloud-core.svg + :target: https://pypi.org/project/google-cloud-core/ +.. |versions| image:: https://img.shields.io/pypi/pyversions/google-cloud-core.svg + :target: https://pypi.org/project/google-cloud-core/ +.. _Documentation: https://googleapis.dev/python/google-cloud-core/latest + +Quick Start +----------- + +.. code-block:: console + + $ pip install --upgrade google-cloud-core + +For more information on setting up your Python development environment, +such as installing ``pip`` and ``virtualenv`` on your system, please refer +to `Python Development Environment Setup Guide`_ for Google Cloud Platform. + +.. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup + + +Supported Python Versions +------------------------- +Python >= 3.6 + +Deprecated Python Versions +-------------------------- +Python == 2.7. Python 2.7 support will be removed on January 1, 2020. + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.6.0/tests/unit/test__http.py new/google-cloud-core-1.7.2/tests/unit/test__http.py --- old/google-cloud-core-1.6.0/tests/unit/test__http.py 2021-02-06 02:19:50.000000000 +0100 +++ new/google-cloud-core-1.7.2/tests/unit/test__http.py 2021-07-27 20:05:41.000000000 +0200 @@ -134,6 +134,11 @@ response._content = content response.headers = headers response.request = requests.Request() + + # Work around requests 'charset_normalizer' idiocy. + # See https://github.com/googleapis/python-cloud-core/issues/117 + response.encoding = "utf-8" + return response @@ -186,14 +191,18 @@ client = object() conn = self._make_mock_one(client) # Intended to emulate self.mock_template - URI = "/".join([conn.API_BASE_URL, "mock", conn.API_VERSION, "foo?prettyPrint=false"]) + URI = "/".join( + [conn.API_BASE_URL, "mock", conn.API_VERSION, "foo?prettyPrint=false"] + ) self.assertEqual(conn.build_api_url("/foo"), URI) def test_build_api_url_w_pretty_print_query_params(self): client = object() conn = self._make_mock_one(client) uri = conn.build_api_url("/foo", {"prettyPrint": "true"}) - URI = "/".join([conn.API_BASE_URL, "mock", conn.API_VERSION, "foo?prettyPrint=true"]) + URI = "/".join( + [conn.API_BASE_URL, "mock", conn.API_VERSION, "foo?prettyPrint=true"] + ) self.assertEqual(uri, URI) def test_build_api_url_w_extra_query_params(self): @@ -220,7 +229,9 @@ client = object() conn = self._make_mock_one(client) - uri = conn.build_api_url("/foo", [("bar", "baz"), ("qux", "quux"), ("qux", "corge")]) + uri = conn.build_api_url( + "/foo", [("bar", "baz"), ("qux", "quux"), ("qux", "corge")] + ) scheme, netloc, path, qs, _ = urlsplit(uri) self.assertEqual("%s://%s" % (scheme, netloc), conn.API_BASE_URL) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.6.0/tests/unit/test_client.py new/google-cloud-core-1.7.2/tests/unit/test_client.py --- old/google-cloud-core-1.6.0/tests/unit/test_client.py 2021-02-06 02:19:50.000000000 +0100 +++ new/google-cloud-core-1.7.2/tests/unit/test_client.py 2021-07-27 20:05:41.000000000 +0200 @@ -80,7 +80,7 @@ from google.api_core.exceptions import DuplicateCredentialArgs credentials = _make_credentials() - client_options = {'credentials_file': '/path/to/creds.json'} + client_options = {"credentials_file": "/path/to/creds.json"} with self.assertRaises(DuplicateCredentialArgs): self._make_one(credentials=credentials, client_options=client_options) @@ -92,26 +92,34 @@ 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} + 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)) + 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) + 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} + 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) + client_obj = self._make_one( + credentials=credentials, client_options=client_options + ) - self.assertIs(client_obj._credentials, credentials.with_quota_project.return_value) + 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): @@ -126,32 +134,62 @@ credentials = _make_credentials() mock_client_cert_source = mock.Mock() - client_options = {'client_cert_source': mock_client_cert_source} + client_options = {"client_cert_source": mock_client_cert_source} client = self._make_one(credentials=credentials, client_options=client_options) self.assertIsNone(client._http_internal) - with mock.patch('google.auth.transport.requests.AuthorizedSession') as AuthorizedSession: + with mock.patch( + "google.auth.transport.requests.AuthorizedSession" + ) as AuthorizedSession: session = mock.Mock() session.configure_mtls_channel = mock.Mock() AuthorizedSession.return_value = session self.assertIs(client._http, session) # Check the mock. - AuthorizedSession.assert_called_once_with(credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT) - session.configure_mtls_channel.assert_called_once_with(mock_client_cert_source) + AuthorizedSession.assert_called_once_with( + credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT + ) + session.configure_mtls_channel.assert_called_once_with( + mock_client_cert_source + ) # Make sure the cached value is used on subsequent access. self.assertIs(client._http_internal, session) self.assertIs(client._http, session) self.assertEqual(AuthorizedSession.call_count, 1) + def test_from_service_account_info(self): + klass = self._get_target_class() + + info = {"dummy": "value", "valid": "json"} + constructor_patch = mock.patch( + "google.oauth2.service_account.Credentials.from_service_account_info", + return_value=_make_credentials(), + ) + + with constructor_patch as constructor: + client_obj = klass.from_service_account_info(info) + + self.assertIs(client_obj._credentials, constructor.return_value) + self.assertIsNone(client_obj._http_internal) + constructor.assert_called_once_with(info) + + def test_from_service_account_info_w_explicit_credentials(self): + KLASS = self._get_target_class() + + info = {"dummy": "value", "valid": "json"} + + with self.assertRaises(TypeError): + KLASS.from_service_account_info(info, credentials=mock.sentinel.credentials) + def test_from_service_account_json(self): from google.cloud import _helpers klass = self._get_target_class() - # Mock both the file opening and the credentials constructor. info = {"dummy": "value", "valid": "json"} - json_fi = io.StringIO(_helpers._bytes_to_unicode(json.dumps(info))) - file_open_patch = mock.patch("io.open", return_value=json_fi) + json_file = io.StringIO(_helpers._bytes_to_unicode(json.dumps(info))) + + file_open_patch = mock.patch("io.open", return_value=json_file) constructor_patch = mock.patch( "google.oauth2.service_account.Credentials." "from_service_account_info", return_value=_make_credentials(), @@ -167,13 +205,20 @@ file_open.assert_called_once_with(mock.sentinel.filename, "r", encoding="utf-8") constructor.assert_called_once_with(info) - def test_from_service_account_json_bad_args(self): - KLASS = self._get_target_class() + def test_close_w__http_internal_none(self): + credentials = _make_credentials() + client_obj = self._make_one(credentials=credentials, _http=None) - with self.assertRaises(TypeError): - KLASS.from_service_account_json( - mock.sentinel.filename, credentials=mock.sentinel.credentials - ) + client_obj.close() # noraise + + def test_close_w__http_internal_set(self): + credentials = _make_credentials() + http = mock.Mock(spec=["close"]) + client_obj = self._make_one(credentials=credentials, _http=http) + + client_obj.close() + + http.close.assert_called_once_with() class Test_ClientProjectMixin(unittest.TestCase): @@ -190,8 +235,7 @@ environ = {} patch_env = mock.patch("os.environ", new=environ) patch_default = mock.patch( - "google.cloud.client._determine_default_project", - return_value=None, + "google.cloud.client._determine_default_project", return_value=None, ) with patch_env: with patch_default as patched: @@ -225,8 +269,7 @@ def test_ctor_w_explicit_project(self): explicit_project = "explicit-project-456" patch_default = mock.patch( - "google.cloud.client._determine_default_project", - return_value=None, + "google.cloud.client._determine_default_project", return_value=None, ) with patch_default as patched: client = self._make_one(project=explicit_project) @@ -238,8 +281,7 @@ def test_ctor_w_explicit_project_bytes(self): explicit_project = b"explicit-project-456" patch_default = mock.patch( - "google.cloud.client._determine_default_project", - return_value=None, + "google.cloud.client._determine_default_project", return_value=None, ) with patch_default as patched: client = self._make_one(project=explicit_project) @@ -251,8 +293,7 @@ def test_ctor_w_explicit_project_invalid(self): explicit_project = object() patch_default = mock.patch( - "google.cloud.client._determine_default_project", - return_value=None, + "google.cloud.client._determine_default_project", return_value=None, ) with patch_default as patched: with self.assertRaises(ValueError): @@ -291,8 +332,7 @@ project = "credentials-project-456" credentials = self._make_credentials(project_id=project) patch_default = mock.patch( - "google.cloud.client._determine_default_project", - return_value=None, + "google.cloud.client._determine_default_project", return_value=None, ) with patch_default as patched: client = self._make_one(credentials=credentials) @@ -377,18 +417,78 @@ PROJECT = u"PROJECT" self._explicit_ctor_helper(PROJECT) + def _from_service_account_info_helper(self, project=None): + klass = self._get_target_class() + + default_project = "eye-d-of-project" + info = {"dummy": "value", "valid": "json", "project_id": default_project} + kwargs = {} + + if project is None: + expected_project = default_project + else: + expected_project = project + kwargs["project"] = project + + constructor_patch = mock.patch( + "google.oauth2.service_account.Credentials.from_service_account_info", + return_value=_make_credentials(), + ) + + with constructor_patch as constructor: + client_obj = klass.from_service_account_info(info, **kwargs) + + self.assertIs(client_obj._credentials, constructor.return_value) + self.assertIsNone(client_obj._http_internal) + self.assertEqual(client_obj.project, expected_project) + + constructor.assert_called_once_with(info) + + def test_from_service_account_info(self): + self._from_service_account_info_helper() + + def test_from_service_account_info_with_project(self): + self._from_service_account_info_helper(project="prah-jekt") + + def test_from_service_account_info_with_posarg(self): + class Derived(self._get_target_class()): + def __init__(self, required, **kwargs): + super(Derived, self).__init__(**kwargs) + self.required = required + + project = "eye-d-of-project" + info = {"dummy": "value", "valid": "json", "project_id": project} + + # Mock both the file opening and the credentials constructor. + constructor_patch = mock.patch( + "google.oauth2.service_account.Credentials.from_service_account_info", + return_value=_make_credentials(), + ) + + with constructor_patch as constructor: + client_obj = Derived.from_service_account_info(info, "REQUIRED") + + self.assertIsInstance(client_obj, Derived) + self.assertIs(client_obj._credentials, constructor.return_value) + self.assertIsNone(client_obj._http_internal) + self.assertEqual(client_obj.project, project) + self.assertEqual(client_obj.required, "REQUIRED") + + # Check that mocks were called as expected. + constructor.assert_called_once_with(info) + def _from_service_account_json_helper(self, project=None): from google.cloud import _helpers klass = self._get_target_class() - info = {"dummy": "value", "valid": "json"} + default_project = "eye-d-of-project" + info = {"dummy": "value", "valid": "json", "project_id": default_project} if project is None: expected_project = "eye-d-of-project" else: expected_project = project - info["project_id"] = expected_project # Mock both the file opening and the credentials constructor. json_fi = io.StringIO(_helpers._bytes_to_unicode(json.dumps(info))) file_open_patch = mock.patch("io.open", return_value=json_fi) @@ -418,3 +518,38 @@ def test_from_service_account_json_project_set(self): self._from_service_account_json_helper(project="prah-jekt") + + def test_from_service_account_json_with_posarg(self): + from google.cloud import _helpers + + class Derived(self._get_target_class()): + def __init__(self, required, **kwargs): + super(Derived, self).__init__(**kwargs) + self.required = required + + project = "eye-d-of-project" + info = {"dummy": "value", "valid": "json", "project_id": project} + + # Mock both the file opening and the credentials constructor. + json_fi = io.StringIO(_helpers._bytes_to_unicode(json.dumps(info))) + file_open_patch = mock.patch("io.open", return_value=json_fi) + constructor_patch = mock.patch( + "google.oauth2.service_account.Credentials.from_service_account_info", + return_value=_make_credentials(), + ) + + with file_open_patch as file_open: + with constructor_patch as constructor: + client_obj = Derived.from_service_account_json( + mock.sentinel.filename, "REQUIRED" + ) + + self.assertIsInstance(client_obj, Derived) + self.assertIs(client_obj._credentials, constructor.return_value) + self.assertIsNone(client_obj._http_internal) + self.assertEqual(client_obj.project, project) + self.assertEqual(client_obj.required, "REQUIRED") + + # Check that mocks were called as expected. + file_open.assert_called_once_with(mock.sentinel.filename, "r", encoding="utf-8") + constructor.assert_called_once_with(info)