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-04-14 10:10:55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-google-cloud-core (Old) and /work/SRC/openSUSE:Factory/.python-google-cloud-core.new.2401 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-google-cloud-core" Wed Apr 14 10:10:55 2021 rev:8 rq:884874 version:1.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-google-cloud-core/python-google-cloud-core.changes 2020-04-21 13:10:49.748831654 +0200 +++ /work/SRC/openSUSE:Factory/.python-google-cloud-core.new.2401/python-google-cloud-core.changes 2021-04-14 10:11:23.241535054 +0200 @@ -1,0 +2,13 @@ +Tue Apr 6 07:45:12 UTC 2021 - Dirk M??ller <dmuel...@suse.com> + +- update to 1.6.0: + * add mtls support + * infer project from explicit service account creds + * add support for Python 3.9, drop support for Python 3.5 + * require six>=1.12.0 and google-api-core>=1.21. + * avoid using 'pkg_resources' to determine version + * handle query_params tuples in JSONConnection.build_api_url + * add quota_project, credentials file, and scopes options + * add support for Python 3.8 + +------------------------------------------------------------------- Old: ---- google-cloud-core-1.3.0.tar.gz New: ---- google-cloud-core-1.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-google-cloud-core.spec ++++++ --- /var/tmp/diff_new_pack.nwPd1h/_old 2021-04-14 10:11:23.817536026 +0200 +++ /var/tmp/diff_new_pack.nwPd1h/_new 2021-04-14 10:11:23.821536033 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-google-cloud-core # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,21 +19,24 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %bcond_without python2 Name: python-google-cloud-core -Version: 1.3.0 +Version: 1.6.0 Release: 0 Summary: Google Cloud API client core library License: Apache-2.0 Group: Development/Languages/Python -URL: https://github.com/GoogleCloudPlatform/google-cloud-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.16.0} +BuildRequires: %{python_module google-api-core >= 1.21.0} +BuildRequires: %{python_module google-auth >= 1.24.0} BuildRequires: %{python_module grpcio >= 1.8.2} BuildRequires: %{python_module mock} BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros -Requires: python-google-api-core >= 1.16.0 +Requires: python-google-api-core >= 1.21.0 +Requires: python-google-auth >= 1.24.0 +Requires: python-six >= 1.12.0 Recommends: python-grpcio >= 1.8.2 BuildArch: noarch %if %{with python2} ++++++ google-cloud-core-1.3.0.tar.gz -> google-cloud-core-1.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/MANIFEST.in new/google-cloud-core-1.6.0/MANIFEST.in --- old/google-cloud-core-1.3.0/MANIFEST.in 2020-01-31 22:59:39.000000000 +0100 +++ new/google-cloud-core-1.6.0/MANIFEST.in 2021-02-06 02:19:50.000000000 +0100 @@ -1,3 +1,25 @@ +# -*- coding: utf-8 -*- +# +# 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 +# +# https://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. + +# Generated by synthtool. DO NOT EDIT! include README.rst LICENSE +recursive-include google *.json *.proto recursive-include tests * -global-exclude *.pyc __pycache__ +global-exclude *.py[co] +global-exclude __pycache__ + +# Exclude scripts for samples readmegen +prune scripts/readme-gen \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/PKG-INFO new/google-cloud-core-1.6.0/PKG-INFO --- old/google-cloud-core-1.3.0/PKG-INFO 2020-01-31 23:02:20.810099000 +0100 +++ new/google-cloud-core-1.6.0/PKG-INFO 2021-02-06 02:23:12.442483000 +0100 @@ -1,15 +1,15 @@ Metadata-Version: 2.1 Name: google-cloud-core -Version: 1.3.0 +Version: 1.6.0 Summary: Google Cloud API client core library -Home-page: https://github.com/GoogleCloudPlatform/google-cloud-python +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| + |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 @@ -40,24 +40,25 @@ Supported Python Versions ------------------------- - Python >= 3.5 + 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 :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Operating System :: OS Independent Classifier: Topic :: Internet -Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.* Provides-Extra: grpc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/README.rst new/google-cloud-core-1.6.0/README.rst --- old/google-cloud-core-1.3.0/README.rst 2020-01-31 22:59:39.000000000 +0100 +++ new/google-cloud-core-1.6.0/README.rst 2021-02-06 02:19:50.000000000 +0100 @@ -1,7 +1,7 @@ Core Helpers for Google Cloud Python Client Library =================================================== -|pypi| |versions| +|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 @@ -32,7 +32,7 @@ Supported Python Versions ------------------------- -Python >= 3.5 +Python >= 3.6 Deprecated Python Versions -------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/google/cloud/_http.py new/google-cloud-core-1.6.0/google/cloud/_http.py --- old/google-cloud-core-1.3.0/google/cloud/_http.py 2020-01-31 22:59:39.000000000 +0100 +++ new/google-cloud-core-1.6.0/google/cloud/_http.py 2021-02-06 02:19:50.000000000 +0100 @@ -14,22 +14,28 @@ """Shared implementation of connections to API servers.""" +import collections +try: + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc import json +import os import platform import warnings -from pkg_resources import get_distribution from six.moves.urllib.parse import urlencode from google.api_core.client_info import ClientInfo from google.cloud import exceptions +from google.cloud import version API_BASE_URL = "https://www.googleapis.com" """The base of the API call URL.""" DEFAULT_USER_AGENT = "gcloud-python/{0}".format( - get_distribution("google-cloud-core").version + version.__version__ ) """The user agent for google-cloud-python requests.""" @@ -171,12 +177,56 @@ API_BASE_URL = None """The base of the API call URL.""" + API_BASE_MTLS_URL = None + """The base of the API call URL for mutual TLS.""" + + ALLOW_AUTO_SWITCH_TO_MTLS_URL = False + """Indicates if auto switch to mTLS url is allowed.""" + API_VERSION = None """The version of the API, used in building the API call's URL.""" API_URL_TEMPLATE = None """A template for the URL of a particular API call.""" + def get_api_base_url_for_mtls(self, api_base_url=None): + """Return the api base url for mutual TLS. + + Typically, you shouldn't need to use this method. + + The logic is as follows: + + If `api_base_url` is provided, just return this value; otherwise, the + return value depends `GOOGLE_API_USE_MTLS_ENDPOINT` environment variable + value. + + If the environment variable value is "always", return `API_BASE_MTLS_URL`. + If the environment variable value is "never", return `API_BASE_URL`. + Otherwise, if `ALLOW_AUTO_SWITCH_TO_MTLS_URL` is True and the underlying + http is mTLS, then return `API_BASE_MTLS_URL`; otherwise return `API_BASE_URL`. + + :type api_base_url: str + :param api_base_url: User provided api base url. It takes precedence over + `API_BASE_URL` and `API_BASE_MTLS_URL`. + + :rtype: str + :returns: The api base url used for mTLS. + """ + if api_base_url: + return api_base_url + + env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if env == "always": + url_to_use = self.API_BASE_MTLS_URL + elif env == "never": + 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 + else: + url_to_use = self.API_BASE_URL + return url_to_use + def build_api_url( self, path, query_params=None, api_base_url=None, api_version=None ): @@ -205,14 +255,24 @@ :returns: The URL assembled from the pieces provided. """ url = self.API_URL_TEMPLATE.format( - api_base_url=(api_base_url or self.API_BASE_URL), + api_base_url=self.get_api_base_url_for_mtls(api_base_url), api_version=(api_version or self.API_VERSION), path=path, ) query_params = query_params or {} - if query_params: - url += "?" + urlencode(query_params, doseq=True) + + if isinstance(query_params, collections_abc.Mapping): + query_params = query_params.copy() + else: + query_params_dict = collections.defaultdict(list) + for key, value in query_params: + query_params_dict[key].append(value) + query_params = query_params_dict + + query_params.setdefault("prettyPrint", "false") + + url += "?" + urlencode(query_params, doseq=True) return url diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/google/cloud/client.py new/google-cloud-core-1.6.0/google/cloud/client.py --- old/google-cloud-core-1.3.0/google/cloud/client.py 2020-01-31 22:59:39.000000000 +0100 +++ new/google-cloud-core-1.6.0/google/cloud/client.py 2021-02-06 02:19:50.000000000 +0100 @@ -16,11 +16,15 @@ import io import json +import os from pickle import PicklingError import six +import google.api_core.client_options +import google.api_core.exceptions import google.auth +from google.auth import environment_vars import google.auth.credentials import google.auth.transport.requests from google.cloud._helpers import _determine_default_project @@ -102,6 +106,8 @@ (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,17 +129,37 @@ 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 + self._client_cert_source = client_options.client_cert_source def __getstate__(self): """Explicitly state that clients are not pickleable.""" @@ -158,6 +184,7 @@ self._credentials, refresh_timeout=_CREDENTIALS_REFRESH_TIMEOUT, ) + self._http_internal.configure_mtls_channel(self._client_cert_source) return self._http_internal @@ -165,26 +192,50 @@ """Mixin to allow setting the project on the client. :type project: str - :param project: the project which the client acts on behalf of. If not - passed falls back to the default inferred from the - environment. + :param project: + (Optional) the project which the client acts on behalf of. If not + passed, falls back to the default inferred from the environment. + + :type credentials: :class:`google.auth.credentials.Credentials` + :param credentials: + (Optional) credentials used to discover a project, if not passed. :raises: :class:`EnvironmentError` if the project is neither passed in nor - set in the environment. :class:`ValueError` if the project value - is invalid. + set on the credentials or in the environment. :class:`ValueError` + if the project value is invalid. """ - def __init__(self, project=None): - project = self._determine_default(project) + def __init__(self, project=None, credentials=None): + # This test duplicates the one from `google.auth.default`, but earlier, + # for backward compatibility: we want the environment variable to + # override any project set on the credentials. See: + # 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), + ) + + # Project set on explicit credentials overrides discovery from + # SDK / GAE / GCE. + if project is None and credentials is not None: + project = getattr(credentials, "project_id", None) + + if project is None: + project = self._determine_default(project) + if project is None: raise EnvironmentError( "Project was not passed and could not be " "determined from the environment." ) + if isinstance(project, six.binary_type): project = project.decode("utf-8") + if not isinstance(project, six.string_types): raise ValueError("Project must be a string.") + self.project = project @staticmethod @@ -222,6 +273,6 @@ _SET_PROJECT = True # Used by from_service_account_json() - def __init__(self, project=None, credentials=None, _http=None): - _ClientProjectMixin.__init__(self, project=project) - Client.__init__(self, credentials=credentials, _http=_http) + 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) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/google/cloud/version.py new/google-cloud-core-1.6.0/google/cloud/version.py --- old/google-cloud-core-1.3.0/google/cloud/version.py 1970-01-01 01:00:00.000000000 +0100 +++ new/google-cloud-core-1.6.0/google/cloud/version.py 2021-02-06 02:19:50.000000000 +0100 @@ -0,0 +1,15 @@ +# 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. + +__version__ = "1.6.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/google_cloud_core.egg-info/PKG-INFO new/google-cloud-core-1.6.0/google_cloud_core.egg-info/PKG-INFO --- old/google-cloud-core-1.3.0/google_cloud_core.egg-info/PKG-INFO 2020-01-31 23:02:20.000000000 +0100 +++ new/google-cloud-core-1.6.0/google_cloud_core.egg-info/PKG-INFO 2021-02-06 02:23:12.000000000 +0100 @@ -1,15 +1,15 @@ Metadata-Version: 2.1 Name: google-cloud-core -Version: 1.3.0 +Version: 1.6.0 Summary: Google Cloud API client core library -Home-page: https://github.com/GoogleCloudPlatform/google-cloud-python +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| + |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 @@ -40,24 +40,25 @@ Supported Python Versions ------------------------- - Python >= 3.5 + 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 :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Classifier: Operating System :: OS Independent Classifier: Topic :: Internet -Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.* Provides-Extra: grpc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/google_cloud_core.egg-info/SOURCES.txt new/google-cloud-core-1.6.0/google_cloud_core.egg-info/SOURCES.txt --- old/google-cloud-core-1.3.0/google_cloud_core.egg-info/SOURCES.txt 2020-01-31 23:02:20.000000000 +0100 +++ new/google-cloud-core-1.6.0/google_cloud_core.egg-info/SOURCES.txt 2021-02-06 02:23:12.000000000 +0100 @@ -13,6 +13,7 @@ google/cloud/exceptions.py google/cloud/obsolete.py google/cloud/operation.py +google/cloud/version.py google_cloud_core.egg-info/PKG-INFO google_cloud_core.egg-info/SOURCES.txt google_cloud_core.egg-info/dependency_links.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/google_cloud_core.egg-info/requires.txt new/google-cloud-core-1.6.0/google_cloud_core.egg-info/requires.txt --- old/google-cloud-core-1.3.0/google_cloud_core.egg-info/requires.txt 2020-01-31 23:02:20.000000000 +0100 +++ new/google-cloud-core-1.6.0/google_cloud_core.egg-info/requires.txt 2021-02-06 02:23:12.000000000 +0100 @@ -1,4 +1,6 @@ -google-api-core<2.0.0dev,>=1.16.0 +google-api-core<2.0.0dev,>=1.21.0 +google-auth<2.0dev,>=1.24.0 +six>=1.12.0 [grpc] grpcio<2.0dev,>=1.8.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/setup.py new/google-cloud-core-1.6.0/setup.py --- old/google-cloud-core-1.3.0/setup.py 2020-01-31 22:59:39.000000000 +0100 +++ new/google-cloud-core-1.6.0/setup.py 2021-02-06 02:19:50.000000000 +0100 @@ -22,13 +22,18 @@ name = "google-cloud-core" description = "Google Cloud API client core library" -version = "1.3.0" # Should be one of: # 'Development Status :: 3 - Alpha' # 'Development Status :: 4 - Beta' # 'Development Status :: 5 - Production/Stable' -release_status = "Development Status :: 4 - Beta" -dependencies = ["google-api-core >= 1.16.0, < 2.0.0dev"] +release_status = "Development Status :: 5 - Production/Stable" +dependencies = [ + "google-api-core >= 1.21.0, < 2.0.0dev", + "google-auth >= 1.24.0, < 2.0dev", + # Support six==1.12.0 due to App Engine standard runtime. + # https://github.com/googleapis/python-cloud-core/issues/45 + "six >=1.12.0", +] extras = {"grpc": "grpcio >= 1.8.2, < 2.0dev"} @@ -36,6 +41,11 @@ package_root = os.path.abspath(os.path.dirname(__file__)) +version = {} +with open(os.path.join(package_root, "google/cloud/version.py")) as fp: + exec(fp.read(), version) +version = version["__version__"] + readme_filename = os.path.join(package_root, "README.rst") with io.open(readme_filename, encoding="utf-8") as readme_file: readme = readme_file.read() @@ -60,7 +70,7 @@ author="Google LLC", author_email="googleapis-packa...@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", @@ -69,9 +79,10 @@ "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "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", + "Programming Language :: Python :: 3.9", "Operating System :: OS Independent", "Topic :: Internet", ], @@ -80,7 +91,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.*,!=3.4.*,!=3.5.*", include_package_data=True, zip_safe=False, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/tests/unit/test__http.py new/google-cloud-core-1.6.0/tests/unit/test__http.py --- old/google-cloud-core-1.3.0/tests/unit/test__http.py 2020-01-31 22:59:39.000000000 +0100 +++ new/google-cloud-core-1.6.0/tests/unit/test__http.py 2021-02-06 02:19:50.000000000 +0100 @@ -13,6 +13,7 @@ # limitations under the License. import json +import os import unittest import warnings @@ -165,6 +166,7 @@ class MockConnection(self._get_target_class()): API_URL_TEMPLATE = "{api_base_url}/mock/{api_version}{path}" API_BASE_URL = "http://mock" + API_BASE_MTLS_URL = "https://mock.mtls" API_VERSION = "vMOCK" return MockConnection(*args, **kw) @@ -184,9 +186,16 @@ 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"]) + 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"]) + self.assertEqual(uri, URI) + def test_build_api_url_w_extra_query_params(self): from six.moves.urllib.parse import parse_qs from six.moves.urllib.parse import urlsplit @@ -203,6 +212,69 @@ parms = dict(parse_qs(qs)) self.assertEqual(parms["bar"], ["baz"]) self.assertEqual(parms["qux"], ["quux", "corge"]) + self.assertEqual(parms["prettyPrint"], ["false"]) + + def test_build_api_url_w_extra_query_params_tuples(self): + from six.moves.urllib.parse import parse_qs + from six.moves.urllib.parse import urlsplit + + client = object() + conn = self._make_mock_one(client) + 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) + # Intended to emulate mock_template + PATH = "/".join(["", "mock", conn.API_VERSION, "foo"]) + self.assertEqual(path, PATH) + parms = dict(parse_qs(qs)) + self.assertEqual(parms["bar"], ["baz"]) + self.assertEqual(parms["qux"], ["quux", "corge"]) + self.assertEqual(parms["prettyPrint"], ["false"]) + + def test_get_api_base_url_for_mtls_w_api_base_url(self): + client = object() + conn = self._make_mock_one(client) + uri = conn.get_api_base_url_for_mtls(api_base_url="http://foo") + self.assertEqual(uri, "http://foo") + + def test_get_api_base_url_for_mtls_env_always(self): + client = object() + conn = self._make_mock_one(client) + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + uri = conn.get_api_base_url_for_mtls() + self.assertEqual(uri, "https://mock.mtls") + + def test_get_api_base_url_for_mtls_env_never(self): + client = object() + conn = self._make_mock_one(client) + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + uri = conn.get_api_base_url_for_mtls() + self.assertEqual(uri, "http://mock") + + def test_get_api_base_url_for_mtls_env_auto(self): + client = mock.Mock() + client._http = mock.Mock() + client._http.is_mtls = False + conn = self._make_mock_one(client) + + # ALLOW_AUTO_SWITCH_TO_MTLS_URL is False, so use regular endpoint. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}): + uri = conn.get_api_base_url_for_mtls() + self.assertEqual(uri, "http://mock") + + # ALLOW_AUTO_SWITCH_TO_MTLS_URL is True, so now endpoint dependes + # on client._http.is_mtls + conn.ALLOW_AUTO_SWITCH_TO_MTLS_URL = True + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}): + uri = conn.get_api_base_url_for_mtls() + self.assertEqual(uri, "http://mock") + + client._http.is_mtls = True + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}): + uri = conn.get_api_base_url_for_mtls() + self.assertEqual(uri, "https://mock.mtls") def test__make_request_no_data_no_content_type_no_headers(self): from google.cloud._http import CLIENT_INFO_HEADER @@ -319,7 +391,7 @@ "User-Agent": conn.user_agent, CLIENT_INFO_HEADER: conn.user_agent, } - expected_url = "{base}/mock/{version}{path}".format( + expected_url = "{base}/mock/{version}{path}?prettyPrint=false".format( base=conn.API_BASE_URL, version=conn.API_VERSION, path=path ) http.request.assert_called_once_with( @@ -481,7 +553,7 @@ "User-Agent": conn.user_agent, CLIENT_INFO_HEADER: conn.user_agent, } - expected_url = "{base}/mock/{version}{path}".format( + expected_url = "{base}/mock/{version}{path}?prettyPrint=false".format( base=conn.API_BASE_URL, version=conn.API_VERSION, path=path ) http.request.assert_called_once_with( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/google-cloud-core-1.3.0/tests/unit/test_client.py new/google-cloud-core-1.6.0/tests/unit/test_client.py --- old/google-cloud-core-1.3.0/tests/unit/test_client.py 2020-01-31 22:59:39.000000000 +0100 +++ new/google-cloud-core-1.6.0/tests/unit/test_client.py 2021-02-06 02:19:50.000000000 +0100 @@ -22,7 +22,7 @@ def _make_credentials(): import google.auth.credentials - return mock.Mock(spec=google.auth.credentials.Credentials) + return mock.Mock(spec=google.auth.credentials.CredentialsWithQuotaProject) class Test_ClientFactoryMixin(unittest.TestCase): @@ -50,14 +50,14 @@ 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 @@ 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,73 @@ 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() + mock_client_cert_source = mock.Mock() + 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: + 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) + # 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_json(self): from google.cloud import _helpers @@ -114,31 +175,131 @@ 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) +class Test_ClientProjectMixin(unittest.TestCase): + @staticmethod + def _get_target_class(): + from google.cloud.client import _ClientProjectMixin + + return _ClientProjectMixin - authorized_session_patch = mock.patch( - "google.auth.transport.requests.AuthorizedSession", - return_value=mock.sentinel.http, + def _make_one(self, *args, **kw): + return self._get_target_class()(*args, **kw) + + def test_ctor_defaults_wo_envvar(self): + environ = {} + patch_env = mock.patch("os.environ", new=environ) + patch_default = mock.patch( + "google.cloud.client._determine_default_project", + return_value=None, ) - 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) + with patch_env: + with patch_default as patched: + with self.assertRaises(EnvironmentError): + self._make_one() + + patched.assert_called_once_with(None) + + def test_ctor_defaults_w_envvar(self): + from google.auth.environment_vars import PROJECT + + project = "some-project-123" + environ = {PROJECT: project} + patch_env = mock.patch("os.environ", new=environ) + with patch_env: + client = self._make_one() + + self.assertEqual(client.project, project) + + def test_ctor_defaults_w_legacy_envvar(self): + from google.auth.environment_vars import LEGACY_PROJECT + + project = "some-project-123" + environ = {LEGACY_PROJECT: project} + patch_env = mock.patch("os.environ", new=environ) + with patch_env: + client = self._make_one() + + self.assertEqual(client.project, project) + + 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, + ) + with patch_default as patched: + client = self._make_one(project=explicit_project) + + self.assertEqual(client.project, explicit_project) + + patched.assert_not_called() + + 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, + ) + with patch_default as patched: + client = self._make_one(project=explicit_project) + + self.assertEqual(client.project, explicit_project.decode("utf-8")) + + patched.assert_not_called() + + def test_ctor_w_explicit_project_invalid(self): + explicit_project = object() + patch_default = mock.patch( + "google.cloud.client._determine_default_project", + return_value=None, + ) + with patch_default as patched: + with self.assertRaises(ValueError): + self._make_one(project=explicit_project) + + patched.assert_not_called() + + @staticmethod + def _make_credentials(**kw): + from google.auth.credentials import Credentials + + class _Credentials(Credentials): + def __init__(self, **kw): + self.__dict__.update(kw) + + def refresh(self): # pragma: NO COVER + pass + + return _Credentials(**kw) + + def test_ctor_w_explicit_credentials_wo_project(self): + default_project = "default-project-123" + credentials = self._make_credentials() + patch_default = mock.patch( + "google.cloud.client._determine_default_project", + return_value=default_project, + ) + with patch_default as patched: + client = self._make_one(credentials=credentials) + + self.assertEqual(client.project, default_project) + + patched.assert_called_once_with(None) + + def test_ctor_w_explicit_credentials_w_project(self): + project = "credentials-project-456" + credentials = self._make_credentials(project_id=project) + patch_default = mock.patch( + "google.cloud.client._determine_default_project", + return_value=None, + ) + with patch_default as patched: + client = self._make_one(credentials=credentials) + + self.assertEqual(client.project, project) + + patched.assert_not_called() class TestClientWithProject(unittest.TestCase): @@ -167,7 +328,7 @@ 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):