Skip to content

Commit 1967aa1

Browse files
authored
chore: refactor wrap method helper into a macro (#2111)
1 parent 11e3967 commit 1967aa1

File tree

18 files changed

+361
-149
lines changed

18 files changed

+361
-149
lines changed

gapic/templates/%namespace/%name_%version/%sub/services/%service/_shared_macros.j2

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,53 @@ def _get_response(
159159
raise core_exceptions.from_http_response(response)
160160

161161
{% endmacro %}
162+
163+
164+
{% macro prep_wrapped_messages_async_method(service) %}
165+
def _prep_wrapped_messages(self, client_info):
166+
""" Precompute the wrapped methods, overriding the base class method to use async wrappers."""
167+
self._wrapped_methods = {
168+
{% for method in service.methods.values() %}
169+
self.{{ method.transport_safe_name|snake_case }}: self._wrap_method(
170+
self.{{ method.transport_safe_name|snake_case }},
171+
{% if method.retry %}
172+
default_retry=retries.AsyncRetry(
173+
{% if method.retry.initial_backoff %}
174+
initial={{ method.retry.initial_backoff }},
175+
{% endif %}
176+
{% if method.retry.max_backoff %}
177+
maximum={{ method.retry.max_backoff }},
178+
{% endif %}
179+
{% if method.retry.backoff_multiplier %}
180+
multiplier={{ method.retry.backoff_multiplier }},
181+
{% endif %}
182+
predicate=retries.if_exception_type(
183+
{% for ex in method.retry.retryable_exceptions|sort(attribute='__name__') %}
184+
core_exceptions.{{ ex.__name__ }},
185+
{% endfor %}
186+
),
187+
deadline={{ method.timeout }},
188+
),
189+
{% endif %}
190+
default_timeout={{ method.timeout }},
191+
client_info=client_info,
192+
),
193+
{% endfor %}{# service.methods.values() #}
194+
}
195+
{% endmacro %}
196+
197+
{# TODO: This helper logic to check whether `kind` needs to be configured in wrap_method
198+
can be removed once we require the correct version of the google-api-core dependency to
199+
avoid having a gRPC code path in an async REST call.
200+
See related issue: https://github.com/googleapis/python-api-core/issues/661.
201+
In the meantime, if an older version of the dependency is installed (which has a wrap_method with
202+
no kind parameter), then an async gRPC call will work correctly and async REST transport
203+
will not be available as a transport.
204+
See related issue: https://github.com/googleapis/gapic-generator-python/issues/2119. #}
205+
{% macro wrap_async_method_macro() %}
206+
def _wrap_method(self, func, *args, **kwargs):
207+
{# TODO: Remove `pragma: NO COVER` once https://github.com/googleapis/python-api-core/pull/688 is merged. #}
208+
if self._wrap_with_kind: # pragma: NO COVER
209+
kwargs["kind"] = self.kind
210+
return gapic_v1.method_async.wrap_method(func, *args, **kwargs)
211+
{% endmacro %}

gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/grpc_asyncio.py.j2

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
{% extends '_base.py.j2' %}
22

33
{% block content %}
4+
{% import "%namespace/%name_%version/%sub/services/%service/_shared_macros.j2" as shared_macros %}
45

6+
import inspect
57
import warnings
68
from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union
79

@@ -241,6 +243,7 @@ class {{ service.grpc_asyncio_transport_name }}({{ service.name }}Transport):
241243
)
242244

243245
# Wrap messages. This must be done after self._grpc_channel exists
246+
self._wrap_with_kind = "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters
244247
self._prep_wrapped_messages(client_info)
245248

246249
@property
@@ -385,39 +388,16 @@ class {{ service.grpc_asyncio_transport_name }}({{ service.name }}Transport):
385388
return self._stubs["test_iam_permissions"]
386389
{% endif %}
387390

388-
def _prep_wrapped_messages(self, client_info):
389-
""" Precompute the wrapped methods, overriding the base class method to use async wrappers."""
390-
self._wrapped_methods = {
391-
{% for method in service.methods.values() %}
392-
self.{{ method.transport_safe_name|snake_case }}: gapic_v1.method_async.wrap_method(
393-
self.{{ method.transport_safe_name|snake_case }},
394-
{% if method.retry %}
395-
default_retry=retries.AsyncRetry(
396-
{% if method.retry.initial_backoff %}
397-
initial={{ method.retry.initial_backoff }},
398-
{% endif %}
399-
{% if method.retry.max_backoff %}
400-
maximum={{ method.retry.max_backoff }},
401-
{% endif %}
402-
{% if method.retry.backoff_multiplier %}
403-
multiplier={{ method.retry.backoff_multiplier }},
404-
{% endif %}
405-
predicate=retries.if_exception_type(
406-
{% for ex in method.retry.retryable_exceptions|sort(attribute='__name__') %}
407-
core_exceptions.{{ ex.__name__ }},
408-
{% endfor %}
409-
),
410-
deadline={{ method.timeout }},
411-
),
412-
{% endif %}
413-
default_timeout={{ method.timeout }},
414-
client_info=client_info,
415-
),
416-
{% endfor %} {# service.methods.values() #}
417-
}
391+
{{ shared_macros.prep_wrapped_messages_async_method(service)|indent(4) }}
392+
393+
{{ shared_macros.wrap_async_method_macro()|indent(4) }}
418394

419395
def close(self):
420396
return self.grpc_channel.close()
397+
398+
@property
399+
def kind(self) -> str:
400+
return "grpc_asyncio"
421401

422402
{% include '%namespace/%name_%version/%sub/services/%service/transports/_mixins.py.j2' %}
423403

gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,19 +1041,9 @@ def test_transport_adc(transport_class):
10411041
transport_class()
10421042
adc.assert_called_once()
10431043

1044-
@pytest.mark.parametrize("transport_name", [
1045-
{% if "grpc" in opts.transport %}
1046-
"grpc",
1047-
{% endif %}
1048-
{% if "rest" in opts.transport %}
1049-
"rest",
1050-
{% endif %}
1051-
])
1052-
def test_transport_kind(transport_name):
1053-
transport = {{ service.client_name }}.get_transport_class(transport_name)(
1054-
credentials=ga_credentials.AnonymousCredentials(),
1055-
)
1056-
assert transport.kind == transport_name
1044+
{{ test_macros.transport_kind_test(service, opts) }}
1045+
1046+
{{ test_macros.transport_kind_test(service, opts, is_async=True) }}
10571047

10581048
{% if 'grpc' in opts.transport %}
10591049
def test_transport_grpc_default():

gapic/templates/tests/unit/gapic/%name_%version/%sub/test_macros.j2

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,3 +1876,34 @@ def test_{{ method_name }}_empty_call():
18761876
{% endwith %}{# method_settings #}
18771877
assert args[0] == {{ method.input.ident }}()
18781878
{% endmacro %}
1879+
1880+
1881+
{% macro transport_kind_test(service, opts, is_async=False) %}
1882+
@pytest.mark.parametrize("transport_name", [
1883+
{% if is_async %}
1884+
{% if "grpc" in opts.transport %}
1885+
"grpc_asyncio",
1886+
{% endif %}
1887+
{% else %}{# if not is_async #}
1888+
{% if "grpc" in opts.transport%}
1889+
"grpc",
1890+
{% endif %}
1891+
{% if "rest" in opts.transport %}
1892+
"rest",
1893+
{% endif %}
1894+
{% endif %}{# is_async #}
1895+
])
1896+
{% if is_async %}
1897+
@pytest.mark.asyncio
1898+
async def test_transport_kind_async(transport_name):
1899+
transport = {{ service.async_client_name }}.get_transport_class(transport_name)(
1900+
credentials=async_anonymous_credentials(),
1901+
)
1902+
{% else %}
1903+
def test_transport_kind(transport_name):
1904+
transport = {{ service.client_name }}.get_transport_class(transport_name)(
1905+
credentials=ga_credentials.AnonymousCredentials(),
1906+
)
1907+
{% endif %}
1908+
assert transport.kind == transport_name
1909+
{% endmacro %}

tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/transports/grpc_asyncio.py

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
#
16+
import inspect
1617
import warnings
1718
from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union
1819

@@ -227,6 +228,7 @@ def __init__(self, *,
227228
)
228229

229230
# Wrap messages. This must be done after self._grpc_channel exists
231+
self._wrap_with_kind = "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters
230232
self._prep_wrapped_messages(client_info)
231233

232234
@property
@@ -941,17 +943,17 @@ def analyze_org_policy_governed_assets(self) -> Callable[
941943
def _prep_wrapped_messages(self, client_info):
942944
""" Precompute the wrapped methods, overriding the base class method to use async wrappers."""
943945
self._wrapped_methods = {
944-
self.export_assets: gapic_v1.method_async.wrap_method(
946+
self.export_assets: self._wrap_method(
945947
self.export_assets,
946948
default_timeout=60.0,
947949
client_info=client_info,
948950
),
949-
self.list_assets: gapic_v1.method_async.wrap_method(
951+
self.list_assets: self._wrap_method(
950952
self.list_assets,
951953
default_timeout=None,
952954
client_info=client_info,
953955
),
954-
self.batch_get_assets_history: gapic_v1.method_async.wrap_method(
956+
self.batch_get_assets_history: self._wrap_method(
955957
self.batch_get_assets_history,
956958
default_retry=retries.AsyncRetry(
957959
initial=0.1,
@@ -966,12 +968,12 @@ def _prep_wrapped_messages(self, client_info):
966968
default_timeout=60.0,
967969
client_info=client_info,
968970
),
969-
self.create_feed: gapic_v1.method_async.wrap_method(
971+
self.create_feed: self._wrap_method(
970972
self.create_feed,
971973
default_timeout=60.0,
972974
client_info=client_info,
973975
),
974-
self.get_feed: gapic_v1.method_async.wrap_method(
976+
self.get_feed: self._wrap_method(
975977
self.get_feed,
976978
default_retry=retries.AsyncRetry(
977979
initial=0.1,
@@ -986,7 +988,7 @@ def _prep_wrapped_messages(self, client_info):
986988
default_timeout=60.0,
987989
client_info=client_info,
988990
),
989-
self.list_feeds: gapic_v1.method_async.wrap_method(
991+
self.list_feeds: self._wrap_method(
990992
self.list_feeds,
991993
default_retry=retries.AsyncRetry(
992994
initial=0.1,
@@ -1001,12 +1003,12 @@ def _prep_wrapped_messages(self, client_info):
10011003
default_timeout=60.0,
10021004
client_info=client_info,
10031005
),
1004-
self.update_feed: gapic_v1.method_async.wrap_method(
1006+
self.update_feed: self._wrap_method(
10051007
self.update_feed,
10061008
default_timeout=60.0,
10071009
client_info=client_info,
10081010
),
1009-
self.delete_feed: gapic_v1.method_async.wrap_method(
1011+
self.delete_feed: self._wrap_method(
10101012
self.delete_feed,
10111013
default_retry=retries.AsyncRetry(
10121014
initial=0.1,
@@ -1021,7 +1023,7 @@ def _prep_wrapped_messages(self, client_info):
10211023
default_timeout=60.0,
10221024
client_info=client_info,
10231025
),
1024-
self.search_all_resources: gapic_v1.method_async.wrap_method(
1026+
self.search_all_resources: self._wrap_method(
10251027
self.search_all_resources,
10261028
default_retry=retries.AsyncRetry(
10271029
initial=0.1,
@@ -1036,7 +1038,7 @@ def _prep_wrapped_messages(self, client_info):
10361038
default_timeout=15.0,
10371039
client_info=client_info,
10381040
),
1039-
self.search_all_iam_policies: gapic_v1.method_async.wrap_method(
1041+
self.search_all_iam_policies: self._wrap_method(
10401042
self.search_all_iam_policies,
10411043
default_retry=retries.AsyncRetry(
10421044
initial=0.1,
@@ -1051,7 +1053,7 @@ def _prep_wrapped_messages(self, client_info):
10511053
default_timeout=15.0,
10521054
client_info=client_info,
10531055
),
1054-
self.analyze_iam_policy: gapic_v1.method_async.wrap_method(
1056+
self.analyze_iam_policy: self._wrap_method(
10551057
self.analyze_iam_policy,
10561058
default_retry=retries.AsyncRetry(
10571059
initial=0.1,
@@ -1065,71 +1067,80 @@ def _prep_wrapped_messages(self, client_info):
10651067
default_timeout=300.0,
10661068
client_info=client_info,
10671069
),
1068-
self.analyze_iam_policy_longrunning: gapic_v1.method_async.wrap_method(
1070+
self.analyze_iam_policy_longrunning: self._wrap_method(
10691071
self.analyze_iam_policy_longrunning,
10701072
default_timeout=60.0,
10711073
client_info=client_info,
10721074
),
1073-
self.analyze_move: gapic_v1.method_async.wrap_method(
1075+
self.analyze_move: self._wrap_method(
10741076
self.analyze_move,
10751077
default_timeout=None,
10761078
client_info=client_info,
10771079
),
1078-
self.query_assets: gapic_v1.method_async.wrap_method(
1080+
self.query_assets: self._wrap_method(
10791081
self.query_assets,
10801082
default_timeout=None,
10811083
client_info=client_info,
10821084
),
1083-
self.create_saved_query: gapic_v1.method_async.wrap_method(
1085+
self.create_saved_query: self._wrap_method(
10841086
self.create_saved_query,
10851087
default_timeout=None,
10861088
client_info=client_info,
10871089
),
1088-
self.get_saved_query: gapic_v1.method_async.wrap_method(
1090+
self.get_saved_query: self._wrap_method(
10891091
self.get_saved_query,
10901092
default_timeout=None,
10911093
client_info=client_info,
10921094
),
1093-
self.list_saved_queries: gapic_v1.method_async.wrap_method(
1095+
self.list_saved_queries: self._wrap_method(
10941096
self.list_saved_queries,
10951097
default_timeout=None,
10961098
client_info=client_info,
10971099
),
1098-
self.update_saved_query: gapic_v1.method_async.wrap_method(
1100+
self.update_saved_query: self._wrap_method(
10991101
self.update_saved_query,
11001102
default_timeout=None,
11011103
client_info=client_info,
11021104
),
1103-
self.delete_saved_query: gapic_v1.method_async.wrap_method(
1105+
self.delete_saved_query: self._wrap_method(
11041106
self.delete_saved_query,
11051107
default_timeout=None,
11061108
client_info=client_info,
11071109
),
1108-
self.batch_get_effective_iam_policies: gapic_v1.method_async.wrap_method(
1110+
self.batch_get_effective_iam_policies: self._wrap_method(
11091111
self.batch_get_effective_iam_policies,
11101112
default_timeout=None,
11111113
client_info=client_info,
11121114
),
1113-
self.analyze_org_policies: gapic_v1.method_async.wrap_method(
1115+
self.analyze_org_policies: self._wrap_method(
11141116
self.analyze_org_policies,
11151117
default_timeout=None,
11161118
client_info=client_info,
11171119
),
1118-
self.analyze_org_policy_governed_containers: gapic_v1.method_async.wrap_method(
1120+
self.analyze_org_policy_governed_containers: self._wrap_method(
11191121
self.analyze_org_policy_governed_containers,
11201122
default_timeout=None,
11211123
client_info=client_info,
11221124
),
1123-
self.analyze_org_policy_governed_assets: gapic_v1.method_async.wrap_method(
1125+
self.analyze_org_policy_governed_assets: self._wrap_method(
11241126
self.analyze_org_policy_governed_assets,
11251127
default_timeout=None,
11261128
client_info=client_info,
11271129
),
1128-
}
1130+
}
1131+
1132+
def _wrap_method(self, func, *args, **kwargs):
1133+
if self._wrap_with_kind: # pragma: NO COVER
1134+
kwargs["kind"] = self.kind
1135+
return gapic_v1.method_async.wrap_method(func, *args, **kwargs)
11291136

11301137
def close(self):
11311138
return self.grpc_channel.close()
11321139

1140+
@property
1141+
def kind(self) -> str:
1142+
return "grpc_asyncio"
1143+
11331144
@property
11341145
def get_operation(
11351146
self,

tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16415,6 +16415,18 @@ def test_transport_kind(transport_name):
1641516415
)
1641616416
assert transport.kind == transport_name
1641716417

16418+
16419+
@pytest.mark.parametrize("transport_name", [
16420+
"grpc_asyncio",
16421+
])
16422+
@pytest.mark.asyncio
16423+
async def test_transport_kind_async(transport_name):
16424+
transport = AssetServiceAsyncClient.get_transport_class(transport_name)(
16425+
credentials=async_anonymous_credentials(),
16426+
)
16427+
assert transport.kind == transport_name
16428+
16429+
1641816430
def test_transport_grpc_default():
1641916431
# A client should use the gRPC transport by default.
1642016432
client = AssetServiceClient(

0 commit comments

Comments
 (0)