From d9f8d2bc0c4cc9cb9cf3beceb25001fd34ea635c Mon Sep 17 00:00:00 2001 From: ohmayr Date: Sat, 31 Aug 2024 21:26:36 +0000 Subject: [PATCH 1/7] chore: optionally generate async rest and client files --- gapic/generator/generator.py | 10 +++++++--- .../services/%service/transports/rest_asyncio.py.j2 | 5 +++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 diff --git a/gapic/generator/generator.py b/gapic/generator/generator.py index dff8e0ebed..cb0dff456b 100644 --- a/gapic/generator/generator.py +++ b/gapic/generator/generator.py @@ -294,9 +294,13 @@ def _render_template( ('transport' in template_name and not self._is_desired_transport(template_name, opts)) or - # TODO(yon-mg) - remove when rest async implementation resolved - # temporarily stop async client gen while rest async is unkown - ('async' in template_name and 'grpc' not in opts.transport) + # TODO: Remove the following conditions once support for async rest transport is GA: + # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. + ('async_client' in template_name and 'grpc' not in opts.transport and + not api_schema.all_library_settings[api_schema.naming.proto_package].python_settings.experimental_features.rest_async_io_enabled) + or + ('rest_asyncio' in template_name and + not api_schema.all_library_settings[api_schema.naming.proto_package].python_settings.experimental_features.rest_async_io_enabled) or ('rest_base' in template_name and 'rest' not in opts.transport) ): 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 new file mode 100644 index 0000000000..d73591c472 --- /dev/null +++ b/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 @@ -0,0 +1,5 @@ +{# TODO: Remove the following condition for async rest transport once support for it is GA: + # {% if rest_async_io_enabled %} + # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. +#} +{% set rest_async_io_enabled = api.all_library_settings[api.naming.proto_package].python_settings.experimental_features.rest_async_io_enabled %} From fcc89de77866bc7a57b4fb11f7dbe90f42dec56a Mon Sep 17 00:00:00 2001 From: ohmayr Date: Sun, 1 Sep 2024 03:01:38 +0000 Subject: [PATCH 2/7] feat: implement async rest transport constructor --- .github/workflows/tests.yaml | 2 +- .../%sub/services/%service/client.py.j2 | 11 ++ .../%service/transports/rest_asyncio.py.j2 | 78 ++++++++++++++ .../%name_%version/%sub/test_%service.py.j2 | 21 +++- .../gapic/%name_%version/%sub/test_macros.j2 | 46 ++++---- noxfile.py | 47 ++++++++ .../unit/gapic/asset_v1/test_asset_service.py | 32 +++--- .../credentials_v1/test_iam_credentials.py | 32 +++--- .../unit/gapic/eventarc_v1/test_eventarc.py | 32 +++--- .../logging_v2/test_config_service_v2.py | 24 ++--- .../logging_v2/test_logging_service_v2.py | 24 ++--- .../logging_v2/test_metrics_service_v2.py | 24 ++--- .../redis_v1/services/cloud_redis/client.py | 2 + .../cloud_redis/transports/rest_asyncio.py | 102 ++++++++++++++++++ .../unit/gapic/redis_v1/test_cloud_redis.py | 39 ++++--- tests/integration/redis_v1.yaml | 9 +- 16 files changed, 384 insertions(+), 141 deletions(-) create mode 100755 tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 8be4f041d2..2544bbfb82 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -140,7 +140,7 @@ jobs: strategy: matrix: python: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] - variant: ['', _alternative_templates, _mixins, _alternative_templates_mixins] + variant: ['', _alternative_templates, _mixins, _alternative_templates_mixins, "_w_rest_async"] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 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 6c45fdd728..d325ab6334 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 @@ -1,3 +1,8 @@ +{# TODO: Remove the following condition for async rest transport once support for it is GA: + # {% if rest_async_io_enabled %} + # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. +#} +{% 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 %} @@ -62,6 +67,9 @@ from .transports.grpc_asyncio import {{ service.grpc_asyncio_transport_name }} {% endif %} {% if 'rest' in opts.transport %} from .transports.rest import {{ service.name }}RestTransport +{% if rest_async_io_enabled %} +from .transports.rest_asyncio import Async{{ service.name }}RestTransport +{% endif %}{# if rest_async_io_enabled #} {% endif %} @@ -79,6 +87,9 @@ class {{ service.client_name }}Meta(type): {% endif %} {% if "rest" in opts.transport %} _transport_registry["rest"] = {{ service.name }}RestTransport + {% if rest_async_io_enabled %} + _transport_registry["rest_asyncio"] = Async{{ service.name }}RestTransport + {% endif %}{# if rest_async_io_enabled #} {% endif %} def get_transport_class(cls, 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 d73591c472..731b569c30 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 @@ -3,3 +3,81 @@ # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. #} {% 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 google.api_core import gapic_v1 + +from typing import Any, Optional + +from .rest_base import _Base{{ service.name }}RestTransport + +from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO + +{# TODO (ohmayr): determine the version of rest. aiohttp or google.auth.aio. #} +DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, + grpc_version=None, + rest_version=None, +) + +class Async{{service.name}}RestTransport(_Base{{ service.name }}RestTransport): + """Asynchronous REST backend transport for {{ service.name }}. + + {{ service.meta.doc|rst(width=72, indent=4) }} + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends JSON representations of protocol buffers over HTTP/1.1 + """ + def __init__(self, *, + host: str{% if service.host %} = '{{ service.host }}'{% endif %}, + {# TODO (ohmayr): Update the default type for credentials. #} + credentials: Optional[Any] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + url_scheme: str = 'https', + ) -> None: + """Instantiate the transport. + + {% if not opts.rest_numeric_enums %} + NOTE: This async REST transport functionality is currently in a beta + state (preview). We welcome your feedback via a GitHub issue in + this library's repository. Thank you! + {% endif %} + + Args: + host ({% if service.host %}Optional[str]{% else %}str{% endif %}): + {{ ' ' }}The hostname to connect to {% if service.host %}(default: '{{ service.host }}'){% endif %}. + {# TODO (ohmayr): Update the default type for credentials. #} + credentials (Optional[Any]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you are developing + your own client library. + url_scheme: the protocol scheme for the API endpoint. Normally + "https", but for testing or local servers, + "http" can be specified. + """ + # Run the base constructor + super().__init__( + host=host, + credentials=credentials, + client_info=client_info, + always_use_jwt_access=False, + url_scheme=url_scheme, + api_audience=None + ) + + @property + def kind(self) -> str: + return "rest_asyncio" + +{% endblock %} 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 8a64754a42..13488a6999 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 @@ -1,3 +1,8 @@ +{# TODO: Remove the following condition for async rest transport once support for it is GA: + # {% if rest_async_io_enabled %} + # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. +#} +{% 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 %} @@ -1041,9 +1046,21 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() -{{ test_macros.transport_kind_test(service, opts) }} +{% set configs = [] %} +{% for transport_name in opts.transport %} + {% do configs.append({'service':service, 'transport_name':transport_name}) %} +{# TODO: Remove the following guard once support for async REST is GA. + # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. +#} +{% if 'grpc' in transport_name or rest_async_io_enabled %} + {% do configs.append({'service':service, 'transport_name':transport_name + "_asyncio"}) %} +{% endif %} +{% endfor %} -{{ test_macros.transport_kind_test(service, opts, is_async=True) }} +{% for conf in configs %} +{{ test_macros.transport_kind_test(**conf) }} + +{% endfor %} {% if 'grpc' in opts.transport %} def test_transport_grpc_default(): 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 b73e8eac81..0726a9755e 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 @@ -1878,32 +1878,22 @@ def test_{{ method_name }}_empty_call(): {% endmacro %} -{% macro transport_kind_test(service, opts, is_async=False) %} -@pytest.mark.parametrize("transport_name", [ - {% if is_async %} - {% if "grpc" in opts.transport %} - "grpc_asyncio", - {% endif %} - {% else %}{# if not is_async #} - {% if "grpc" in opts.transport%} - "grpc", - {% endif %} - {% if "rest" in opts.transport %} - "rest", - {% endif %} - {% endif %}{# is_async #} -]) -{% if is_async %} -@pytest.mark.asyncio -async def test_transport_kind_async(transport_name): - transport = {{ service.async_client_name }}.get_transport_class(transport_name)( - credentials=async_anonymous_credentials(), - ) -{% else %} -def test_transport_kind(transport_name): - transport = {{ service.client_name }}.get_transport_class(transport_name)( - credentials=ga_credentials.AnonymousCredentials(), +{% macro transport_kind_test(service, transport_name) %} +{%- set named_clients = { + 'rest': service.client_name, + 'grpc': service.client_name, + 'grpc_asyncio': service.async_client_name, + 'rest_asyncio': service.async_client_name} +-%} +{%- set named_credentials = { + 'rest': 'ga_credentials.AnonymousCredentials()', + 'grpc': 'ga_credentials.AnonymousCredentials()', + 'grpc_asyncio': 'async_anonymous_credentials()', + 'rest_asyncio': 'async_anonymous_credentials()'} +-%} +def test_transport_kind_{{transport_name}}(): + transport = {{ named_clients[transport_name] }}.get_transport_class("{{transport_name}}")( + credentials={{named_credentials[transport_name]}} ) -{% endif %} - assert transport.kind == transport_name -{% endmacro %} \ No newline at end of file + assert transport.kind == "{{ transport_name }}" +{% endmacro %} diff --git a/noxfile.py b/noxfile.py index 3ab6c7b392..7382249ff2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -175,11 +175,25 @@ def fragment_alternative_templates(session): fragment(session, use_ads_templates=True) +def _add_python_settings(tmp_dir, python_settings): + return f""" +import yaml +from pathlib import Path +temp_file_path = Path(f"{tmp_dir}/showcase_v1beta1.yaml") +with temp_file_path.open('r') as file: + data = yaml.safe_load(file) + data['publishing']['library_settings'] = {python_settings} + +with temp_file_path.open('w') as file: + yaml.safe_dump(data, file, default_flow_style=False, sort_keys=False) +""" + @contextmanager def showcase_library( session, templates="DEFAULT", other_opts: typing.Iterable[str] = (), include_service_yaml=True, retry_config=True, + rest_async_io_enabled=False ): """Install the generated library into the session for showcase tests.""" @@ -220,6 +234,26 @@ def showcase_library( external=True, silent=True, ) + # TODO: The below section updates the showcase service yaml + # to test experimental async rest transport. It must be + # removed once support for async rest is GA. + # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. + if rest_async_io_enabled: + # Install pyYAML for yaml + session.install("pyYAML") + + python_settings = [ + { + 'version': 'google.showcase.v1beta1', + 'python_settings': { + 'experimental_features': { + 'rest_async_io_enabled': True + } + } + } + ] + update_service_yaml = _add_python_settings(tmp_dir, python_settings) + session.run("python", "-c" f"{update_service_yaml}") if retry_config: session.run( "curl", @@ -392,6 +426,19 @@ def showcase_unit( run_showcase_unit_tests(session) +# TODO: `showcase_unit_w_rest_async` nox session runs showcase unit tests with the +# experimental async rest transport and must be removed once support for async rest is GA. +# See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. +@nox.session(python=ALL_PYTHON) +def showcase_unit_w_rest_async( + session, templates="DEFAULT", other_opts: typing.Iterable[str] = (), +): + """Run the generated unit tests with async rest transport against the Showcase library.""" + with showcase_library(session, templates=templates, other_opts=other_opts, rest_async_io_enabled=True) as lib: + session.chdir(lib) + run_showcase_unit_tests(session) + + @nox.session(python=ALL_PYTHON) def showcase_unit_alternative_templates(session): with showcase_library( diff --git a/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py b/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py index f952581a0e..bef9509470 100755 --- a/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py +++ b/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py @@ -16405,26 +16405,26 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() -@pytest.mark.parametrize("transport_name", [ - "grpc", - "rest", -]) -def test_transport_kind(transport_name): - transport = AssetServiceClient.get_transport_class(transport_name)( - credentials=ga_credentials.AnonymousCredentials(), + +def test_transport_kind_grpc(): + transport = AssetServiceClient.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc" -@pytest.mark.parametrize("transport_name", [ - "grpc_asyncio", -]) -@pytest.mark.asyncio -async def test_transport_kind_async(transport_name): - transport = AssetServiceAsyncClient.get_transport_class(transport_name)( - credentials=async_anonymous_credentials(), +def test_transport_kind_grpc_asyncio(): + transport = AssetServiceAsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() + ) + assert transport.kind == "grpc_asyncio" + + +def test_transport_kind_rest(): + transport = AssetServiceClient.get_transport_class("rest")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "rest" def test_transport_grpc_default(): diff --git a/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py b/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py index 2e96e2aebf..9f573cca17 100755 --- a/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py +++ b/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py @@ -3490,26 +3490,26 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() -@pytest.mark.parametrize("transport_name", [ - "grpc", - "rest", -]) -def test_transport_kind(transport_name): - transport = IAMCredentialsClient.get_transport_class(transport_name)( - credentials=ga_credentials.AnonymousCredentials(), + +def test_transport_kind_grpc(): + transport = IAMCredentialsClient.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc" -@pytest.mark.parametrize("transport_name", [ - "grpc_asyncio", -]) -@pytest.mark.asyncio -async def test_transport_kind_async(transport_name): - transport = IAMCredentialsAsyncClient.get_transport_class(transport_name)( - credentials=async_anonymous_credentials(), +def test_transport_kind_grpc_asyncio(): + transport = IAMCredentialsAsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() + ) + assert transport.kind == "grpc_asyncio" + + +def test_transport_kind_rest(): + transport = IAMCredentialsClient.get_transport_class("rest")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "rest" def test_transport_grpc_default(): diff --git a/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py b/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py index 225cf30b7a..8816a70cd6 100755 --- a/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py +++ b/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py @@ -13837,26 +13837,26 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() -@pytest.mark.parametrize("transport_name", [ - "grpc", - "rest", -]) -def test_transport_kind(transport_name): - transport = EventarcClient.get_transport_class(transport_name)( - credentials=ga_credentials.AnonymousCredentials(), + +def test_transport_kind_grpc(): + transport = EventarcClient.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc" -@pytest.mark.parametrize("transport_name", [ - "grpc_asyncio", -]) -@pytest.mark.asyncio -async def test_transport_kind_async(transport_name): - transport = EventarcAsyncClient.get_transport_class(transport_name)( - credentials=async_anonymous_credentials(), +def test_transport_kind_grpc_asyncio(): + transport = EventarcAsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() + ) + assert transport.kind == "grpc_asyncio" + + +def test_transport_kind_rest(): + transport = EventarcClient.get_transport_class("rest")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "rest" def test_transport_grpc_default(): diff --git a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py index 86f42cc044..8b87aca12b 100755 --- a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py +++ b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py @@ -12335,25 +12335,19 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() -@pytest.mark.parametrize("transport_name", [ - "grpc", -]) -def test_transport_kind(transport_name): - transport = ConfigServiceV2Client.get_transport_class(transport_name)( - credentials=ga_credentials.AnonymousCredentials(), + +def test_transport_kind_grpc(): + transport = ConfigServiceV2Client.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc" -@pytest.mark.parametrize("transport_name", [ - "grpc_asyncio", -]) -@pytest.mark.asyncio -async def test_transport_kind_async(transport_name): - transport = ConfigServiceV2AsyncClient.get_transport_class(transport_name)( - credentials=async_anonymous_credentials(), +def test_transport_kind_grpc_asyncio(): + transport = ConfigServiceV2AsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc_asyncio" def test_transport_grpc_default(): diff --git a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py index 7602beb75d..bd2ce61344 100755 --- a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py +++ b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py @@ -3099,25 +3099,19 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() -@pytest.mark.parametrize("transport_name", [ - "grpc", -]) -def test_transport_kind(transport_name): - transport = LoggingServiceV2Client.get_transport_class(transport_name)( - credentials=ga_credentials.AnonymousCredentials(), + +def test_transport_kind_grpc(): + transport = LoggingServiceV2Client.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc" -@pytest.mark.parametrize("transport_name", [ - "grpc_asyncio", -]) -@pytest.mark.asyncio -async def test_transport_kind_async(transport_name): - transport = LoggingServiceV2AsyncClient.get_transport_class(transport_name)( - credentials=async_anonymous_credentials(), +def test_transport_kind_grpc_asyncio(): + transport = LoggingServiceV2AsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc_asyncio" def test_transport_grpc_default(): diff --git a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py index 8ac1d16bae..b3adec89d3 100755 --- a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py +++ b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py @@ -2906,25 +2906,19 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() -@pytest.mark.parametrize("transport_name", [ - "grpc", -]) -def test_transport_kind(transport_name): - transport = MetricsServiceV2Client.get_transport_class(transport_name)( - credentials=ga_credentials.AnonymousCredentials(), + +def test_transport_kind_grpc(): + transport = MetricsServiceV2Client.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc" -@pytest.mark.parametrize("transport_name", [ - "grpc_asyncio", -]) -@pytest.mark.asyncio -async def test_transport_kind_async(transport_name): - transport = MetricsServiceV2AsyncClient.get_transport_class(transport_name)( - credentials=async_anonymous_credentials(), +def test_transport_kind_grpc_asyncio(): + transport = MetricsServiceV2AsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc_asyncio" def test_transport_grpc_default(): 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 925ff8da9f..2771c20739 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 @@ -49,6 +49,7 @@ from .transports.grpc import CloudRedisGrpcTransport from .transports.grpc_asyncio import CloudRedisGrpcAsyncIOTransport from .transports.rest import CloudRedisRestTransport +from .transports.rest_asyncio import AsyncCloudRedisRestTransport class CloudRedisClientMeta(type): @@ -62,6 +63,7 @@ class CloudRedisClientMeta(type): _transport_registry["grpc"] = CloudRedisGrpcTransport _transport_registry["grpc_asyncio"] = CloudRedisGrpcAsyncIOTransport _transport_registry["rest"] = CloudRedisRestTransport + _transport_registry["rest_asyncio"] = AsyncCloudRedisRestTransport def get_transport_class(cls, label: Optional[str] = None, 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 new file mode 100755 index 0000000000..14a53fc4e1 --- /dev/null +++ b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from google.api_core import gapic_v1 + +from typing import Any, Optional + +from .rest_base import _BaseCloudRedisRestTransport + +from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO + +DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, + grpc_version=None, + rest_version=None, +) + +class AsyncCloudRedisRestTransport(_BaseCloudRedisRestTransport): + """Asynchronous REST backend transport for CloudRedis. + + Configures and manages Cloud Memorystore for Redis instances + + Google Cloud Memorystore for Redis v1 + + The ``redis.googleapis.com`` service implements the Google Cloud + Memorystore for Redis API and defines the following resource model + for managing Redis instances: + + - The service works with a collection of cloud projects, named: + ``/projects/*`` + - Each project has a collection of available locations, named: + ``/locations/*`` + - Each location has a collection of Redis instances, named: + ``/instances/*`` + - As such, Redis instances are resources of the form: + ``/projects/{project_id}/locations/{location_id}/instances/{instance_id}`` + + Note that location_id must be referring to a GCP ``region``; for + example: + + - ``projects/redpepper-1290/locations/us-central1/instances/my-redis`` + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends JSON representations of protocol buffers over HTTP/1.1 + """ + def __init__(self, *, + host: str = 'redis.googleapis.com', + credentials: Optional[Any] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + url_scheme: str = 'https', + ) -> None: + """Instantiate the transport. + + NOTE: This async REST transport functionality is currently in a beta + state (preview). We welcome your feedback via a GitHub issue in + this library's repository. Thank you! + + Args: + host (Optional[str]): + The hostname to connect to (default: 'redis.googleapis.com'). + credentials (Optional[Any]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you are developing + your own client library. + url_scheme: the protocol scheme for the API endpoint. Normally + "https", but for testing or local servers, + "http" can be specified. + """ + # Run the base constructor + super().__init__( + host=host, + credentials=credentials, + client_info=client_info, + always_use_jwt_access=False, + url_scheme=url_scheme, + api_audience=None + ) + + @property + def kind(self) -> str: + return "rest_asyncio" 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 d9bacbf441..6eac00c0a0 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 @@ -8403,26 +8403,33 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() -@pytest.mark.parametrize("transport_name", [ - "grpc", - "rest", -]) -def test_transport_kind(transport_name): - transport = CloudRedisClient.get_transport_class(transport_name)( - credentials=ga_credentials.AnonymousCredentials(), + +def test_transport_kind_grpc(): + transport = CloudRedisClient.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() ) - assert transport.kind == transport_name + assert transport.kind == "grpc" -@pytest.mark.parametrize("transport_name", [ - "grpc_asyncio", -]) -@pytest.mark.asyncio -async def test_transport_kind_async(transport_name): - transport = CloudRedisAsyncClient.get_transport_class(transport_name)( - credentials=async_anonymous_credentials(), +def test_transport_kind_grpc_asyncio(): + transport = CloudRedisAsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() + ) + assert transport.kind == "grpc_asyncio" + + +def test_transport_kind_rest(): + transport = CloudRedisClient.get_transport_class("rest")( + credentials=ga_credentials.AnonymousCredentials() + ) + assert transport.kind == "rest" + + +def test_transport_kind_rest_asyncio(): + transport = CloudRedisAsyncClient.get_transport_class("rest_asyncio")( + credentials=async_anonymous_credentials() ) - assert transport.kind == transport_name + assert transport.kind == "rest_asyncio" def test_transport_grpc_default(): diff --git a/tests/integration/redis_v1.yaml b/tests/integration/redis_v1.yaml index 499c13d4e4..26c5b674f5 100644 --- a/tests/integration/redis_v1.yaml +++ b/tests/integration/redis_v1.yaml @@ -67,4 +67,11 @@ authentication: - selector: 'google.longrunning.Operations.*' oauth: canonical_scopes: |- - https://www.googleapis.com/auth/cloud-platform \ No newline at end of file + https://www.googleapis.com/auth/cloud-platform + +publishing: + library_settings: + - version: 'google.cloud.redis.v1' + python_settings: + experimental_features: + rest_async_io_enabled: true From 0bbb9abca2aa0d8328390345b3ec2463d82258e4 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Wed, 4 Sep 2024 19:31:20 +0000 Subject: [PATCH 3/7] address PR comments --- .github/workflows/tests.yaml | 2 +- gapic/generator/generator.py | 3 +-- .../%sub/services/%service/client.py.j2 | 5 +--- .../%service/transports/rest_asyncio.py.j2 | 5 +--- .../%name_%version/%sub/test_%service.py.j2 | 13 +++------- .../gapic/%name_%version/%sub/test_macros.j2 | 25 ++++++++----------- noxfile.py | 7 +++--- 7 files changed, 21 insertions(+), 39 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2544bbfb82..d4c95290c9 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -140,7 +140,7 @@ jobs: strategy: matrix: python: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] - variant: ['', _alternative_templates, _mixins, _alternative_templates_mixins, "_w_rest_async"] + variant: ['', _alternative_templates, _mixins, _alternative_templates_mixins, _w_rest_async] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/gapic/generator/generator.py b/gapic/generator/generator.py index cb0dff456b..27ad4b7ff2 100644 --- a/gapic/generator/generator.py +++ b/gapic/generator/generator.py @@ -294,8 +294,7 @@ def _render_template( ('transport' in template_name and not self._is_desired_transport(template_name, opts)) or - # TODO: Remove the following conditions once support for async rest transport is GA: - # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove this condition when async rest is GA. ('async_client' in template_name and 'grpc' not in opts.transport and not api_schema.all_library_settings[api_schema.naming.proto_package].python_settings.experimental_features.rest_async_io_enabled) or 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 d325ab6334..77cbbc6f0f 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 @@ -1,7 +1,4 @@ -{# TODO: Remove the following condition for async rest transport once support for it is GA: - # {% if rest_async_io_enabled %} - # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. -#} +{# 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' %} 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 731b569c30..8e0fc27a89 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 @@ -1,7 +1,4 @@ -{# TODO: Remove the following condition for async rest transport once support for it is GA: - # {% if rest_async_io_enabled %} - # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. -#} +{# 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' %} 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 13488a6999..ad0c1b751e 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 @@ -1,7 +1,4 @@ -{# TODO: Remove the following condition for async rest transport once support for it is GA: - # {% if rest_async_io_enabled %} - # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. -#} +{# 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" %} @@ -1048,12 +1045,10 @@ def test_transport_adc(transport_class): {% set configs = [] %} {% for transport_name in opts.transport %} - {% do configs.append({'service':service, 'transport_name':transport_name}) %} -{# TODO: Remove the following guard once support for async REST is GA. - # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. -#} + {% do configs.append({'service':service, 'transport_name':transport_name, 'is_async':false}) %} +{# TODO(#2121): Remove this condition when async rest is GA #} {% if 'grpc' in transport_name or rest_async_io_enabled %} - {% do configs.append({'service':service, 'transport_name':transport_name + "_asyncio"}) %} + {% do configs.append({'service':service, 'transport_name':transport_name + "_asyncio", 'is_async':true}) %} {% endif %} {% endfor %} 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 0726a9755e..c095d33fd3 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 @@ -1877,23 +1877,18 @@ def test_{{ method_name }}_empty_call(): assert args[0] == {{ method.input.ident }}() {% endmacro %} +{% macro get_credentials(is_async=False) %} +{{-'async_anonymous_credentials()' if is_async else 'ga_credentials.AnonymousCredentials()'-}} +{% endmacro %} + +{% macro get_client(service, is_async) %} +{{-service.async_client_name if is_async else service.client_name-}} +{% endmacro %} -{% macro transport_kind_test(service, transport_name) %} -{%- set named_clients = { - 'rest': service.client_name, - 'grpc': service.client_name, - 'grpc_asyncio': service.async_client_name, - 'rest_asyncio': service.async_client_name} --%} -{%- set named_credentials = { - 'rest': 'ga_credentials.AnonymousCredentials()', - 'grpc': 'ga_credentials.AnonymousCredentials()', - 'grpc_asyncio': 'async_anonymous_credentials()', - 'rest_asyncio': 'async_anonymous_credentials()'} --%} +{% macro transport_kind_test(service, transport_name, is_async) %} def test_transport_kind_{{transport_name}}(): - transport = {{ named_clients[transport_name] }}.get_transport_class("{{transport_name}}")( - credentials={{named_credentials[transport_name]}} + transport = {{ get_client(service, is_async) }}.get_transport_class("{{transport_name}}")( + credentials={{get_credentials(is_async)}} ) assert transport.kind == "{{ transport_name }}" {% endmacro %} diff --git a/noxfile.py b/noxfile.py index 7382249ff2..d5b0676859 100644 --- a/noxfile.py +++ b/noxfile.py @@ -188,6 +188,7 @@ def _add_python_settings(tmp_dir, python_settings): yaml.safe_dump(data, file, default_flow_style=False, sort_keys=False) """ +# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): `rest_async_io_enabled` must be removed once async rest is GA. @contextmanager def showcase_library( session, templates="DEFAULT", other_opts: typing.Iterable[str] = (), @@ -234,10 +235,8 @@ def showcase_library( external=True, silent=True, ) - # TODO: The below section updates the showcase service yaml - # to test experimental async rest transport. It must be - # removed once support for async rest is GA. - # See related issue: https://github.com/googleapis/gapic-generator-python/issues/2121. + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): The section below updates the showcase service yaml + # to test experimental async rest transport. It must be removed once support for async rest is GA. if rest_async_io_enabled: # Install pyYAML for yaml session.install("pyYAML") From d48c6cf03fb267380cc4c0542671899d987251c5 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Wed, 4 Sep 2024 19:59:03 +0000 Subject: [PATCH 4/7] address feedback --- .github/workflows/tests.yaml | 1 + .../%name_%version/%sub/services/%service/client.py.j2 | 2 ++ .../%sub/services/%service/transports/rest_asyncio.py.j2 | 6 +++--- .../unit/gapic/%name_%version/%sub/test_%service.py.j2 | 4 +--- .../tests/unit/gapic/%name_%version/%sub/test_macros.j2 | 1 + noxfile.py | 6 +++++- .../asset/tests/unit/gapic/asset_v1/test_asset_service.py | 1 - .../tests/unit/gapic/credentials_v1/test_iam_credentials.py | 1 - .../eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py | 1 - .../tests/unit/gapic/logging_v2/test_config_service_v2.py | 1 - .../tests/unit/gapic/logging_v2/test_logging_service_v2.py | 1 - .../tests/unit/gapic/logging_v2/test_metrics_service_v2.py | 1 - .../redis/tests/unit/gapic/redis_v1/test_cloud_redis.py | 4 ---- tests/integration/redis_v1.yaml | 2 ++ 14 files changed, 15 insertions(+), 17 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d4c95290c9..4c00a2bb32 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -140,6 +140,7 @@ jobs: strategy: matrix: python: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2121) Remove `_w_rest_async` variant when async rest is GA. variant: ['', _alternative_templates, _mixins, _alternative_templates_mixins, _w_rest_async] runs-on: ubuntu-latest steps: 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 77cbbc6f0f..8cdda2c833 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 @@ -64,6 +64,7 @@ from .transports.grpc_asyncio import {{ service.grpc_asyncio_transport_name }} {% endif %} {% if 'rest' in opts.transport %} from .transports.rest import {{ service.name }}RestTransport +{# TODO(#2121): Remove this condition when async rest is GA. #} {% if rest_async_io_enabled %} from .transports.rest_asyncio import Async{{ service.name }}RestTransport {% endif %}{# if rest_async_io_enabled #} @@ -84,6 +85,7 @@ class {{ service.client_name }}Meta(type): {% endif %} {% if "rest" in opts.transport %} _transport_registry["rest"] = {{ service.name }}RestTransport + {# TODO(#2121): Remove this condition when async rest is GA. #} {% if rest_async_io_enabled %} _transport_registry["rest_asyncio"] = Async{{ service.name }}RestTransport {% endif %}{# if rest_async_io_enabled #} 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 8e0fc27a89..a34c336b03 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 @@ -12,7 +12,7 @@ from .rest_base import _Base{{ service.name }}RestTransport from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO -{# TODO (ohmayr): determine the version of rest. aiohttp or google.auth.aio. #} +{# 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, @@ -32,7 +32,7 @@ class Async{{service.name}}RestTransport(_Base{{ service.name }}RestTransport): """ def __init__(self, *, host: str{% if service.host %} = '{{ service.host }}'{% endif %}, - {# TODO (ohmayr): Update the default type for credentials. #} + {# TODO (https://github.com/googleapis/gapic-generator-python/issues/2129): Update the default type for credentials. #} credentials: Optional[Any] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, url_scheme: str = 'https', @@ -48,7 +48,7 @@ class Async{{service.name}}RestTransport(_Base{{ service.name }}RestTransport): Args: host ({% if service.host %}Optional[str]{% else %}str{% endif %}): {{ ' ' }}The hostname to connect to {% if service.host %}(default: '{{ service.host }}'){% endif %}. - {# TODO (ohmayr): Update the default type for credentials. #} + {# TODO (https://github.com/googleapis/gapic-generator-python/issues/2129): Update the default type for credentials. #} credentials (Optional[Any]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none 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 ad0c1b751e..90fe7ea2d5 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 @@ -1046,15 +1046,13 @@ def test_transport_adc(transport_class): {% set configs = [] %} {% for transport_name in opts.transport %} {% do configs.append({'service':service, 'transport_name':transport_name, 'is_async':false}) %} -{# TODO(#2121): Remove this condition when async rest is GA #} +{# TODO(#2121): Remove this condition when async rest is GA. #} {% if 'grpc' in transport_name or rest_async_io_enabled %} {% do configs.append({'service':service, 'transport_name':transport_name + "_asyncio", 'is_async':true}) %} {% endif %} {% endfor %} - {% for conf in configs %} {{ test_macros.transport_kind_test(**conf) }} - {% endfor %} {% if 'grpc' in opts.transport %} 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 c095d33fd3..be3ef35581 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 @@ -1891,4 +1891,5 @@ def test_transport_kind_{{transport_name}}(): credentials={{get_credentials(is_async)}} ) assert transport.kind == "{{ transport_name }}" + {% endmacro %} diff --git a/noxfile.py b/noxfile.py index d5b0676859..4855d0b734 100644 --- a/noxfile.py +++ b/noxfile.py @@ -174,7 +174,11 @@ def fragment(session, use_ads_templates=False): def fragment_alternative_templates(session): fragment(session, use_ads_templates=True) - +# `_add_python_settings` consumes a path to a temporary directory (str) i.e. tmp_dir and +# python settings i.e. python_settings (Dict) and modifies the service yaml within +# tmp_dir to include python settings. The primary purpose of this function is to modify +# the service yaml and include `rest_async_io_enabled=True` to test the async rest +# optional feature. def _add_python_settings(tmp_dir, python_settings): return f""" import yaml diff --git a/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py b/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py index bef9509470..b7bb1fad34 100755 --- a/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py +++ b/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py @@ -16405,7 +16405,6 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() - def test_transport_kind_grpc(): transport = AssetServiceClient.get_transport_class("grpc")( credentials=ga_credentials.AnonymousCredentials() diff --git a/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py b/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py index 9f573cca17..37bcaed5d0 100755 --- a/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py +++ b/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py @@ -3490,7 +3490,6 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() - def test_transport_kind_grpc(): transport = IAMCredentialsClient.get_transport_class("grpc")( credentials=ga_credentials.AnonymousCredentials() diff --git a/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py b/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py index 8816a70cd6..5e67d9980c 100755 --- a/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py +++ b/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py @@ -13837,7 +13837,6 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() - def test_transport_kind_grpc(): transport = EventarcClient.get_transport_class("grpc")( credentials=ga_credentials.AnonymousCredentials() diff --git a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py index 8b87aca12b..17c35b4b5b 100755 --- a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py +++ b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py @@ -12335,7 +12335,6 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() - def test_transport_kind_grpc(): transport = ConfigServiceV2Client.get_transport_class("grpc")( credentials=ga_credentials.AnonymousCredentials() diff --git a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py index bd2ce61344..c8b9bfa333 100755 --- a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py +++ b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py @@ -3099,7 +3099,6 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() - def test_transport_kind_grpc(): transport = LoggingServiceV2Client.get_transport_class("grpc")( credentials=ga_credentials.AnonymousCredentials() diff --git a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py index b3adec89d3..1c391fcfc3 100755 --- a/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py +++ b/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py @@ -2906,7 +2906,6 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() - def test_transport_kind_grpc(): transport = MetricsServiceV2Client.get_transport_class("grpc")( credentials=ga_credentials.AnonymousCredentials() 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 6eac00c0a0..37450e5364 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 @@ -8403,28 +8403,24 @@ def test_transport_adc(transport_class): transport_class() adc.assert_called_once() - def test_transport_kind_grpc(): transport = CloudRedisClient.get_transport_class("grpc")( credentials=ga_credentials.AnonymousCredentials() ) assert transport.kind == "grpc" - def test_transport_kind_grpc_asyncio(): transport = CloudRedisAsyncClient.get_transport_class("grpc_asyncio")( credentials=async_anonymous_credentials() ) assert transport.kind == "grpc_asyncio" - def test_transport_kind_rest(): transport = CloudRedisClient.get_transport_class("rest")( credentials=ga_credentials.AnonymousCredentials() ) assert transport.kind == "rest" - def test_transport_kind_rest_asyncio(): transport = CloudRedisAsyncClient.get_transport_class("rest_asyncio")( credentials=async_anonymous_credentials() diff --git a/tests/integration/redis_v1.yaml b/tests/integration/redis_v1.yaml index 26c5b674f5..47440ccdad 100644 --- a/tests/integration/redis_v1.yaml +++ b/tests/integration/redis_v1.yaml @@ -69,6 +69,8 @@ authentication: canonical_scopes: |- https://www.googleapis.com/auth/cloud-platform +# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove this section +# when async rest is GA. publishing: library_settings: - version: 'google.cloud.redis.v1' From 41ded044b1d440f75a3d08baebf5faa6728433d5 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Wed, 4 Sep 2024 20:06:10 +0000 Subject: [PATCH 5/7] address minor nits --- .../%sub/services/%service/transports/rest_asyncio.py.j2 | 2 +- noxfile.py | 3 ++- .../redis_v1/services/cloud_redis/transports/rest_asyncio.py | 2 +- .../redis/tests/unit/gapic/redis_v1/test_cloud_redis.py | 3 +++ 4 files changed, 7 insertions(+), 3 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 a34c336b03..ab87c32c76 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 @@ -59,7 +59,7 @@ class Async{{service.name}}RestTransport(_Base{{ service.name }}RestTransport): API requests. If ``None``, then default info will be used. Generally, you only need to set this if you are developing your own client library. - url_scheme: the protocol scheme for the API endpoint. Normally + url_scheme (str): the protocol scheme for the API endpoint. Normally "https", but for testing or local servers, "http" can be specified. """ diff --git a/noxfile.py b/noxfile.py index 4855d0b734..c7f46cfc27 100644 --- a/noxfile.py +++ b/noxfile.py @@ -242,7 +242,7 @@ def showcase_library( # TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): The section below updates the showcase service yaml # to test experimental async rest transport. It must be removed once support for async rest is GA. if rest_async_io_enabled: - # Install pyYAML for yaml + # Install pyYAML for yaml. session.install("pyYAML") python_settings = [ @@ -257,6 +257,7 @@ def showcase_library( ] update_service_yaml = _add_python_settings(tmp_dir, python_settings) session.run("python", "-c" f"{update_service_yaml}") + # END TODO section to remove. if retry_config: session.run( "curl", 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 14a53fc4e1..5127656c34 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 @@ -83,7 +83,7 @@ def __init__(self, *, API requests. If ``None``, then default info will be used. Generally, you only need to set this if you are developing your own client library. - url_scheme: the protocol scheme for the API endpoint. Normally + url_scheme (str): the protocol scheme for the API endpoint. Normally "https", but for testing or local servers, "http" can be specified. """ 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 37450e5364..906e41d327 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 @@ -8409,18 +8409,21 @@ def test_transport_kind_grpc(): ) assert transport.kind == "grpc" + def test_transport_kind_grpc_asyncio(): transport = CloudRedisAsyncClient.get_transport_class("grpc_asyncio")( credentials=async_anonymous_credentials() ) assert transport.kind == "grpc_asyncio" + def test_transport_kind_rest(): transport = CloudRedisClient.get_transport_class("rest")( credentials=ga_credentials.AnonymousCredentials() ) assert transport.kind == "rest" + def test_transport_kind_rest_asyncio(): transport = CloudRedisAsyncClient.get_transport_class("rest_asyncio")( credentials=async_anonymous_credentials() From 1806b8976a3cb4841928d2be862041fe4ac3a1b0 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Wed, 4 Sep 2024 20:47:51 +0000 Subject: [PATCH 6/7] address PR comments --- .../gapic/%name_%version/%sub/test_%service.py.j2 | 8 ++++---- .../unit/gapic/%name_%version/%sub/test_macros.j2 | 15 ++++++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) 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 90fe7ea2d5..ae6e9ee43f 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 @@ -1044,11 +1044,11 @@ def test_transport_adc(transport_class): adc.assert_called_once() {% set configs = [] %} -{% for transport_name in opts.transport %} - {% do configs.append({'service':service, 'transport_name':transport_name, 'is_async':false}) %} +{% for transport in opts.transport %} + {% do configs.append({'service':service, 'transport':transport, 'is_async':false}) %} {# TODO(#2121): Remove this condition when async rest is GA. #} -{% if 'grpc' in transport_name or rest_async_io_enabled %} - {% do configs.append({'service':service, 'transport_name':transport_name + "_asyncio", 'is_async':true}) %} +{% if 'grpc' in transport or rest_async_io_enabled %} + {% do configs.append({'service':service, 'transport':transport, 'is_async':true}) %} {% endif %} {% endfor %} {% for conf in configs %} 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 be3ef35581..8db08d81af 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 @@ -1878,16 +1878,21 @@ def test_{{ method_name }}_empty_call(): {% endmacro %} {% macro get_credentials(is_async=False) %} -{{-'async_anonymous_credentials()' if is_async else 'ga_credentials.AnonymousCredentials()'-}} +{{- 'async_anonymous_credentials()' if is_async else 'ga_credentials.AnonymousCredentials()' -}} {% endmacro %} {% macro get_client(service, is_async) %} -{{-service.async_client_name if is_async else service.client_name-}} +{{- service.async_client_name if is_async else service.client_name -}} {% endmacro %} -{% macro transport_kind_test(service, transport_name, is_async) %} -def test_transport_kind_{{transport_name}}(): - transport = {{ get_client(service, is_async) }}.get_transport_class("{{transport_name}}")( +{% macro get_transport_name(transport, is_async=False)%} +{{- transport + "_asyncio" if is_async else transport -}} +{% endmacro %} + +{% macro transport_kind_test(service, transport, is_async) %} +{% set transport_name = get_transport_name(transport, is_async) %} +def test_transport_kind_{{ transport_name }}(): + transport = {{ get_client(service, is_async) }}.get_transport_class("{{ transport_name }}")( credentials={{get_credentials(is_async)}} ) assert transport.kind == "{{ transport_name }}" From c26f72c7e22d23a578366cadb2d1fa70ccfb9623 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Thu, 5 Sep 2024 18:44:23 +0000 Subject: [PATCH 7/7] address PR comments --- .../%name_%version/%sub/services/%service/client.py.j2 | 4 ++-- .../tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 | 2 +- .../tests/unit/gapic/%name_%version/%sub/test_macros.j2 | 2 +- noxfile.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) 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 8cdda2c833..168955c8f6 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 @@ -64,7 +64,7 @@ from .transports.grpc_asyncio import {{ service.grpc_asyncio_transport_name }} {% endif %} {% if 'rest' in opts.transport %} from .transports.rest import {{ service.name }}RestTransport -{# TODO(#2121): Remove this condition when async rest is GA. #} +{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove this condition when async rest is GA. #} {% if rest_async_io_enabled %} from .transports.rest_asyncio import Async{{ service.name }}RestTransport {% endif %}{# if rest_async_io_enabled #} @@ -85,7 +85,7 @@ class {{ service.client_name }}Meta(type): {% endif %} {% if "rest" in opts.transport %} _transport_registry["rest"] = {{ service.name }}RestTransport - {# TODO(#2121): Remove this condition when async rest is GA. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove this condition when async rest is GA. #} {% if rest_async_io_enabled %} _transport_registry["rest_asyncio"] = Async{{ service.name }}RestTransport {% endif %}{# if rest_async_io_enabled #} 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 ae6e9ee43f..6f467a56a6 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 @@ -1046,7 +1046,7 @@ def test_transport_adc(transport_class): {% set configs = [] %} {% for transport in opts.transport %} {% do configs.append({'service':service, 'transport':transport, 'is_async':false}) %} -{# TODO(#2121): Remove this condition when async rest is GA. #} +{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove this condition when async rest is GA. #} {% if 'grpc' in transport or rest_async_io_enabled %} {% do configs.append({'service':service, 'transport':transport, 'is_async':true}) %} {% endif %} 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 8db08d81af..2f2fb3b74f 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 @@ -1886,7 +1886,7 @@ def test_{{ method_name }}_empty_call(): {% endmacro %} {% macro get_transport_name(transport, is_async=False)%} -{{- transport + "_asyncio" if is_async else transport -}} +{{- transport + ("_asyncio" if is_async else "") -}} {% endmacro %} {% macro transport_kind_test(service, transport, is_async) %} diff --git a/noxfile.py b/noxfile.py index c7f46cfc27..0a28618b4e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -174,8 +174,8 @@ def fragment(session, use_ads_templates=False): def fragment_alternative_templates(session): fragment(session, use_ads_templates=True) -# `_add_python_settings` consumes a path to a temporary directory (str) i.e. tmp_dir and -# python settings i.e. python_settings (Dict) and modifies the service yaml within +# `_add_python_settings` consumes a path to a temporary directory (str; i.e. tmp_dir) and +# python settings (Dict; i.e. python settings) and modifies the service yaml within # tmp_dir to include python settings. The primary purpose of this function is to modify # the service yaml and include `rest_async_io_enabled=True` to test the async rest # optional feature.