From 58f0b7cf1141d4b9df8edf77b9b0cb0d3f7f3972 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Tue, 17 Sep 2024 03:16:51 +0000 Subject: [PATCH 1/4] chore: add showcase e2e tests for async rest --- .../%service/transports/rest_asyncio.py.j2 | 4 +- noxfile.py | 33 ++++++++++++ .../cloud_redis/transports/rest_asyncio.py | 3 +- tests/system/conftest.py | 23 +++++---- tests/system/test_client_context_manager.py | 3 +- tests/system/test_lro.py | 6 +++ tests/system/test_pagination.py | 12 +++++ tests/system/test_resource_crud.py | 12 +++++ tests/system/test_retry.py | 3 +- tests/system/test_streams.py | 51 +++++++++++++++++++ tests/system/test_unary.py | 11 ++-- 11 files changed, 144 insertions(+), 17 deletions(-) diff --git a/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 b/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 index 7ef5f9cb7..2cc6e88ab 100644 --- a/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 +++ b/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 @@ -5,6 +5,7 @@ {% block content %} +import google.auth try: from google.auth.aio.transport.sessions import AsyncAuthorizedSession # type: ignore except ImportError as e: # pragma: NO COVER @@ -35,11 +36,10 @@ try: except AttributeError: # pragma: NO COVER OptionalRetry = Union[retries.AsyncRetry, object, None] # type: ignore -{# TODO (https://github.com/googleapis/gapic-generator-python/issues/2128): Update `rest_version` to include the transport dependency version. #} DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, grpc_version=None, - rest_version=None, + rest_version=google.auth.__version__ ) {# TODO: Add an `_interceptor` property once implemented #} diff --git a/noxfile.py b/noxfile.py index 2eedbc440..50056ae6e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -341,6 +341,39 @@ def showcase( env=env, ) +@nox.session(python=ALL_PYTHON) +def showcase_w_rest_async( + session, + templates="DEFAULT", + other_opts: typing.Iterable[str] = (), + env: typing.Optional[typing.Dict[str, str]] = {}, +): + """Run the Showcase test suite.""" + + with showcase_library(session, templates=templates, other_opts=other_opts, rest_async_io_enabled=True): + session.install("aiohttp") + session.install("pytest", "pytest-asyncio") + test_directory = Path("tests", "system") + ignore_file = env.get("IGNORE_FILE") + pytest_command = [ + "py.test", + "--quiet", + *(session.posargs or [str(test_directory)]), + ] + if ignore_file: + ignore_path = test_directory / ignore_file + pytest_command.extend(["--ignore", str(ignore_path)]) + + # Note: google-api-core and google-auth are re-installed here to override the version installed in constraints. + # TODO(https://github.com/googleapis/python-api-core/pull/694): Update the version of google-api-core once the linked PR is merged. + session.install('--no-cache-dir', '--force-reinstall', "google-api-core[grpc]@git+https://github.com/googleapis/python-api-core.git@7dea20d73878eca93b61bb82ae6ddf335fb3a8ca") + # TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. + session.install('--no-cache-dir', '--force-reinstall', "google-auth@git+https://github.com/googleapis/google-auth-library-python.git") + session.run( + *pytest_command, + env=env, + ) + @nox.session(python=NEWEST_PYTHON) def showcase_mtls( diff --git a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py index 3381a80c6..a21ff26d8 100755 --- a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py +++ b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import google.auth try: from google.auth.aio.transport.sessions import AsyncAuthorizedSession # type: ignore except ImportError as e: # pragma: NO COVER @@ -48,7 +49,7 @@ DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, grpc_version=None, - rest_version=None, + rest_version=google.auth.__version__ ) @dataclasses.dataclass diff --git a/tests/system/conftest.py b/tests/system/conftest.py index 7b541976f..6507078b6 100644 --- a/tests/system/conftest.py +++ b/tests/system/conftest.py @@ -37,6 +37,11 @@ import asyncio from google.showcase import EchoAsyncClient from google.showcase import IdentityAsyncClient + try: + import google.showcase.transports.rest_asyncio + SHOWCASE_ASYNC_TRANSPORTS = ["grpc_asyncio", "rest_asyncio"] + except: + SHOWCASE_ASYNC_TRANSPORTS = ["grpc_asyncio"] # TODO: use async auth anon credentials by default once the minimum version of google-auth is upgraded. # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2107. @@ -57,23 +62,23 @@ def async_anonymous_credentials(): def event_loop(): return asyncio.get_event_loop() - @pytest.fixture - def async_echo(use_mtls, event_loop): + @pytest.fixture(params=SHOWCASE_ASYNC_TRANSPORTS) + def async_echo(use_mtls, request, event_loop): return construct_client( EchoAsyncClient, use_mtls, - transport_name="grpc_asyncio", - channel_creator=aio.insecure_channel, + transport_name=request.param, + channel_creator=aio.insecure_channel if request.param == "grpc_asyncio" else None, credentials=async_anonymous_credentials(), ) - @pytest.fixture - def async_identity(use_mtls, event_loop): + @pytest.fixture(params=SHOWCASE_ASYNC_TRANSPORTS) + def async_identity(use_mtls, request, event_loop): return construct_client( IdentityAsyncClient, use_mtls, - transport_name="grpc_asyncio", - channel_creator=aio.insecure_channel, + transport_name=request.param, + channel_creator=aio.insecure_channel if request.param == "grpc_asyncio" else None, credentials=async_anonymous_credentials(), ) @@ -135,7 +140,7 @@ def construct_client( credentials=credentials, channel=channel_creator(transport_endpoint), ) - elif transport_name == "rest": + elif transport_name in ["rest", "rest_asyncio"]: # The custom host explicitly bypasses https. transport = transport_cls( credentials=credentials, diff --git a/tests/system/test_client_context_manager.py b/tests/system/test_client_context_manager.py index 0d20292dc..541de4c5b 100644 --- a/tests/system/test_client_context_manager.py +++ b/tests/system/test_client_context_manager.py @@ -15,6 +15,7 @@ import os import pytest import grpc +from google.auth import exceptions def test_client(echo): @@ -50,7 +51,7 @@ async def test_client_async(async_echo): @pytest.mark.asyncio async def test_client_destroyed_async(async_echo): await async_echo.__aexit__(None, None, None) - with pytest.raises(grpc._cython.cygrpc.UsageError): + with pytest.raises((grpc._cython.cygrpc.UsageError, exceptions.TransportError)): await async_echo.echo({ 'content': 'hello' }) diff --git a/tests/system/test_lro.py b/tests/system/test_lro.py index 8098519d9..3ed859368 100644 --- a/tests/system/test_lro.py +++ b/tests/system/test_lro.py @@ -39,6 +39,12 @@ def test_lro(echo): @pytest.mark.asyncio async def test_lro_async(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest LRO. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + await async_echo.wait() + return + future = await async_echo.wait({ 'end_time': datetime.now(tz=timezone.utc) + timedelta(seconds=1), 'success': { diff --git a/tests/system/test_pagination.py b/tests/system/test_pagination.py index fbf1a243d..96d1312b6 100644 --- a/tests/system/test_pagination.py +++ b/tests/system/test_pagination.py @@ -51,6 +51,12 @@ def test_pagination_pages(echo): if os.environ.get("GAPIC_PYTHON_ASYNC", "true") == "true": @pytest.mark.asyncio async def test_pagination_async(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest pagers. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + await async_echo.paged_expand() + return + text = 'The hail in Wales falls mainly on the snails.' results = [] async for i in await async_echo.paged_expand({ @@ -65,6 +71,12 @@ async def test_pagination_async(async_echo): @pytest.mark.asyncio async def test_pagination_pages_async(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest pagers. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + await async_echo.paged_expand() + return + text = "The hail in Wales falls mainly on the snails." page_results = [] async for page in (await async_echo.paged_expand({ diff --git a/tests/system/test_resource_crud.py b/tests/system/test_resource_crud.py index b3e704d60..a72aa3caf 100644 --- a/tests/system/test_resource_crud.py +++ b/tests/system/test_resource_crud.py @@ -81,6 +81,12 @@ def test_path_parsing(messaging): @pytest.mark.asyncio async def test_crud_with_request_async(async_identity): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest pagers. + if "rest" in str(async_identity.transport).lower(): + with pytest.raises(NotImplementedError): + await async_identity.list_users() + return + pager = await async_identity.list_users() count = len(pager.users) user = await async_identity.create_user(request={'user': { @@ -100,6 +106,12 @@ async def test_crud_with_request_async(async_identity): @pytest.mark.asyncio async def test_crud_flattened_async(async_identity): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest pagers. + if "rest" in str(async_identity.transport).lower(): + with pytest.raises(NotImplementedError): + await async_identity.list_users() + return + count = len((await async_identity.list_users()).users) user = await async_identity.create_user( display_name='Monty Python', diff --git a/tests/system/test_retry.py b/tests/system/test_retry.py index 6ec707cd5..10e5dea18 100644 --- a/tests/system/test_retry.py +++ b/tests/system/test_retry.py @@ -59,7 +59,8 @@ async def test_retry_bubble_async(async_echo): @pytest.mark.asyncio async def test_method_async_wrapper_for_async_client(async_echo): - with pytest.raises(exceptions.NotFound): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for retrying LRO. + with pytest.raises((exceptions.NotFound, NotImplementedError)): await async_echo.get_operation({ 'name': "operations/echo" }) diff --git a/tests/system/test_streams.py b/tests/system/test_streams.py index aa8c84c84..2c338333d 100644 --- a/tests/system/test_streams.py +++ b/tests/system/test_streams.py @@ -114,6 +114,12 @@ def test_stream_stream_passing_dict(echo): @pytest.mark.asyncio async def test_async_unary_stream_reader(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest server-streaming. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + call = await async_echo.expand() + return + content = 'The hail in Wales falls mainly on the snails.' call = await async_echo.expand({ 'content': content, @@ -131,6 +137,12 @@ async def test_async_unary_stream_reader(async_echo): @pytest.mark.asyncio async def test_async_unary_stream_async_generator(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest server-streaming. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + call = await async_echo.expand() + return + content = 'The hail in Wales falls mainly on the snails.' call = await async_echo.expand({ 'content': content, @@ -149,6 +161,12 @@ async def test_async_unary_stream_async_generator(async_echo): @pytest.mark.asyncio async def test_async_stream_unary_iterable(async_echo): + # NOTE: There are currently no plans for supporting async rest client-streaming. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + call = await async_echo.collect() + return + requests = [] requests.append(showcase.EchoRequest(content="hello")) requests.append(showcase.EchoRequest(content="world!")) @@ -159,6 +177,11 @@ async def test_async_stream_unary_iterable(async_echo): @pytest.mark.asyncio async def test_async_stream_unary_async_generator(async_echo): + # NOTE: There are currently no plans for supporting async rest client-streaming. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + call = await async_echo.collect() + return async def async_generator(): yield showcase.EchoRequest(content="hello") @@ -170,6 +193,11 @@ async def async_generator(): @pytest.mark.asyncio async def test_async_stream_unary_writer(async_echo): + # NOTE: There are currently no plans for supporting async rest client-streaming. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + call = await async_echo.collect() + return call = await async_echo.collect() await call.write(showcase.EchoRequest(content="hello")) await call.write(showcase.EchoRequest(content="world!")) @@ -180,6 +208,12 @@ async def test_async_stream_unary_writer(async_echo): @pytest.mark.asyncio async def test_async_stream_unary_passing_dict(async_echo): + # NOTE: There are currently no plans for supporting async rest client-streaming. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + call = await async_echo.collect() + return + requests = [{'content': 'hello'}, {'content': 'world!'}] call = await async_echo.collect(iter(requests)) response = await call @@ -187,6 +221,12 @@ async def test_async_stream_unary_passing_dict(async_echo): @pytest.mark.asyncio async def test_async_stream_stream_reader_writier(async_echo): + # NOTE: There are currently no plans for supporting async rest client-streaming. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + call = await async_echo.chat(metadata=_METADATA) + return + call = await async_echo.chat(metadata=_METADATA) await call.write(showcase.EchoRequest(content="hello")) await call.write(showcase.EchoRequest(content="world!")) @@ -203,6 +243,11 @@ async def test_async_stream_stream_reader_writier(async_echo): @pytest.mark.asyncio async def test_async_stream_stream_async_generator(async_echo): + # NOTE: There are currently no plans for supporting async rest client-streaming. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + call = await async_echo.chat(metadata=_METADATA) + return async def async_generator(): yield showcase.EchoRequest(content="hello") @@ -220,6 +265,12 @@ async def async_generator(): @pytest.mark.asyncio async def test_async_stream_stream_passing_dict(async_echo): + # NOTE: There are currently no plans for supporting async rest client-streaming. + if "rest" in str(async_echo.transport).lower(): + with pytest.raises(NotImplementedError): + call = await async_echo.chat(metadata=_METADATA) + return + requests = [{'content': 'hello'}, {'content': 'world!'}] call = await async_echo.chat(iter(requests), metadata=_METADATA) diff --git a/tests/system/test_unary.py b/tests/system/test_unary.py index 59f0ad1c5..eaf491247 100644 --- a/tests/system/test_unary.py +++ b/tests/system/test_unary.py @@ -148,13 +148,18 @@ async def test_async_unary_with_dict(async_echo): @pytest.mark.asyncio async def test_async_unary_error(async_echo): message = "Bad things! Bad things!" - with pytest.raises(exceptions.InvalidArgument) as exc: + http_message = f"POST http://localhost:7469/v1beta1/echo:echo: {message}" + # Note: InvalidArgument is from gRPC, BadRequest from http (no MTLS) + with pytest.raises((exceptions.InvalidArgument, exceptions.BadRequest)) as exc: await async_echo.echo( { "error": { - "code": code_pb2.Code.Value("INVALID_ARGUMENT"), + "code": code_pb2.Code.Value("INVALID_ARGUMENT",), "message": message, }, } ) - assert exc.value.message == message + err_message = message if "grpc_asyncio" in str( + async_echo.transport) else http_message + assert exc.value.code == 400 + assert exc.value.message == err_message From 25e16e9c29e23c48be316eeb035d77f27c916f68 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Tue, 17 Sep 2024 04:22:52 +0000 Subject: [PATCH 2/4] update workflow --- .github/workflows/tests.yaml | 3 ++- .../%service/transports/__init__.py.j2 | 20 +++++++++++++-- .../cloud_redis/transports/__init__.py | 14 +++++++++-- tests/system/conftest.py | 25 +++++++++++++------ 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 4c00a2bb3..2e433a8e3 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -55,8 +55,9 @@ jobs: strategy: # Run showcase tests on the lowest and highest supported runtimes matrix: + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2121) Remove `showcase_w_rest_async` target when async rest is GA. python: ["3.7", "3.12"] - target: [showcase, showcase_alternative_templates] + target: [showcase, showcase_w_rest_async] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/__init__.py.j2 b/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/__init__.py.j2 index 66be2e5c2..48232c710 100644 --- a/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/__init__.py.j2 +++ b/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/__init__.py.j2 @@ -1,9 +1,11 @@ +{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove the following variable (and the condition later in this file) for async rest transport once support for it is GA. #} +{% set rest_async_io_enabled = api.all_library_settings[api.naming.proto_package].python_settings.experimental_features.rest_async_io_enabled %} {% extends '_base.py.j2' %} {% block content %} from collections import OrderedDict -from typing import Dict, Type +from typing import Dict, Type{% if rest_async_io_enabled %}, Tuple{% endif +%} from .base import {{ service.name }}Transport {% if 'grpc' in opts.transport %} @@ -13,6 +15,16 @@ from .grpc_asyncio import {{ service.name }}GrpcAsyncIOTransport {% if 'rest' in opts.transport %} from .rest import {{ service.name }}RestTransport from .rest import {{ service.name }}RestInterceptor +{% if rest_async_io_enabled %} +ASYNC_REST_CLASSES: Tuple[str, ...] +try: + from .rest_asyncio import Async{{ service.name }}RestTransport + ASYNC_REST_CLASSES = ('Async{{ service.name }}RestTransport',) + HAS_REST_ASYNC = True +except ImportError: # pragma: NO COVER + ASYNC_REST_CLASSES = () + HAS_REST_ASYNC = False +{% endif %}{# if rest_async_io_enabled #} {% endif %} @@ -25,6 +37,10 @@ _transport_registry['grpc_asyncio'] = {{ service.name }}GrpcAsyncIOTransport {% endif %} {% if 'rest' in opts.transport %} _transport_registry['rest'] = {{ service.name }}RestTransport +{% if rest_async_io_enabled %} +if HAS_REST_ASYNC: # pragma: NO COVER + _transport_registry['rest_asyncio'] = Async{{ service.name }}RestTransport +{% endif %}{# if rest_async_io_enabled #} {% endif %} __all__ = ( @@ -37,5 +53,5 @@ __all__ = ( '{{ service.name }}RestTransport', '{{ service.name }}RestInterceptor', {% endif %} -) +){% if 'rest' in opts.transport and rest_async_io_enabled%} + ASYNC_REST_CLASSES{%endif%} {% endblock %} diff --git a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/__init__.py b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/__init__.py index 889648d30..7bd88e6c6 100755 --- a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/__init__.py +++ b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/__init__.py @@ -14,13 +14,21 @@ # limitations under the License. # from collections import OrderedDict -from typing import Dict, Type +from typing import Dict, Type, Tuple from .base import CloudRedisTransport from .grpc import CloudRedisGrpcTransport from .grpc_asyncio import CloudRedisGrpcAsyncIOTransport from .rest import CloudRedisRestTransport from .rest import CloudRedisRestInterceptor +ASYNC_REST_CLASSES: Tuple[str, ...] +try: + from .rest_asyncio import AsyncCloudRedisRestTransport + ASYNC_REST_CLASSES = ('AsyncCloudRedisRestTransport',) + HAS_REST_ASYNC = True +except ImportError: # pragma: NO COVER + ASYNC_REST_CLASSES = () + HAS_REST_ASYNC = False # Compile a registry of transports. @@ -28,6 +36,8 @@ _transport_registry['grpc'] = CloudRedisGrpcTransport _transport_registry['grpc_asyncio'] = CloudRedisGrpcAsyncIOTransport _transport_registry['rest'] = CloudRedisRestTransport +if HAS_REST_ASYNC: # pragma: NO COVER + _transport_registry['rest_asyncio'] = AsyncCloudRedisRestTransport __all__ = ( 'CloudRedisTransport', @@ -35,4 +45,4 @@ 'CloudRedisGrpcAsyncIOTransport', 'CloudRedisRestTransport', 'CloudRedisRestInterceptor', -) +) + ASYNC_REST_CLASSES diff --git a/tests/system/conftest.py b/tests/system/conftest.py index 6507078b6..9b454cfd7 100644 --- a/tests/system/conftest.py +++ b/tests/system/conftest.py @@ -38,10 +38,15 @@ from google.showcase import EchoAsyncClient from google.showcase import IdentityAsyncClient try: - import google.showcase.transports.rest_asyncio - SHOWCASE_ASYNC_TRANSPORTS = ["grpc_asyncio", "rest_asyncio"] + from google.showcase_v1beta1.services.echo.transports import AsyncEchoRestTransport + HAS_ASYNC_REST_ECHO_TRANSPORT = True except: - SHOWCASE_ASYNC_TRANSPORTS = ["grpc_asyncio"] + HAS_ASYNC_REST_ECHO_TRANSPORT = False + try: + from google.showcase_v1beta1.services.identity.transports import AsyncIdentityRestTransport + HAS_ASYNC_REST_IDENTITY_TRANSPORT = True + except: + HAS_ASYNC_REST_IDENTITY_TRANSPORT = False # TODO: use async auth anon credentials by default once the minimum version of google-auth is upgraded. # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2107. @@ -62,22 +67,28 @@ def async_anonymous_credentials(): def event_loop(): return asyncio.get_event_loop() - @pytest.fixture(params=SHOWCASE_ASYNC_TRANSPORTS) + @pytest.fixture(params=["grpc_asyncio", "rest_asyncio"]) def async_echo(use_mtls, request, event_loop): + transport = request.param + if transport == "rest_asyncio" and not HAS_ASYNC_REST_ECHO_TRANSPORT: + pytest.skip("Skipping test with async rest.") return construct_client( EchoAsyncClient, use_mtls, - transport_name=request.param, + transport_name=transport, channel_creator=aio.insecure_channel if request.param == "grpc_asyncio" else None, credentials=async_anonymous_credentials(), ) - @pytest.fixture(params=SHOWCASE_ASYNC_TRANSPORTS) + @pytest.fixture(params=["grpc_asyncio", "rest_asyncio"]) def async_identity(use_mtls, request, event_loop): + transport = request.param + if transport == "rest_asyncio" and not HAS_ASYNC_REST_IDENTITY_TRANSPORT: + pytest.skip("Skipping test with async rest.") return construct_client( IdentityAsyncClient, use_mtls, - transport_name=request.param, + transport_name=transport, channel_creator=aio.insecure_channel if request.param == "grpc_asyncio" else None, credentials=async_anonymous_credentials(), ) From 7043f260cb506c3802534497d4bd67154b9c3f5c Mon Sep 17 00:00:00 2001 From: ohmayr Date: Tue, 17 Sep 2024 18:33:25 +0000 Subject: [PATCH 3/4] address PR comments --- .github/workflows/tests.yaml | 2 +- .../%sub/services/%service/_shared_macros.j2 | 1 + .../%sub/services/%service/client.py.j2 | 14 ++++++- .../%service/transports/rest_asyncio.py.j2 | 7 ++++ .../%name_%version/%sub/test_%service.py.j2 | 9 ++++ .../gapic/%name_%version/%sub/test_macros.j2 | 15 ++++++- .../redis_v1/services/cloud_redis/client.py | 10 ++++- .../cloud_redis/transports/rest_asyncio.py | 5 +++ .../unit/gapic/redis_v1/test_cloud_redis.py | 41 ++++++++++++++++++- tests/system/conftest.py | 1 + tests/system/test_lro.py | 2 +- tests/system/test_pagination.py | 4 +- tests/system/test_resource_crud.py | 4 +- tests/system/test_retry.py | 2 +- tests/system/test_streams.py | 11 ++++- tests/system/test_unary.py | 7 ++-- 16 files changed, 117 insertions(+), 18 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2e433a8e3..5eaf75d2a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -57,7 +57,7 @@ jobs: matrix: # TODO(https://github.com/googleapis/gapic-generator-python/issues/2121) Remove `showcase_w_rest_async` target when async rest is GA. python: ["3.7", "3.12"] - target: [showcase, showcase_w_rest_async] + target: [showcase, showcase_alternative_templates, showcase_w_rest_async] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/_shared_macros.j2 b/gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/_shared_macros.j2 index e7f623cfd..7cf28e9f9 100644 --- a/gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/_shared_macros.j2 +++ b/gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/_shared_macros.j2 @@ -57,6 +57,7 @@ The `try/except` below can be removed once the minimum version of try: from google.api_core import version_header HAS_GOOGLE_API_CORE_VERSION_HEADER = True # pragma: NO COVER +{# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. #} except ImportError: # pragma: NO COVER HAS_GOOGLE_API_CORE_VERSION_HEADER = False {% endif %}{# service_version #} diff --git a/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 b/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 index 76b88f968..467ed1608 100644 --- a/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 +++ b/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 @@ -69,10 +69,18 @@ from .transports.rest import {{ service.name }}RestTransport try: from .transports.rest_asyncio import Async{{ service.name }}RestTransport HAS_GOOGLE_AUTH_AIO = True +{# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. #} except ImportError as e: # pragma: NO COVER HAS_GOOGLE_AUTH_AIO = False GOOGLE_AUTH_AIO_EXCEPTION = e +try: + import aiohttp # type: ignore + HAS_AIOHTTP_INSTALLED = True +{# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. #} +except ImportError as e: # pragma: NO COVER + HAS_AIOHTTP_INSTALLED = False + {% endif %}{# if rest_async_io_enabled #} {% endif %} @@ -93,7 +101,7 @@ class {{ service.client_name }}Meta(type): _transport_registry["rest"] = {{ service.name }}RestTransport {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove this condition when async rest is GA. #} {% if rest_async_io_enabled %} - if HAS_GOOGLE_AUTH_AIO: # pragma: NO COVER + if HAS_GOOGLE_AUTH_AIO and HAS_AIOHTTP_INSTALLED: # pragma: NO COVER _transport_registry["rest_asyncio"] = Async{{ service.name }}RestTransport {% endif %}{# if rest_async_io_enabled #} {% endif %} @@ -113,8 +121,12 @@ class {{ service.client_name }}Meta(type): # If a specific transport is requested, return that one. {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove this condition when async rest is GA. #} {% if rest_async_io_enabled %} + {# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. #} if label == "rest_asyncio" and not HAS_GOOGLE_AUTH_AIO: # pragma: NO COVER raise GOOGLE_AUTH_AIO_EXCEPTION + {# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. #} + elif label == "rest_asyncio" and not HAS_AIOHTTP_INSTALLED: # pragma: NO COVER + raise ImportError("async rest transport requires aiohttp external package.") {% endif %} if label: return cls._transport_registry[label] diff --git a/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 b/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 index 2cc6e88ab..babada054 100644 --- a/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 +++ b/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 @@ -5,9 +5,16 @@ {% block content %} +try: + import aiohttp # type: ignore +{# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. #} +except ImportError as e: # pragma: NO COVER + raise ImportError("async rest transport requires aiohttp external package.") from e + import google.auth try: from google.auth.aio.transport.sessions import AsyncAuthorizedSession # type: ignore +{# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. #} except ImportError as e: # pragma: NO COVER {# TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. #} raise ImportError("async rest transport requires google.auth >= 2.x.x") from e diff --git a/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 b/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 index 1d0c0908f..5069b230d 100644 --- a/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 +++ b/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 @@ -30,6 +30,14 @@ from google.api_core import api_core_version from proto.marshal.rules.dates import DurationRule, TimestampRule from proto.marshal.rules import wrappers {% if 'rest' in opts.transport %} +{% if rest_async_io_enabled %} +try: + import aiohttp # type: ignore + HAS_AIOHTTP_INSTALLED = True +{# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. #} +except ImportError: # pragma: NO COVER + HAS_AIOHTTP_INSTALLED = False +{% endif %}{# if rest_async_io_enabled #} from requests import Response from requests import Request, PreparedRequest from requests.sessions import Session @@ -43,6 +51,7 @@ try: from google.auth.aio.transport.sessions import AsyncAuthorizedSession {% endif %} HAS_GOOGLE_AUTH_AIO = True +{# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. #} except ImportError: # pragma: NO COVER HAS_GOOGLE_AUTH_AIO = False diff --git a/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_macros.j2 b/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_macros.j2 index 3f471b572..77389dfe8 100644 --- a/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_macros.j2 +++ b/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_macros.j2 @@ -1861,6 +1861,8 @@ def test_transport_kind_{{ transport_name }}(): if not HAS_GOOGLE_AUTH_AIO: {# TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. #} pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") {% endif %} transport = {{ get_client(service, is_async) }}.get_transport_class("{{ transport_name }}")( credentials={{get_credentials(is_async)}} @@ -1884,6 +1886,8 @@ def test_transport_kind_{{ transport_name }}(): if not HAS_GOOGLE_AUTH_AIO: {# TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. #} pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") {% endif %} client = {{ get_client(service, is_async) }}( credentials={{get_credentials(is_async)}}, @@ -1902,6 +1906,8 @@ def test_unsupported_parameter_rest_asyncio(): if not HAS_GOOGLE_AUTH_AIO: {# TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. #} pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") options = client_options.ClientOptions(quota_project_id="octopus") {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2137): Remove `type: ignore` once we add a version check for google-api-core. #} with pytest.raises(core_exceptions.AsyncRestUnsupportedParameterError, match="google.api_core.client_options.ClientOptions.quota_project_id") as exc: # type: ignore @@ -1987,6 +1993,8 @@ def test_unsupported_parameter_rest_asyncio(): if not HAS_GOOGLE_AUTH_AIO: {# TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. #} pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") {% endif %} client = {{ get_client(service, is_async) }}( @@ -2015,6 +2023,8 @@ def test_initialize_client_w_{{transport_name}}(): if not HAS_GOOGLE_AUTH_AIO: {# TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. #} pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") {% endif %} client = {{ get_client(service, is_async) }}( credentials={{get_credentials(is_async)}}, @@ -2044,7 +2054,8 @@ def test_initialize_client_w_{{transport_name}}(): if not HAS_GOOGLE_AUTH_AIO: {# TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. #} pytest.skip("google-auth > 2.x.x is required for async rest transport.") - + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") {% endif %} client = {{ get_client(service, is_async) }}( credentials={{get_credentials(is_async)}}, @@ -2110,6 +2121,8 @@ def test_initialize_client_w_{{transport_name}}(): if not HAS_GOOGLE_AUTH_AIO: {# TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. #} pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") {% endif %} client = {{ get_client(service, is_async) }}( diff --git a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py index cfaf4de55..037eec4bb 100755 --- a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py +++ b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py @@ -56,6 +56,12 @@ HAS_GOOGLE_AUTH_AIO = False GOOGLE_AUTH_AIO_EXCEPTION = e +try: + import aiohttp # type: ignore + HAS_AIOHTTP_INSTALLED = True +except ImportError as e: # pragma: NO COVER + HAS_AIOHTTP_INSTALLED = False + class CloudRedisClientMeta(type): """Metaclass for the CloudRedis client. @@ -68,7 +74,7 @@ class CloudRedisClientMeta(type): _transport_registry["grpc"] = CloudRedisGrpcTransport _transport_registry["grpc_asyncio"] = CloudRedisGrpcAsyncIOTransport _transport_registry["rest"] = CloudRedisRestTransport - if HAS_GOOGLE_AUTH_AIO: # pragma: NO COVER + if HAS_GOOGLE_AUTH_AIO and HAS_AIOHTTP_INSTALLED: # pragma: NO COVER _transport_registry["rest_asyncio"] = AsyncCloudRedisRestTransport def get_transport_class(cls, @@ -86,6 +92,8 @@ def get_transport_class(cls, # If a specific transport is requested, return that one. if label == "rest_asyncio" and not HAS_GOOGLE_AUTH_AIO: # pragma: NO COVER raise GOOGLE_AUTH_AIO_EXCEPTION + elif label == "rest_asyncio" and not HAS_AIOHTTP_INSTALLED: + raise ImportError("async rest transport requires aiohttp external package.") if label: return cls._transport_registry[label] diff --git a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py index a21ff26d8..8eee1ebba 100755 --- a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py +++ b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py @@ -13,6 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # +try: + import aiohttp # type: ignore +except ImportError as e: # pragma: NO COVER + raise ImportError("async rest transport requires aiohttp external package.") from e + import google.auth try: from google.auth.aio.transport.sessions import AsyncAuthorizedSession # type: ignore diff --git a/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py b/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py index 609bd19a1..2276304e1 100755 --- a/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py +++ b/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py @@ -31,6 +31,11 @@ from google.api_core import api_core_version from proto.marshal.rules.dates import DurationRule, TimestampRule from proto.marshal.rules import wrappers +try: + import aiohttp # type: ignore + HAS_AIOHTTP_INSTALLED = True +except ImportError: # pragma: NO COVER + HAS_AIOHTTP_INSTALLED = False from requests import Response from requests import Request, PreparedRequest from requests.sessions import Session @@ -8396,6 +8401,8 @@ def test_initialize_client_w_rest(): def test_transport_kind_rest_asyncio(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") transport = CloudRedisAsyncClient.get_transport_class("rest_asyncio")( credentials=async_anonymous_credentials() ) @@ -8406,6 +8413,8 @@ def test_transport_kind_rest_asyncio(): async def test_list_instances_rest_asyncio_error(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8424,7 +8433,8 @@ async def test_list_instances_rest_asyncio_error(): async def test_get_instance_rest_asyncio_bad_request(request_type=cloud_redis.GetInstanceRequest): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") - + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), transport="rest_asyncio" @@ -8452,6 +8462,8 @@ async def test_get_instance_rest_asyncio_bad_request(request_type=cloud_redis.Ge async def test_get_instance_rest_asyncio_call_success(request_type): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8541,7 +8553,8 @@ async def test_get_instance_rest_asyncio_call_success(request_type): async def test_get_instance_auth_string_rest_asyncio_bad_request(request_type=cloud_redis.GetInstanceAuthStringRequest): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") - + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), transport="rest_asyncio" @@ -8569,6 +8582,8 @@ async def test_get_instance_auth_string_rest_asyncio_bad_request(request_type=cl async def test_get_instance_auth_string_rest_asyncio_call_success(request_type): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8606,6 +8621,8 @@ async def test_get_instance_auth_string_rest_asyncio_call_success(request_type): async def test_create_instance_rest_asyncio_error(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8624,6 +8641,8 @@ async def test_create_instance_rest_asyncio_error(): async def test_update_instance_rest_asyncio_error(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8642,6 +8661,8 @@ async def test_update_instance_rest_asyncio_error(): async def test_upgrade_instance_rest_asyncio_error(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8660,6 +8681,8 @@ async def test_upgrade_instance_rest_asyncio_error(): async def test_import_instance_rest_asyncio_error(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8678,6 +8701,8 @@ async def test_import_instance_rest_asyncio_error(): async def test_export_instance_rest_asyncio_error(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8696,6 +8721,8 @@ async def test_export_instance_rest_asyncio_error(): async def test_failover_instance_rest_asyncio_error(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8714,6 +8741,8 @@ async def test_failover_instance_rest_asyncio_error(): async def test_delete_instance_rest_asyncio_error(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8732,6 +8761,8 @@ async def test_delete_instance_rest_asyncio_error(): async def test_reschedule_maintenance_rest_asyncio_error(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), @@ -8749,6 +8780,8 @@ async def test_reschedule_maintenance_rest_asyncio_error(): def test_initialize_client_w_rest_asyncio(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), transport="rest_asyncio" @@ -8759,6 +8792,8 @@ def test_initialize_client_w_rest_asyncio(): def test_unsupported_parameter_rest_asyncio(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") options = client_options.ClientOptions(quota_project_id="octopus") with pytest.raises(core_exceptions.AsyncRestUnsupportedParameterError, match="google.api_core.client_options.ClientOptions.quota_project_id") as exc: # type: ignore client = CloudRedisAsyncClient( @@ -10474,6 +10509,8 @@ def test_transport_close_rest(): async def test_transport_close_rest_asyncio(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") client = CloudRedisAsyncClient( credentials=async_anonymous_credentials(), transport="rest_asyncio" diff --git a/tests/system/conftest.py b/tests/system/conftest.py index 9b454cfd7..395467661 100644 --- a/tests/system/conftest.py +++ b/tests/system/conftest.py @@ -24,6 +24,7 @@ from google.auth.aio import credentials as ga_credentials_async HAS_GOOGLE_AUTH_AIO = True +# NOTE: `pragma: NO COVER` is needed since the coverage for presubmits isn't combined. except ImportError: # pragma: NO COVER HAS_GOOGLE_AUTH_AIO = False import google.auth diff --git a/tests/system/test_lro.py b/tests/system/test_lro.py index 3ed859368..a30e738fe 100644 --- a/tests/system/test_lro.py +++ b/tests/system/test_lro.py @@ -39,7 +39,7 @@ def test_lro(echo): @pytest.mark.asyncio async def test_lro_async(async_echo): - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest LRO. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2170): Add test for async rest LRO. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): await async_echo.wait() diff --git a/tests/system/test_pagination.py b/tests/system/test_pagination.py index 96d1312b6..8c560e36e 100644 --- a/tests/system/test_pagination.py +++ b/tests/system/test_pagination.py @@ -51,7 +51,7 @@ def test_pagination_pages(echo): if os.environ.get("GAPIC_PYTHON_ASYNC", "true") == "true": @pytest.mark.asyncio async def test_pagination_async(async_echo): - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest pagers. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2171): Add test for async rest pagers. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): await async_echo.paged_expand() @@ -71,7 +71,7 @@ async def test_pagination_async(async_echo): @pytest.mark.asyncio async def test_pagination_pages_async(async_echo): - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest pagers. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2171): Add test for async rest pagers. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): await async_echo.paged_expand() diff --git a/tests/system/test_resource_crud.py b/tests/system/test_resource_crud.py index a72aa3caf..d6d3a4067 100644 --- a/tests/system/test_resource_crud.py +++ b/tests/system/test_resource_crud.py @@ -81,7 +81,7 @@ def test_path_parsing(messaging): @pytest.mark.asyncio async def test_crud_with_request_async(async_identity): - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest pagers. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2171): Add test for async rest pagers. if "rest" in str(async_identity.transport).lower(): with pytest.raises(NotImplementedError): await async_identity.list_users() @@ -106,7 +106,7 @@ async def test_crud_with_request_async(async_identity): @pytest.mark.asyncio async def test_crud_flattened_async(async_identity): - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest pagers. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2171): Add test for async rest pagers. if "rest" in str(async_identity.transport).lower(): with pytest.raises(NotImplementedError): await async_identity.list_users() diff --git a/tests/system/test_retry.py b/tests/system/test_retry.py index 10e5dea18..5b06b127f 100644 --- a/tests/system/test_retry.py +++ b/tests/system/test_retry.py @@ -59,7 +59,7 @@ async def test_retry_bubble_async(async_echo): @pytest.mark.asyncio async def test_method_async_wrapper_for_async_client(async_echo): - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for retrying LRO. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2170): Add test for retrying LRO. with pytest.raises((exceptions.NotFound, NotImplementedError)): await async_echo.get_operation({ 'name': "operations/echo" diff --git a/tests/system/test_streams.py b/tests/system/test_streams.py index 2c338333d..1e89d4605 100644 --- a/tests/system/test_streams.py +++ b/tests/system/test_streams.py @@ -114,7 +114,7 @@ def test_stream_stream_passing_dict(echo): @pytest.mark.asyncio async def test_async_unary_stream_reader(async_echo): - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest server-streaming. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2168): Add test for async rest server-streaming. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): call = await async_echo.expand() @@ -137,7 +137,7 @@ async def test_async_unary_stream_reader(async_echo): @pytest.mark.asyncio async def test_async_unary_stream_async_generator(async_echo): - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2152): Add test for async rest server-streaming. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2168): Add test for async rest server-streaming. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): call = await async_echo.expand() @@ -161,6 +161,7 @@ async def test_async_unary_stream_async_generator(async_echo): @pytest.mark.asyncio async def test_async_stream_unary_iterable(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Add test for async rest client-streaming. # NOTE: There are currently no plans for supporting async rest client-streaming. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): @@ -177,6 +178,7 @@ async def test_async_stream_unary_iterable(async_echo): @pytest.mark.asyncio async def test_async_stream_unary_async_generator(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Add test for async rest client-streaming. # NOTE: There are currently no plans for supporting async rest client-streaming. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): @@ -193,6 +195,7 @@ async def async_generator(): @pytest.mark.asyncio async def test_async_stream_unary_writer(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Add test for async rest client-streaming. # NOTE: There are currently no plans for supporting async rest client-streaming. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): @@ -208,6 +211,7 @@ async def test_async_stream_unary_writer(async_echo): @pytest.mark.asyncio async def test_async_stream_unary_passing_dict(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Add test for async rest client-streaming. # NOTE: There are currently no plans for supporting async rest client-streaming. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): @@ -221,6 +225,7 @@ async def test_async_stream_unary_passing_dict(async_echo): @pytest.mark.asyncio async def test_async_stream_stream_reader_writier(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Add test for async rest client-streaming. # NOTE: There are currently no plans for supporting async rest client-streaming. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): @@ -243,6 +248,7 @@ async def test_async_stream_stream_reader_writier(async_echo): @pytest.mark.asyncio async def test_async_stream_stream_async_generator(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Add test for async rest client-streaming. # NOTE: There are currently no plans for supporting async rest client-streaming. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): @@ -265,6 +271,7 @@ async def async_generator(): @pytest.mark.asyncio async def test_async_stream_stream_passing_dict(async_echo): + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Add test for async rest client-streaming. # NOTE: There are currently no plans for supporting async rest client-streaming. if "rest" in str(async_echo.transport).lower(): with pytest.raises(NotImplementedError): diff --git a/tests/system/test_unary.py b/tests/system/test_unary.py index eaf491247..674919eb6 100644 --- a/tests/system/test_unary.py +++ b/tests/system/test_unary.py @@ -148,7 +148,8 @@ async def test_async_unary_with_dict(async_echo): @pytest.mark.asyncio async def test_async_unary_error(async_echo): message = "Bad things! Bad things!" - http_message = f"POST http://localhost:7469/v1beta1/echo:echo: {message}" + expected_err_message = message if "grpc_asyncio" in str( + async_echo.transport) else f"POST http://localhost:7469/v1beta1/echo:echo: {message}" # Note: InvalidArgument is from gRPC, BadRequest from http (no MTLS) with pytest.raises((exceptions.InvalidArgument, exceptions.BadRequest)) as exc: await async_echo.echo( @@ -159,7 +160,5 @@ async def test_async_unary_error(async_echo): }, } ) - err_message = message if "grpc_asyncio" in str( - async_echo.transport) else http_message assert exc.value.code == 400 - assert exc.value.message == err_message + assert exc.value.message == expected_err_message From 69476d67ab7b2f5505e19145c390e2bffc3356c0 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Tue, 17 Sep 2024 19:59:04 +0000 Subject: [PATCH 4/4] update goldens --- .../redis/google/cloud/redis_v1/services/cloud_redis/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py index 037eec4bb..539b30ffe 100755 --- a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py +++ b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py @@ -92,7 +92,7 @@ def get_transport_class(cls, # If a specific transport is requested, return that one. if label == "rest_asyncio" and not HAS_GOOGLE_AUTH_AIO: # pragma: NO COVER raise GOOGLE_AUTH_AIO_EXCEPTION - elif label == "rest_asyncio" and not HAS_AIOHTTP_INSTALLED: + elif label == "rest_asyncio" and not HAS_AIOHTTP_INSTALLED: # pragma: NO COVER raise ImportError("async rest transport requires aiohttp external package.") if label: return cls._transport_registry[label]