From 1d0bbbd3d6e14664075d45d4007d343d2d95a47d Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Sat, 23 Jan 2021 01:04:27 +0000 Subject: [PATCH 01/12] feat: support self-signed jwt --- .../translation_service/transports/base.py | 3 +- .../translation_service/transports/grpc.py | 28 ++++++++++++++----- noxfile.py | 17 ++++++----- samples/snippets/noxfile.py | 4 +++ 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/google/cloud/translate_v3/services/translation_service/transports/base.py b/google/cloud/translate_v3/services/translation_service/transports/base.py index 204e32ec..5af8284b 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/base.py +++ b/google/cloud/translate_v3/services/translation_service/transports/base.py @@ -45,11 +45,12 @@ class TranslationServiceTransport(abc.ABC): "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-translation", ) + DEFAULT_HOST = "translate.googleapis.com" def __init__( self, *, - host: str = "translate.googleapis.com", + host: str = DEFAULT_HOST, credentials: credentials.Credentials = None, credentials_file: typing.Optional[str] = None, scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES, diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc.py b/google/cloud/translate_v3/services/translation_service/transports/grpc.py index 7efc5785..a5ef2f25 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc.py @@ -105,6 +105,12 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + # If a custom API endpoint is set, set scopes to ensure the auth + # library does not used the self-signed JWT flow for service + # accounts + if host.split(":")[0] != self.DEFAULT_HOST and not scopes: + scopes = self.AUTH_SCOPES + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -127,7 +133,9 @@ def __init__( if credentials is None: credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + default_scopes=self.AUTH_SCOPES, + scopes=scopes, + quota_project_id=quota_project_id ) # Create SSL credentials with client_cert_source or application @@ -146,7 +154,7 @@ def __init__( credentials=credentials, credentials_file=credentials_file, ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, options=[ ("grpc.max_send_message_length", -1), @@ -159,7 +167,9 @@ def __init__( if credentials is None: credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + default_scopes=self.AUTH_SCOPES, + scopes=scopes, + quota_project_id=quota_project_id ) # create a new channel. The provided one is ignored. @@ -168,7 +178,7 @@ def __init__( credentials=credentials, credentials_file=credentials_file, ssl_credentials=ssl_channel_credentials, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, options=[ ("grpc.max_send_message_length", -1), @@ -184,7 +194,7 @@ def __init__( host=host, credentials=credentials, credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, client_info=client_info, ) @@ -224,13 +234,17 @@ def create_channel( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ - scopes = scopes or cls.AUTH_SCOPES + kwargs["default_scopes"] = cls.AUTH_SCOPES + # Only pass scopes if they exist + if scopes: + kwargs["scopes"] = scopes + return grpc_helpers.create_channel( host, credentials=credentials, credentials_file=credentials_file, - scopes=scopes, quota_project_id=quota_project_id, + default_host=cls.DEFAULT_HOST, **kwargs, ) diff --git a/noxfile.py b/noxfile.py index 8004482e..a43153c5 100644 --- a/noxfile.py +++ b/noxfile.py @@ -77,16 +77,14 @@ def default(session): ) session.install("-e", ".") + # Temporarily install google-api-core from branch to test self-signed jwt + session.install("git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") + session.install("git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") + # Run py.test against the unit tests. session.run( "py.test", - "--quiet", - "--cov=google/cloud", - "--cov=tests/unit", - "--cov-append", - "--cov-config=.coveragerc", - "--cov-report=", - "--cov-fail-under=0", + "--collect-only", os.path.join("tests", "unit"), *session.posargs, ) @@ -127,6 +125,11 @@ def system(session): ) session.install("-e", ".") + # Temporarily install google-api-core from branch to test self-signed jwt + session.install("-e", "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") + session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") + + # Run py.test against the system tests. if system_test_exists: session.run("py.test", "--quiet", system_test_path, *session.posargs) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index bca0522e..631d8400 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -183,6 +183,10 @@ def _session_tests(session: nox.sessions.Session, post_install: Callable = None) if post_install: post_install(session) + # Temporarily install google-api-core from branch to test self-signed jwt + session.install("git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") + session.install("git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") + session.run( "pytest", *(PYTEST_COMMON_ARGS + session.posargs), From 76978d7044e849674b5940bf4959a5a66c2b6337 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Sat, 23 Jan 2021 01:26:15 +0000 Subject: [PATCH 02/12] test: update tests --- noxfile.py | 12 ++++++--- samples/snippets/noxfile.py | 4 +-- .../translate_v3/test_translation_service.py | 25 ++++++++++++++++++- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/noxfile.py b/noxfile.py index a43153c5..6dbdc4a2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -78,13 +78,19 @@ def default(session): session.install("-e", ".") # Temporarily install google-api-core from branch to test self-signed jwt - session.install("git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") - session.install("git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") + session.install("-e", "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") + session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") # Run py.test against the unit tests. session.run( "py.test", - "--collect-only", + "--quiet", + "--cov=google/cloud", + "--cov=tests/unit", + "--cov-append", + "--cov-config=.coveragerc", + "--cov-report=", + "--cov-fail-under=0", os.path.join("tests", "unit"), *session.posargs, ) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 631d8400..be2509fd 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -184,8 +184,8 @@ def _session_tests(session: nox.sessions.Session, post_install: Callable = None) post_install(session) # Temporarily install google-api-core from branch to test self-signed jwt - session.install("git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") - session.install("git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") + session.install("-e", "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") + session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") session.run( "pytest", diff --git a/tests/unit/gapic/translate_v3/test_translation_service.py b/tests/unit/gapic/translate_v3/test_translation_service.py index 215b08b1..d0c83a5b 100644 --- a/tests/unit/gapic/translate_v3/test_translation_service.py +++ b/tests/unit/gapic/translate_v3/test_translation_service.py @@ -2406,15 +2406,33 @@ def test_translation_service_auth_adc(): adc.return_value = (credentials.AnonymousCredentials(), None) TranslationServiceClient() adc.assert_called_once_with( - scopes=( + default_scopes=( "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-translation", ), + scopes=None, quota_project_id=None, ) def test_translation_service_transport_auth_adc(): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transports.TranslationServiceGrpcTransport( + quota_project_id="octopus" + ) + adc.assert_called_once_with( + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), + scopes=None, + quota_project_id="octopus", + ) + +def test_translation_service_transport_auth_adc_custom_host(): # If credentials and host are not provided, the transport class should use # ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -2423,6 +2441,10 @@ def test_translation_service_transport_auth_adc(): host="squid.clam.whelk", quota_project_id="octopus" ) adc.assert_called_once_with( + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), scopes=( "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-translation", @@ -2431,6 +2453,7 @@ def test_translation_service_transport_auth_adc(): ) + def test_translation_service_host_no_port(): client = TranslationServiceClient( credentials=credentials.AnonymousCredentials(), From 56f03d5ce0e9d75cc04f2d3e07508c04bf0dbf2d Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Mon, 25 Jan 2021 15:54:18 +0000 Subject: [PATCH 03/12] chore: blacken --- .../translation_service/transports/grpc.py | 4 +-- noxfile.py | 31 +++++++++++++------ .../translate_v3/test_translation_service.py | 6 ++-- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc.py b/google/cloud/translate_v3/services/translation_service/transports/grpc.py index a5ef2f25..4f38dd84 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc.py @@ -135,7 +135,7 @@ def __init__( credentials, _ = auth.default( default_scopes=self.AUTH_SCOPES, scopes=scopes, - quota_project_id=quota_project_id + quota_project_id=quota_project_id, ) # Create SSL credentials with client_cert_source or application @@ -169,7 +169,7 @@ def __init__( credentials, _ = auth.default( default_scopes=self.AUTH_SCOPES, scopes=scopes, - quota_project_id=quota_project_id + quota_project_id=quota_project_id, ) # create a new channel. The provided one is ignored. diff --git a/noxfile.py b/noxfile.py index 6dbdc4a2..d72b82ef 100644 --- a/noxfile.py +++ b/noxfile.py @@ -78,18 +78,24 @@ def default(session): session.install("-e", ".") # Temporarily install google-api-core from branch to test self-signed jwt - session.install("-e", "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") - session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") + session.install( + "-e", + "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core", + ) + session.install( + "-e", + "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth", + ) # Run py.test against the unit tests. session.run( "py.test", "--quiet", - "--cov=google/cloud", - "--cov=tests/unit", - "--cov-append", - "--cov-config=.coveragerc", - "--cov-report=", + "--cov=google/cloud", + "--cov=tests/unit", + "--cov-append", + "--cov-config=.coveragerc", + "--cov-report=", "--cov-fail-under=0", os.path.join("tests", "unit"), *session.posargs, @@ -132,9 +138,14 @@ def system(session): session.install("-e", ".") # Temporarily install google-api-core from branch to test self-signed jwt - session.install("-e", "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") - session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") - + session.install( + "-e", + "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core", + ) + session.install( + "-e", + "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth", + ) # Run py.test against the system tests. if system_test_exists: diff --git a/tests/unit/gapic/translate_v3/test_translation_service.py b/tests/unit/gapic/translate_v3/test_translation_service.py index d0c83a5b..7ac94764 100644 --- a/tests/unit/gapic/translate_v3/test_translation_service.py +++ b/tests/unit/gapic/translate_v3/test_translation_service.py @@ -2420,9 +2420,7 @@ def test_translation_service_transport_auth_adc(): # ADC credentials. with mock.patch.object(auth, "default") as adc: adc.return_value = (credentials.AnonymousCredentials(), None) - transports.TranslationServiceGrpcTransport( - quota_project_id="octopus" - ) + transports.TranslationServiceGrpcTransport(quota_project_id="octopus") adc.assert_called_once_with( default_scopes=( "https://www.googleapis.com/auth/cloud-platform", @@ -2432,6 +2430,7 @@ def test_translation_service_transport_auth_adc(): quota_project_id="octopus", ) + def test_translation_service_transport_auth_adc_custom_host(): # If credentials and host are not provided, the transport class should use # ADC credentials. @@ -2453,7 +2452,6 @@ def test_translation_service_transport_auth_adc_custom_host(): ) - def test_translation_service_host_no_port(): client = TranslationServiceClient( credentials=credentials.AnonymousCredentials(), From 5d0ca15b1f81c18f2254a7a8f70e506f5a597dfe Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 5 Feb 2021 23:46:33 +0000 Subject: [PATCH 04/12] tests: break out tests for various versions of auth, api-core --- .../translation_service/transports/grpc.py | 57 ++++++-- .../transports/grpc_asyncio.py | 35 ++++- noxfile.py | 36 +++-- samples/snippets/noxfile.py | 10 +- setup.py | 4 +- testing/constraints-3.10.txt | 0 testing/constraints-3.11.txt | 0 testing/constraints-3.6.txt | 17 +++ testing/constraints-3.7.txt | 0 testing/constraints-3.8.txt | 0 testing/constraints-3.9.txt | 0 .../translate_v3/test_translation_service.py | 125 +++++++++++++++++- 12 files changed, 246 insertions(+), 38 deletions(-) create mode 100644 testing/constraints-3.10.txt create mode 100644 testing/constraints-3.11.txt create mode 100644 testing/constraints-3.6.txt create mode 100644 testing/constraints-3.7.txt create mode 100644 testing/constraints-3.8.txt create mode 100644 testing/constraints-3.9.txt diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc.py b/google/cloud/translate_v3/services/translation_service/transports/grpc.py index 4f38dd84..cb31b1d3 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc.py @@ -18,12 +18,15 @@ import warnings from typing import Callable, Dict, Optional, Sequence, Tuple +import google.api_core from google.api_core import grpc_helpers # type: ignore from google.api_core import operations_v1 # type: ignore from google.api_core import gapic_v1 # type: ignore from google import auth # type: ignore from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore +import packaging.version +import pkg_resources import grpc # type: ignore @@ -33,6 +36,24 @@ from .base import TranslationServiceTransport, DEFAULT_CLIENT_INFO +try: + # google.auth.__version__ was added in 1.26.0 + _GOOGLE_AUTH_VERSION = auth.__version__ +except AttributeError: + try: # try pkg_resources if it is available + _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version + except pkg_resources.DistributionNotFound: # pragma: NO COVER + _GOOGLE_AUTH_VERSION = None + +try: + _API_CORE_VERSION = google.api_core.__version__ +except AttributeError: + try: # try pkg_resources + _API_CORE_VERSION = pkg_resources.get_distribution("google-api-core").version + except pkg_resources.DistributionNotFound: # pragma: NO COVER + _API_CORE_VERSION = None + + class TranslationServiceGrpcTransport(TranslationServiceTransport): """gRPC backend transport for TranslationService. @@ -111,6 +132,15 @@ def __init__( if host.split(":")[0] != self.DEFAULT_HOST and not scopes: scopes = self.AUTH_SCOPES + # TODO: Remove this if/else once google-auth >= 1.25.0 is required + if _GOOGLE_AUTH_VERSION and ( + packaging.version.parse(_GOOGLE_AUTH_VERSION) + >= packaging.version.parse("1.25.0") + ): + scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES} + else: + scopes_kwargs = {"scopes": scopes or self.AUTH_SCOPES} + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -133,9 +163,7 @@ def __init__( if credentials is None: credentials, _ = auth.default( - default_scopes=self.AUTH_SCOPES, - scopes=scopes, - quota_project_id=quota_project_id, + **scopes_kwargs, quota_project_id=quota_project_id, ) # Create SSL credentials with client_cert_source or application @@ -167,9 +195,7 @@ def __init__( if credentials is None: credentials, _ = auth.default( - default_scopes=self.AUTH_SCOPES, - scopes=scopes, - quota_project_id=quota_project_id, + **scopes_kwargs, quota_project_id=quota_project_id, ) # create a new channel. The provided one is ignored. @@ -234,17 +260,26 @@ def create_channel( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ - kwargs["default_scopes"] = cls.AUTH_SCOPES - # Only pass scopes if they exist - if scopes: - kwargs["scopes"] = scopes + + self_signed_jwt_kwargs = {} + + # TODO: Remove this if/else once google-api-core >= 1.26.0 is required + if _API_CORE_VERSION and ( + packaging.version.parse(_API_CORE_VERSION) + >= packaging.version.parse("1.26.0") + ): + self_signed_jwt_kwargs["default_scopes"] = cls.AUTH_SCOPES + self_signed_jwt_kwargs["scopes"] = scopes + self_signed_jwt_kwargs["default_host"] = cls.DEFAULT_HOST + else: + self_signed_jwt_kwargs["scopes"] = scopes or cls.AUTH_SCOPES return grpc_helpers.create_channel( host, credentials=credentials, credentials_file=credentials_file, quota_project_id=quota_project_id, - default_host=cls.DEFAULT_HOST, + **self_signed_jwt_kwargs, **kwargs, ) diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py b/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py index d63d8c0b..178b9320 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py @@ -24,6 +24,7 @@ from google import auth # type: ignore from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore +import packaging.version import grpc # type: ignore from grpc.experimental import aio # type: ignore @@ -33,6 +34,8 @@ from .base import TranslationServiceTransport, DEFAULT_CLIENT_INFO from .grpc import TranslationServiceGrpcTransport +from .grpc import _API_CORE_VERSION +from .grpc import _GOOGLE_AUTH_VERSION class TranslationServiceGrpcAsyncIOTransport(TranslationServiceTransport): @@ -82,7 +85,19 @@ def create_channel( Returns: aio.Channel: A gRPC AsyncIO channel object. """ - scopes = scopes or cls.AUTH_SCOPES + self_signed_jwt_kwargs = {} + + # TODO: Remove this if/else once google-api-core >= 1.26.0 is required + if _API_CORE_VERSION and ( + packaging.version.parse(_API_CORE_VERSION) + >= packaging.version.parse("1.26.0") + ): + self_signed_jwt_kwargs["default_scopes"] = cls.AUTH_SCOPES + self_signed_jwt_kwargs["scopes"] = scopes + self_signed_jwt_kwargs["default_host"] = cls.DEFAULT_HOST + else: + self_signed_jwt_kwargs["scopes"] = scopes or cls.AUTH_SCOPES + return grpc_helpers_async.create_channel( host, credentials=credentials, @@ -150,6 +165,20 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + # If a custom API endpoint is set, set scopes to ensure the auth + # library does not used the self-signed JWT flow for service + # accounts + if host.split(":")[0] != self.DEFAULT_HOST and not scopes: + scopes = self.AUTH_SCOPES + + # TODO: Remove this if/else once google-auth >= 1.25.0 is required + if _GOOGLE_AUTH_VERSION and packaging.version.parse( + _GOOGLE_AUTH_VERSION + ) >= packaging.version.parse("1.25.0"): + scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES} + else: + scopes_kwargs = {"scopes": scopes or self.AUTH_SCOPES} + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -172,7 +201,7 @@ def __init__( if credentials is None: credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + quota_project_id=quota_project_id, **scopes_kwargs ) # Create SSL credentials with client_cert_source or application @@ -204,7 +233,7 @@ def __init__( if credentials is None: credentials, _ = auth.default( - scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + quota_project_id=quota_project_id, **scopes_kwargs, ) # create a new channel. The provided one is ignored. diff --git a/noxfile.py b/noxfile.py index d72b82ef..2b625d37 100644 --- a/noxfile.py +++ b/noxfile.py @@ -18,6 +18,7 @@ from __future__ import absolute_import import os +import pathlib import shutil import nox @@ -30,6 +31,8 @@ SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] +CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() + @nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): @@ -69,24 +72,27 @@ def lint_setup_py(session): def default(session): + constraints_path = str( + CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" + ) + # Install all test dependencies, then install this package in-place. session.install("asyncmock", "pytest-asyncio") session.install( "mock", "pytest", "pytest-cov", ) - session.install("-e", ".") + session.install("-e", ".", "-c", constraints_path) # Temporarily install google-api-core from branch to test self-signed jwt session.install( "-e", - "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core", - ) - session.install( - "-e", - "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth", + "git+https://github.com/googleapis/python-api-core.git@master#egg=google-api-core", ) + # Install google-auth *again* since it will have been overwitten by api-core + session.install("google-auth", "-c", constraints_path) + # Run py.test against the unit tests. session.run( "py.test", @@ -111,6 +117,11 @@ def unit(session): @nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) def system(session): """Run the system test suite.""" + + constraints_path = str( + CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" + ) + system_test_path = os.path.join("tests", "system.py") system_test_folder_path = os.path.join("tests", "system") @@ -135,18 +146,17 @@ def system(session): session.install( "mock", "pytest", "google-cloud-testutils", ) - session.install("-e", ".") + session.install("-e", ".", "-c", constraints_path) - # Temporarily install google-api-core from branch to test self-signed jwt + # Temporarily install google-api-core from HEAD to test self-signed jwt session.install( "-e", - "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core", - ) - session.install( - "-e", - "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth", + "git+https://github.com/googleapis/python-api-core.git@master#egg=google-api-core", ) + # Install google-auth *again* since it will have been overwitten by api-core + session.install("google-auth", "-c", constraints_path) + # Run py.test against the system tests. if system_test_exists: session.run("py.test", "--quiet", system_test_path, *session.posargs) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index be2509fd..36b7bba7 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -183,10 +183,12 @@ def _session_tests(session: nox.sessions.Session, post_install: Callable = None) if post_install: post_install(session) - # Temporarily install google-api-core from branch to test self-signed jwt - session.install("-e", "git+https://github.com/googleapis/python-api-core.git@self-signed-jwt#egg=google-api-core") - session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") - + # Temporarily install google-api-core from HEAD to test self-signed jwt + session.install( + "-e", + "git+https://github.com/googleapis/python-api-core.git@master#egg=google-api-core", + ) + session.run( "pytest", *(PYTEST_COMMON_ARGS + session.posargs), diff --git a/setup.py b/setup.py index 9045fdcf..6716897f 100644 --- a/setup.py +++ b/setup.py @@ -30,9 +30,9 @@ release_status = "Development Status :: 5 - Production/Stable" dependencies = [ "google-api-core[grpc] >= 1.22.0, < 2.0.0dev", - "google-cloud-core >= 1.1.0, < 2.0dev", - "libcst >= 0.2.5", + "google-cloud-core >= 1.4.0, < 2.0dev", "proto-plus >= 0.4.0", + "packaging >= 14.3", ] extras = {} diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt new file mode 100644 index 00000000..e69de29b diff --git a/testing/constraints-3.11.txt b/testing/constraints-3.11.txt new file mode 100644 index 00000000..e69de29b diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt new file mode 100644 index 00000000..0b5874e1 --- /dev/null +++ b/testing/constraints-3.6.txt @@ -0,0 +1,17 @@ +# This constraints file is used to check that lower bounds +# are correct in setup.py +# List *all* library dependencies and extras in this file. +# Pin the version to the lower bound. +# +# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", +# Then this file should have foo==1.14.0 + +google-api-core==1.22.0 +google-cloud-core==1.4.0 +proto-plus==0.4.0 +packaging==14.3 + +# TODO: Remove when google-api-core >= 1.25.0 is required +# Install an older google-auth version to verify compatibility +# and exercise tests. +google-auth==1.21.1 \ No newline at end of file diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt new file mode 100644 index 00000000..e69de29b diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt new file mode 100644 index 00000000..e69de29b diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/gapic/translate_v3/test_translation_service.py b/tests/unit/gapic/translate_v3/test_translation_service.py index 7ac94764..294aecfe 100644 --- a/tests/unit/gapic/translate_v3/test_translation_service.py +++ b/tests/unit/gapic/translate_v3/test_translation_service.py @@ -21,6 +21,7 @@ import grpc from grpc.experimental import aio import math +import packaging.version import pytest from proto.marshal.rules.dates import DurationRule, TimestampRule @@ -43,12 +44,40 @@ ) from google.cloud.translate_v3.services.translation_service import pagers from google.cloud.translate_v3.services.translation_service import transports +from google.cloud.translate_v3.services.translation_service.transports.grpc import ( + _GOOGLE_AUTH_VERSION, +) +from google.cloud.translate_v3.services.translation_service.transports.grpc import ( + _API_CORE_VERSION, +) from google.cloud.translate_v3.types import translation_service from google.longrunning import operations_pb2 from google.oauth2 import service_account from google.protobuf import timestamp_pb2 as timestamp # type: ignore +# TODO(busunkim): Once google-api-core >= 1.26.0 is required: +# - Delete all the api-core and auth "less than" test cases +# - Delete these pytest markers (Make the "greater than or equal to" tests the default). +requires_google_auth_lt_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) >= packaging.version.parse("1.25.0"), + reason="This test requires google-auth < 1.25.0", +) +requires_google_auth_gte_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_GOOGLE_AUTH_VERSION) < packaging.version.parse("1.25.0"), + reason="This test requires google-auth >= 1.25.0", +) + +requires_api_core_lt_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_API_CORE_VERSION) >= packaging.version.parse("1.25.0"), + reason="This test requires google-api-core < 1.25.0", +) + +requires_api_core_gte_1_25_0 = pytest.mark.skipif( + packaging.version.parse(_API_CORE_VERSION) < packaging.version.parse("1.25.0"), + reason="This test requires google-api-core >= 1.25.0", +) + def client_cert_source_callback(): return b"cert bytes", b"key bytes" @@ -2391,7 +2420,7 @@ def test_translation_service_base_transport_with_credentials_file(): def test_translation_service_base_transport_with_adc(): # Test the default credentials are used if credentials and credentials_file are None. - with mock.patch.object(auth, "default") as adc, mock.patch( + with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch( "google.cloud.translate_v3.services.translation_service.transports.TranslationServiceTransport._prep_wrapped_messages" ) as Transport: Transport.return_value = None @@ -2400,37 +2429,123 @@ def test_translation_service_base_transport_with_adc(): adc.assert_called_once() +@requires_google_auth_gte_1_25_0 def test_translation_service_auth_adc(): # If no credentials are provided, we should use ADC credentials. - with mock.patch.object(auth, "default") as adc: + with mock.patch.object(auth, "default", autospec=True) as adc: adc.return_value = (credentials.AnonymousCredentials(), None) TranslationServiceClient() adc.assert_called_once_with( + scopes=None, default_scopes=( "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-translation", ), - scopes=None, quota_project_id=None, ) +@requires_google_auth_lt_1_25_0 +def test_translation_service_auth_adc_no_default_scopes(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(auth, "default", autospec=True) as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + TranslationServiceClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), + quota_project_id=None, + ) + + +@requires_google_auth_gte_1_25_0 def test_translation_service_transport_auth_adc(): # If credentials and host are not provided, the transport class should use # ADC credentials. - with mock.patch.object(auth, "default") as adc: + with mock.patch.object(auth, "default", autospec=True) as adc: adc.return_value = (credentials.AnonymousCredentials(), None) transports.TranslationServiceGrpcTransport(quota_project_id="octopus") adc.assert_called_once_with( + scopes=None, default_scopes=( "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-translation", ), - scopes=None, quota_project_id="octopus", ) +@requires_google_auth_lt_1_25_0 +def test_translation_service_transport_auth_adc_old_google_auth(): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default", autospec=True) as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transports.TranslationServiceGrpcTransport(quota_project_id="octopus") + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), + quota_project_id="octopus", + ) + +@requires_api_core_lt_1_25_0 +def test_translation_service_transport_auth_adc_old_api_core(): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object(grpc_helpers.create_channel, autospec=True) as create_channel: + + adc.return_value = (credentials.AnonymousCredentials(), None) + transports.TranslationServiceGrpcTransport(quota_project_id="octopus") + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), + quota_project_id="octopus", + ) + + create_channel.assert_called_with( + host="translation.googleapis.com", + credentials=credentials.AnonymousCredentials, + credentials_file=None, + quota_project_id="octopus", + scopes=("https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation") + ) + +@requires_api_core_gte_1_25_0 +def test_translation_service_transport_auth_adc_new_api_core(): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object(grpc_helpers.create_channel, autospec=True) as create_channel: + + adc.return_value = (credentials.AnonymousCredentials(), None) + transports.TranslationServiceGrpcTransport(quota_project_id="octopus") + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), + quota_project_id="octopus", + ) + + create_channel.assert_called_with( + host="translation.googleapis.com", + credentials=credentials.AnonymousCredentials, + credentials_file=None, + quota_project_id="octopus", + default_scopes=("https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation") + scopes=None, + default_host="translation.googleapis.com" + ) + + +@requires_google_auth_gte_1_25_0 def test_translation_service_transport_auth_adc_custom_host(): # If credentials and host are not provided, the transport class should use # ADC credentials. From e5f565e96c92908913ec16e5f3e82d5bc4dd15f2 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 9 Feb 2021 00:43:46 +0000 Subject: [PATCH 05/12] test: add tests for old/new api_core versions --- noxfile.py | 9 -- samples/snippets/noxfile.py | 10 ++- setup.py | 2 +- testing/constraints-3.6.txt | 2 +- .../translate_v3/test_translation_service.py | 83 ++++++++++--------- 5 files changed, 56 insertions(+), 50 deletions(-) diff --git a/noxfile.py b/noxfile.py index 2b625d37..582e197b 100644 --- a/noxfile.py +++ b/noxfile.py @@ -84,15 +84,6 @@ def default(session): ) session.install("-e", ".", "-c", constraints_path) - # Temporarily install google-api-core from branch to test self-signed jwt - session.install( - "-e", - "git+https://github.com/googleapis/python-api-core.git@master#egg=google-api-core", - ) - - # Install google-auth *again* since it will have been overwitten by api-core - session.install("google-auth", "-c", constraints_path) - # Run py.test against the unit tests. session.run( "py.test", diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 36b7bba7..49a948b3 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -92,7 +92,15 @@ def get_pytest_env_vars() -> Dict[str, str]: TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) -INSTALL_LIBRARY_FROM_SOURCE = bool(os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False)) +# INSTALL_LIBRARY_FROM_SOURCE Resolution Order: +# 1. TEST_CONFIG +# 2. Environment +# Default to "False" if not found + +if "INSTALL_LIBRARY_FROM_SOURCE" in TEST_CONFIG: + INSTALL_LIBRARY_FROM_SOURCE = TEST_CONFIG["INSTALL_LIBRARY_FROM_SOURCE"] +else: + INSTALL_LIBRARY_FROM_SOURCE = bool(os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False)) # # Style Checks # diff --git a/setup.py b/setup.py index 6716897f..308095f6 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ # 'Development Status :: 5 - Production/Stable' release_status = "Development Status :: 5 - Production/Stable" dependencies = [ - "google-api-core[grpc] >= 1.22.0, < 2.0.0dev", + "google-api-core[grpc] >= 1.22.2, < 2.0.0dev", "google-cloud-core >= 1.4.0, < 2.0dev", "proto-plus >= 0.4.0", "packaging >= 14.3", diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt index 0b5874e1..fe7b2613 100644 --- a/testing/constraints-3.6.txt +++ b/testing/constraints-3.6.txt @@ -6,7 +6,7 @@ # e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", # Then this file should have foo==1.14.0 -google-api-core==1.22.0 +google-api-core==1.22.2 google-cloud-core==1.4.0 proto-plus==0.4.0 packaging==14.3 diff --git a/tests/unit/gapic/translate_v3/test_translation_service.py b/tests/unit/gapic/translate_v3/test_translation_service.py index 294aecfe..94ba4139 100644 --- a/tests/unit/gapic/translate_v3/test_translation_service.py +++ b/tests/unit/gapic/translate_v3/test_translation_service.py @@ -68,16 +68,17 @@ reason="This test requires google-auth >= 1.25.0", ) -requires_api_core_lt_1_25_0 = pytest.mark.skipif( - packaging.version.parse(_API_CORE_VERSION) >= packaging.version.parse("1.25.0"), - reason="This test requires google-api-core < 1.25.0", +requires_api_core_lt_1_26_0 = pytest.mark.skipif( + packaging.version.parse(_API_CORE_VERSION) >= packaging.version.parse("1.26.0"), + reason="This test requires google-api-core < 1.26.0", ) -requires_api_core_gte_1_25_0 = pytest.mark.skipif( - packaging.version.parse(_API_CORE_VERSION) < packaging.version.parse("1.25.0"), - reason="This test requires google-api-core >= 1.25.0", +requires_api_core_gte_1_26_0 = pytest.mark.skipif( + packaging.version.parse(_API_CORE_VERSION) < packaging.version.parse("1.26.0"), + reason="This test requires google-api-core >= 1.26.0", ) + def client_cert_source_callback(): return b"cert bytes", b"key bytes" @@ -2492,56 +2493,62 @@ def test_translation_service_transport_auth_adc_old_google_auth(): quota_project_id="octopus", ) -@requires_api_core_lt_1_25_0 -def test_translation_service_transport_auth_adc_old_api_core(): + +@requires_api_core_lt_1_26_0 +def test_translation_service_transport_old_api_core(): # If credentials and host are not provided, the transport class should use # ADC credentials. - with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object(grpc_helpers.create_channel, autospec=True) as create_channel: - - adc.return_value = (credentials.AnonymousCredentials(), None) + with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object( + grpc_helpers, "create_channel", autospec=True + ) as create_channel: + creds = credentials.AnonymousCredentials() + adc.return_value = (creds, None) transports.TranslationServiceGrpcTransport(quota_project_id="octopus") - adc.assert_called_once_with( + + create_channel.assert_called_with( + "translate.googleapis.com:443", + credentials=creds, + credentials_file=None, + quota_project_id="octopus", scopes=( "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-translation", ), - quota_project_id="octopus", + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], ) - create_channel.assert_called_with( - host="translation.googleapis.com", - credentials=credentials.AnonymousCredentials, - credentials_file=None, - quota_project_id="octopus", - scopes=("https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/cloud-translation") - ) -@requires_api_core_gte_1_25_0 -def test_translation_service_transport_auth_adc_new_api_core(): +@requires_api_core_gte_1_26_0 +def test_translation_service_transport_new_api_core(): # If credentials and host are not provided, the transport class should use # ADC credentials. - with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object(grpc_helpers.create_channel, autospec=True) as create_channel: - - adc.return_value = (credentials.AnonymousCredentials(), None) + with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object( + grpc_helpers, "create_channel", autospec=True + ) as create_channel: + creds = credentials.AnonymousCredentials() + adc.return_value = (creds, None) transports.TranslationServiceGrpcTransport(quota_project_id="octopus") - adc.assert_called_once_with( - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/cloud-translation", - ), - quota_project_id="octopus", - ) create_channel.assert_called_with( - host="translation.googleapis.com", - credentials=credentials.AnonymousCredentials, + "translate.googleapis.com:443", + credentials=creds, credentials_file=None, quota_project_id="octopus", - default_scopes=("https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/cloud-translation") + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), scopes=None, - default_host="translation.googleapis.com" + default_host="translate.googleapis.com", + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], ) From 9408a194170e8d047bbc1f294a27f2821b5adadc Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 9 Feb 2021 01:03:22 +0000 Subject: [PATCH 06/12] fix: remove try/except for api_core version --- .../services/translation_service/transports/grpc.py | 9 +-------- .../unit/gapic/translate_v3/test_translation_service.py | 4 ++-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc.py b/google/cloud/translate_v3/services/translation_service/transports/grpc.py index cb31b1d3..297bee1b 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc.py @@ -45,14 +45,7 @@ except pkg_resources.DistributionNotFound: # pragma: NO COVER _GOOGLE_AUTH_VERSION = None -try: - _API_CORE_VERSION = google.api_core.__version__ -except AttributeError: - try: # try pkg_resources - _API_CORE_VERSION = pkg_resources.get_distribution("google-api-core").version - except pkg_resources.DistributionNotFound: # pragma: NO COVER - _API_CORE_VERSION = None - +_API_CORE_VERSION = google.api_core.__version__ class TranslationServiceGrpcTransport(TranslationServiceTransport): """gRPC backend transport for TranslationService. diff --git a/tests/unit/gapic/translate_v3/test_translation_service.py b/tests/unit/gapic/translate_v3/test_translation_service.py index 94ba4139..4da1a9ba 100644 --- a/tests/unit/gapic/translate_v3/test_translation_service.py +++ b/tests/unit/gapic/translate_v3/test_translation_service.py @@ -2495,7 +2495,7 @@ def test_translation_service_transport_auth_adc_old_google_auth(): @requires_api_core_lt_1_26_0 -def test_translation_service_transport_old_api_core(): +def test_translation_service_transport_create_channel_old_api_core(): # If credentials and host are not provided, the transport class should use # ADC credentials. with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object( @@ -2523,7 +2523,7 @@ def test_translation_service_transport_old_api_core(): @requires_api_core_gte_1_26_0 -def test_translation_service_transport_new_api_core(): +def test_translation_service_transport_create_channel(): # If credentials and host are not provided, the transport class should use # ADC credentials. with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object( From 8fc35866199e6554ac7d63ceb9f0c002422c1e73 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 9 Feb 2021 17:54:34 +0000 Subject: [PATCH 07/12] chore: blacken --- .../translate_v3/services/translation_service/transports/grpc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc.py b/google/cloud/translate_v3/services/translation_service/transports/grpc.py index 297bee1b..635dbbda 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc.py @@ -47,6 +47,7 @@ _API_CORE_VERSION = google.api_core.__version__ + class TranslationServiceGrpcTransport(TranslationServiceTransport): """gRPC backend transport for TranslationService. From 884889af832b53b8faa1f8f440be9458ea24cc2d Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 9 Feb 2021 18:08:56 +0000 Subject: [PATCH 08/12] chore: undo changes to sample noxfile --- samples/snippets/noxfile.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 49a948b3..bca0522e 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -92,15 +92,7 @@ def get_pytest_env_vars() -> Dict[str, str]: TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) -# INSTALL_LIBRARY_FROM_SOURCE Resolution Order: -# 1. TEST_CONFIG -# 2. Environment -# Default to "False" if not found - -if "INSTALL_LIBRARY_FROM_SOURCE" in TEST_CONFIG: - INSTALL_LIBRARY_FROM_SOURCE = TEST_CONFIG["INSTALL_LIBRARY_FROM_SOURCE"] -else: - INSTALL_LIBRARY_FROM_SOURCE = bool(os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False)) +INSTALL_LIBRARY_FROM_SOURCE = bool(os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False)) # # Style Checks # @@ -191,12 +183,6 @@ def _session_tests(session: nox.sessions.Session, post_install: Callable = None) if post_install: post_install(session) - # Temporarily install google-api-core from HEAD to test self-signed jwt - session.install( - "-e", - "git+https://github.com/googleapis/python-api-core.git@master#egg=google-api-core", - ) - session.run( "pytest", *(PYTEST_COMMON_ARGS + session.posargs), From 72a695773c08a5d923c6700668bf0fd1bd2351fe Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Wed, 10 Feb 2021 01:22:18 +0000 Subject: [PATCH 09/12] tests: add async tests, add additional coverage --- .../transports/grpc_asyncio.py | 8 +- .../translate_v3/test_translation_service.py | 125 +++++++++++++----- 2 files changed, 98 insertions(+), 35 deletions(-) diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py b/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py index 178b9320..13c19e3e 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py @@ -102,8 +102,8 @@ def create_channel( host, credentials=credentials, credentials_file=credentials_file, - scopes=scopes, quota_project_id=quota_project_id, + **self_signed_jwt_kwargs, **kwargs, ) @@ -220,7 +220,7 @@ def __init__( credentials=credentials, credentials_file=credentials_file, ssl_credentials=ssl_credentials, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, options=[ ("grpc.max_send_message_length", -1), @@ -242,7 +242,7 @@ def __init__( credentials=credentials, credentials_file=credentials_file, ssl_credentials=ssl_channel_credentials, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, options=[ ("grpc.max_send_message_length", -1), @@ -255,7 +255,7 @@ def __init__( host=host, credentials=credentials, credentials_file=credentials_file, - scopes=scopes or self.AUTH_SCOPES, + scopes=scopes, quota_project_id=quota_project_id, client_info=client_info, ) diff --git a/tests/unit/gapic/translate_v3/test_translation_service.py b/tests/unit/gapic/translate_v3/test_translation_service.py index 4da1a9ba..c33e8ce1 100644 --- a/tests/unit/gapic/translate_v3/test_translation_service.py +++ b/tests/unit/gapic/translate_v3/test_translation_service.py @@ -2447,7 +2447,7 @@ def test_translation_service_auth_adc(): @requires_google_auth_lt_1_25_0 -def test_translation_service_auth_adc_no_default_scopes(): +def test_translation_service_auth_adc_old_google_auth(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default", autospec=True) as adc: adc.return_value = (credentials.AnonymousCredentials(), None) @@ -2467,24 +2467,61 @@ def test_translation_service_transport_auth_adc(): # ADC credentials. with mock.patch.object(auth, "default", autospec=True) as adc: adc.return_value = (credentials.AnonymousCredentials(), None) - transports.TranslationServiceGrpcTransport(quota_project_id="octopus") + transports.TranslationServiceGrpcTransport( + quota_project_id="octopus", scopes=["1", "2"] + ) + adc.assert_called_once_with( + scopes=["1", "2"], + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.TranslationServiceGrpcTransport, + transports.TranslationServiceGrpcAsyncIOTransport, + ], +) +@requires_google_auth_gte_1_25_0 +def test_translation_service_transport_auth_adc_custom_host(transport_class): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transport_class(host="squid.clam.whelk", quota_project_id="octopus") adc.assert_called_once_with( - scopes=None, default_scopes=( "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-translation", ), + # scopes should be set since a custom endpoint (host) was set + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), quota_project_id="octopus", ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.TranslationServiceGrpcTransport, + transports.TranslationServiceGrpcAsyncIOTransport, + ], +) @requires_google_auth_lt_1_25_0 -def test_translation_service_transport_auth_adc_old_google_auth(): +def test_translation_service_transport_auth_adc_old_google_auth(transport_class): # If credentials and host are not provided, the transport class should use # ADC credentials. with mock.patch.object(auth, "default", autospec=True) as adc: adc.return_value = (credentials.AnonymousCredentials(), None) - transports.TranslationServiceGrpcTransport(quota_project_id="octopus") + transport_class(quota_project_id="octopus") adc.assert_called_once_with( scopes=( "https://www.googleapis.com/auth/cloud-platform", @@ -2494,8 +2531,15 @@ def test_translation_service_transport_auth_adc_old_google_auth(): ) -@requires_api_core_lt_1_26_0 -def test_translation_service_transport_create_channel_old_api_core(): +@pytest.mark.parametrize( + "transport_class,grpc_helpers", + [ + (transports.TranslationServiceGrpcTransport, grpc_helpers), + (transports.TranslationServiceGrpcAsyncIOTransport, grpc_helpers_async), + ], +) +@requires_api_core_gte_1_26_0 +def test_translation_service_transport_create_channel(transport_class, grpc_helpers): # If credentials and host are not provided, the transport class should use # ADC credentials. with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object( @@ -2503,17 +2547,19 @@ def test_translation_service_transport_create_channel_old_api_core(): ) as create_channel: creds = credentials.AnonymousCredentials() adc.return_value = (creds, None) - transports.TranslationServiceGrpcTransport(quota_project_id="octopus") + transport_class(quota_project_id="octopus", scopes=["1", "2"]) create_channel.assert_called_with( "translate.googleapis.com:443", credentials=creds, credentials_file=None, quota_project_id="octopus", - scopes=( + default_scopes=( "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-translation", ), + scopes=["1", "2"], + default_host="translate.googleapis.com", ssl_credentials=None, options=[ ("grpc.max_send_message_length", -1), @@ -2522,8 +2568,15 @@ def test_translation_service_transport_create_channel_old_api_core(): ) -@requires_api_core_gte_1_26_0 -def test_translation_service_transport_create_channel(): +@pytest.mark.parametrize( + "transport_class,grpc_helpers", + [ + (transports.TranslationServiceGrpcTransport, grpc_helpers), + (transports.TranslationServiceGrpcAsyncIOTransport, grpc_helpers_async), + ], +) +@requires_api_core_lt_1_26_0 +def test_translation_service_transport_create_channel(transport_class, grpc_helpers): # If credentials and host are not provided, the transport class should use # ADC credentials. with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object( @@ -2531,19 +2584,17 @@ def test_translation_service_transport_create_channel(): ) as create_channel: creds = credentials.AnonymousCredentials() adc.return_value = (creds, None) - transports.TranslationServiceGrpcTransport(quota_project_id="octopus") + transport_class(quota_project_id="octopus") create_channel.assert_called_with( "translate.googleapis.com:443", credentials=creds, credentials_file=None, quota_project_id="octopus", - default_scopes=( + scopes=( "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/cloud-translation", ), - scopes=None, - default_host="translate.googleapis.com", ssl_credentials=None, options=[ ("grpc.max_send_message_length", -1), @@ -2552,25 +2603,37 @@ def test_translation_service_transport_create_channel(): ) -@requires_google_auth_gte_1_25_0 -def test_translation_service_transport_auth_adc_custom_host(): +@pytest.mark.parametrize( + "transport_class,grpc_helpers", + [ + (transports.TranslationServiceGrpcTransport, grpc_helpers), + (transports.TranslationServiceGrpcAsyncIOTransport, grpc_helpers_async), + ], +) +@requires_api_core_lt_1_26_0 +def test_translation_service_transport_create_channel_old_api_core_user_scopes( + transport_class, grpc_helpers +): # If credentials and host are not provided, the transport class should use # ADC credentials. - with mock.patch.object(auth, "default") as adc: - adc.return_value = (credentials.AnonymousCredentials(), None) - transports.TranslationServiceGrpcTransport( - host="squid.clam.whelk", quota_project_id="octopus" - ) - adc.assert_called_once_with( - default_scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/cloud-translation", - ), - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/cloud-translation", - ), + with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch.object( + grpc_helpers, "create_channel", autospec=True + ) as create_channel: + creds = credentials.AnonymousCredentials() + adc.return_value = (creds, None) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) + + create_channel.assert_called_with( + "translate.googleapis.com:443", + credentials=creds, + credentials_file=None, quota_project_id="octopus", + scopes=["1", "2"], + ssl_credentials=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], ) From 2f16d21ddc6366caccb13016f5d17785d90dbccf Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 11 Feb 2021 21:58:29 +0000 Subject: [PATCH 10/12] fix: move api_core, auth version detection into base transport --- .../translation_service/transports/base.py | 36 ++++++++++++++-- .../translation_service/transports/grpc.py | 15 +------ .../transports/grpc_asyncio.py | 6 +-- .../translate_v3/test_translation_service.py | 41 ++++++++++++++++--- 4 files changed, 72 insertions(+), 26 deletions(-) diff --git a/google/cloud/translate_v3/services/translation_service/transports/base.py b/google/cloud/translate_v3/services/translation_service/transports/base.py index 5af8284b..5b67ea9a 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/base.py +++ b/google/cloud/translate_v3/services/translation_service/transports/base.py @@ -17,9 +17,11 @@ import abc import typing +import packaging.version import pkg_resources from google import auth # type: ignore +import google.api_core from google.api_core import exceptions # type: ignore from google.api_core import gapic_v1 # type: ignore from google.api_core import retry as retries # type: ignore @@ -37,6 +39,17 @@ except pkg_resources.DistributionNotFound: DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() +try: + # google.auth.__version__ was added in 1.26.0 + _GOOGLE_AUTH_VERSION = auth.__version__ +except AttributeError: + try: # try pkg_resources if it is available + _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version + except pkg_resources.DistributionNotFound: # pragma: NO COVER + _GOOGLE_AUTH_VERSION = None + +_API_CORE_VERSION = google.api_core.__version__ + class TranslationServiceTransport(abc.ABC): """Abstract transport class for TranslationService.""" @@ -53,7 +66,7 @@ def __init__( host: str = DEFAULT_HOST, credentials: credentials.Credentials = None, credentials_file: typing.Optional[str] = None, - scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES, + scopes: typing.Optional[typing.Sequence[str]] = None, quota_project_id: typing.Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, **kwargs, @@ -70,7 +83,7 @@ def __init__( credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. This argument is mutually exclusive with credentials. - scope (Optional[Sequence[str]]): A list of scopes. + scopes (Optional[Sequence[str]]): A list of scopes. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -84,6 +97,21 @@ def __init__( host += ":443" self._host = host + # If a custom API endpoint is set, set scopes to ensure the auth + # library does not used the self-signed JWT flow for service + # accounts + if host.split(":")[0] != self.DEFAULT_HOST and not scopes: + scopes = self.AUTH_SCOPES + + # TODO: Remove this if/else once google-auth >= 1.25.0 is required + if _GOOGLE_AUTH_VERSION and ( + packaging.version.parse(_GOOGLE_AUTH_VERSION) + >= packaging.version.parse("1.25.0") + ): + scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES} + else: + scopes_kwargs = {"scopes": scopes or self.AUTH_SCOPES} + # If no credentials are provided, then determine the appropriate # defaults. if credentials and credentials_file: @@ -93,12 +121,12 @@ def __init__( if credentials_file is not None: credentials, _ = auth.load_credentials_from_file( - credentials_file, scopes=scopes, quota_project_id=quota_project_id + credentials_file, **scopes_kwargs, quota_project_id=quota_project_id ) elif credentials is None: credentials, _ = auth.default( - scopes=scopes, quota_project_id=quota_project_id + **scopes_kwargs, quota_project_id=quota_project_id ) # Save the credentials. diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc.py b/google/cloud/translate_v3/services/translation_service/transports/grpc.py index 635dbbda..04f751d1 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc.py @@ -18,14 +18,13 @@ import warnings from typing import Callable, Dict, Optional, Sequence, Tuple -import google.api_core from google.api_core import grpc_helpers # type: ignore from google.api_core import operations_v1 # type: ignore from google.api_core import gapic_v1 # type: ignore from google import auth # type: ignore from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore -import packaging.version +import packaging import pkg_resources import grpc # type: ignore @@ -34,19 +33,9 @@ from google.longrunning import operations_pb2 as operations # type: ignore from .base import TranslationServiceTransport, DEFAULT_CLIENT_INFO +from .base import _GOOGLE_AUTH_VERSION, _API_CORE_VERSION -try: - # google.auth.__version__ was added in 1.26.0 - _GOOGLE_AUTH_VERSION = auth.__version__ -except AttributeError: - try: # try pkg_resources if it is available - _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version - except pkg_resources.DistributionNotFound: # pragma: NO COVER - _GOOGLE_AUTH_VERSION = None - -_API_CORE_VERSION = google.api_core.__version__ - class TranslationServiceGrpcTransport(TranslationServiceTransport): """gRPC backend transport for TranslationService. diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py b/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py index 13c19e3e..51e647ca 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + # -*- coding: utf-8 -*- # Copyright 2020 Google LLC # @@ -33,10 +33,8 @@ from google.longrunning import operations_pb2 as operations # type: ignore from .base import TranslationServiceTransport, DEFAULT_CLIENT_INFO +from .base import _GOOGLE_AUTH_VERSION, _API_CORE_VERSION from .grpc import TranslationServiceGrpcTransport -from .grpc import _API_CORE_VERSION -from .grpc import _GOOGLE_AUTH_VERSION - class TranslationServiceGrpcAsyncIOTransport(TranslationServiceTransport): """gRPC AsyncIO backend transport for TranslationService. diff --git a/tests/unit/gapic/translate_v3/test_translation_service.py b/tests/unit/gapic/translate_v3/test_translation_service.py index c33e8ce1..a395a6f0 100644 --- a/tests/unit/gapic/translate_v3/test_translation_service.py +++ b/tests/unit/gapic/translate_v3/test_translation_service.py @@ -44,10 +44,10 @@ ) from google.cloud.translate_v3.services.translation_service import pagers from google.cloud.translate_v3.services.translation_service import transports -from google.cloud.translate_v3.services.translation_service.transports.grpc import ( +from google.cloud.translate_v3.services.translation_service.transports.base import ( _GOOGLE_AUTH_VERSION, ) -from google.cloud.translate_v3.services.translation_service.transports.grpc import ( +from google.cloud.translate_v3.services.translation_service.transports.base import ( _API_CORE_VERSION, ) from google.cloud.translate_v3.types import translation_service @@ -2396,8 +2396,32 @@ def test_translation_service_base_transport(): with pytest.raises(NotImplementedError): transport.operations_client - +@requires_google_auth_gte_1_25_0 def test_translation_service_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + auth, "load_credentials_from_file" + ) as load_creds, mock.patch( + "google.cloud.translate_v3.services.translation_service.transports.TranslationServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TranslationServiceTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=None, + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), + quota_project_id="octopus", + ) + + +@requires_google_auth_lt_1_25_0 +def test_translation_service_base_transport_with_credentials_file_old_google_auth(): # Instantiate the base transport with a credentials file with mock.patch.object( auth, "load_credentials_from_file" @@ -2461,13 +2485,20 @@ def test_translation_service_auth_adc_old_google_auth(): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.TranslationServiceGrpcTransport, + transports.TranslationServiceGrpcAsyncIOTransport, + ], +) @requires_google_auth_gte_1_25_0 -def test_translation_service_transport_auth_adc(): +def test_translation_service_transport_auth_adc(transport_class): # If credentials and host are not provided, the transport class should use # ADC credentials. with mock.patch.object(auth, "default", autospec=True) as adc: adc.return_value = (credentials.AnonymousCredentials(), None) - transports.TranslationServiceGrpcTransport( + transport_class( quota_project_id="octopus", scopes=["1", "2"] ) adc.assert_called_once_with( From b9b29e7fa94ec9f0354ae12ec596c4b331c0878a Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 11 Feb 2021 22:27:17 +0000 Subject: [PATCH 11/12] test: add base transport custom host test --- .../translation_service/transports/grpc.py | 1 - .../transports/grpc_asyncio.py | 3 +- .../translate_v3/test_translation_service.py | 35 +++++++++++++++++-- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc.py b/google/cloud/translate_v3/services/translation_service/transports/grpc.py index 04f751d1..142f2fc2 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc.py @@ -36,7 +36,6 @@ from .base import _GOOGLE_AUTH_VERSION, _API_CORE_VERSION - class TranslationServiceGrpcTransport(TranslationServiceTransport): """gRPC backend transport for TranslationService. diff --git a/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py b/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py index 51e647ca..586777cf 100644 --- a/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py +++ b/google/cloud/translate_v3/services/translation_service/transports/grpc_asyncio.py @@ -1,4 +1,4 @@ - # -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # Copyright 2020 Google LLC # @@ -36,6 +36,7 @@ from .base import _GOOGLE_AUTH_VERSION, _API_CORE_VERSION from .grpc import TranslationServiceGrpcTransport + class TranslationServiceGrpcAsyncIOTransport(TranslationServiceTransport): """gRPC AsyncIO backend transport for TranslationService. diff --git a/tests/unit/gapic/translate_v3/test_translation_service.py b/tests/unit/gapic/translate_v3/test_translation_service.py index a395a6f0..3a1f061a 100644 --- a/tests/unit/gapic/translate_v3/test_translation_service.py +++ b/tests/unit/gapic/translate_v3/test_translation_service.py @@ -2396,6 +2396,7 @@ def test_translation_service_base_transport(): with pytest.raises(NotImplementedError): transport.operations_client + @requires_google_auth_gte_1_25_0 def test_translation_service_base_transport_with_credentials_file(): # Instantiate the base transport with a credentials file @@ -2443,6 +2444,36 @@ def test_translation_service_base_transport_with_credentials_file_old_google_aut ) +@requires_google_auth_gte_1_25_0 +def test_translation_service_base_transport_custom_host(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + auth, "load_credentials_from_file" + ) as load_creds, mock.patch( + "google.cloud.translate_v3.services.translation_service.transports.TranslationServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TranslationServiceTransport( + host="squid.clam.whelk", + credentials_file="credentials.json", + quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + # scopes should be set since custom endpoint was passed + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), + default_scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/cloud-translation", + ), + quota_project_id="octopus", + ) + + def test_translation_service_base_transport_with_adc(): # Test the default credentials are used if credentials and credentials_file are None. with mock.patch.object(auth, "default", autospec=True) as adc, mock.patch( @@ -2498,9 +2529,7 @@ def test_translation_service_transport_auth_adc(transport_class): # ADC credentials. with mock.patch.object(auth, "default", autospec=True) as adc: adc.return_value = (credentials.AnonymousCredentials(), None) - transport_class( - quota_project_id="octopus", scopes=["1", "2"] - ) + transport_class(quota_project_id="octopus", scopes=["1", "2"]) adc.assert_called_once_with( scopes=["1", "2"], default_scopes=( From 4727d2ccd975db1b64e7b6d8b875e442c0f2f956 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Mon, 22 Feb 2021 18:11:29 +0000 Subject: [PATCH 12/12] test: use lower-bound-checker to enforce constraints --- noxfile.py | 36 ++++++++++++++++++++++++++++++++++++ setup.py | 2 +- testing/constraints-3.6.txt | 15 +-------------- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/noxfile.py b/noxfile.py index 582e197b..0e3f1430 100644 --- a/noxfile.py +++ b/noxfile.py @@ -18,7 +18,9 @@ from __future__ import absolute_import import os +import sys import pathlib +import subprocess import shutil import nox @@ -33,6 +35,9 @@ CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() +LOWER_BOUND_CONSTRAINTS_FILE = CURRENT_DIRECTORY / "testing" / "constraints-3.6.txt" +PACKAGE_NAME = package_name = subprocess.check_output([sys.executable, "setup.py", "--name"], encoding='utf-8').strip() + @nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): @@ -168,6 +173,37 @@ def cover(session): session.run("coverage", "erase") +@nox.session(python="3.8") +def check_lower_bounds(session): + """Check lower bounds in setup.py are reflected in constraints file""" + session.install("google-cloud-testutils") + session.install(".") + + session.run( + "lower-bound-checker", + "check", + "--package-name", + PACKAGE_NAME, + "--constraints-file", + str(LOWER_BOUND_CONSTRAINTS_FILE), + ) + + +@nox.session(python="3.8") +def update_lower_bounds(session): + """Update lower bounds in constraints.txt to match setup.py""" + session.install("google-cloud-testutils") + session.install(".") + + session.run( + "lower-bound-checker", + "update", + "--package-name", + PACKAGE_NAME, + "--constraints-file", + str(LOWER_BOUND_CONSTRAINTS_FILE), + ) + @nox.session(python=DEFAULT_PYTHON_VERSION) def docs(session): """Build the docs for this library.""" diff --git a/setup.py b/setup.py index 308095f6..1bd0bca9 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ dependencies = [ "google-api-core[grpc] >= 1.22.2, < 2.0.0dev", "google-cloud-core >= 1.4.0, < 2.0dev", - "proto-plus >= 0.4.0", + "proto-plus >= 1.4.0", "packaging >= 14.3", ] extras = {} diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt index fe7b2613..81b134a7 100644 --- a/testing/constraints-3.6.txt +++ b/testing/constraints-3.6.txt @@ -1,17 +1,4 @@ -# This constraints file is used to check that lower bounds -# are correct in setup.py -# List *all* library dependencies and extras in this file. -# Pin the version to the lower bound. -# -# e.g., if setup.py has "foo >= 1.14.0, < 2.0.0dev", -# Then this file should have foo==1.14.0 - google-api-core==1.22.2 google-cloud-core==1.4.0 -proto-plus==0.4.0 packaging==14.3 - -# TODO: Remove when google-api-core >= 1.25.0 is required -# Install an older google-auth version to verify compatibility -# and exercise tests. -google-auth==1.21.1 \ No newline at end of file +proto-plus==1.4.0 \ No newline at end of file