Skip to content

Commit ab3defd

Browse files
committed
Merge branch 'master' into vchudnov-showcase.
2 parents 83ead51 + da119c7 commit ab3defd

File tree

163 files changed

+3521
-161
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

163 files changed

+3521
-161
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## [0.49.0](https://www.github.com/googleapis/gapic-generator-python/compare/v0.48.1...v0.49.0) (2021-06-11)
4+
5+
6+
### Features
7+
8+
* add async samples ([#861](https://www.github.com/googleapis/gapic-generator-python/issues/861)) ([e385ffd](https://www.github.com/googleapis/gapic-generator-python/commit/e385ffd7f012c6a38c9fcd7c5f36ce090311032b))
9+
310
### [0.48.1](https://www.github.com/googleapis/gapic-generator-python/compare/v0.48.0...v0.48.1) (2021-06-09)
411

512

gapic/samplegen/samplegen.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,15 @@ def preprocess_sample(sample, api_schema: api.API, rpc: wrappers.Method):
293293
sample["module_name"] = api_schema.naming.versioned_module_name
294294
sample["module_namespace"] = api_schema.naming.module_namespace
295295

296-
sample["client_name"] = api_schema.services[sample["service"]].client_name
296+
# Assume the gRPC transport if the transport is not specified
297+
sample.setdefault("transport", api.TRANSPORT_GRPC)
298+
299+
if sample["transport"] == api.TRANSPORT_GRPC_ASYNC:
300+
sample["client_name"] = api_schema.services[sample["service"]
301+
].async_client_name
302+
else:
303+
sample["client_name"] = api_schema.services[sample["service"]].client_name
304+
297305
# the type of the request object passed to the rpc e.g, `ListRequest`
298306
sample["request_type"] = rpc.input.ident.name
299307

@@ -946,17 +954,16 @@ def generate_sample_specs(api_schema: api.API, *, opts) -> Generator[Dict[str, A
946954

947955
for service_name, service in gapic_metadata.services.items():
948956
api_short_name = api_schema.services[f"{api_schema.naming.proto_package}.{service_name}"].shortname
949-
for transport_type, client in service.clients.items():
950-
if transport_type == "grpc-async":
951-
# TODO(busunkim): Enable generation of async samples
952-
continue
957+
for transport, client in service.clients.items():
958+
transport_type = "async" if transport == api.TRANSPORT_GRPC_ASYNC else "sync"
953959
for rpc_name, method_list in client.rpcs.items():
954960
# Region Tag Format:
955961
# [{START|END} ${apishortname}_generated_${api}_${apiVersion}_${serviceName}_${rpcName}_{sync|async}_${overloadDisambiguation}]
956962
region_tag = f"{api_short_name}_generated_{api_schema.naming.versioned_module_name}_{service_name}_{rpc_name}_{transport_type}"
957963
spec = {
958964
"sample_type": "standalone",
959965
"rpc": rpc_name,
966+
"transport": transport,
960967
"request": [],
961968
# response is populated in `preprocess_sample`
962969
"service": f"{api_schema.naming.proto_package}.{service_name}",

gapic/schema/api.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
from gapic.utils import RESERVED_NAMES
4646

4747

48+
TRANSPORT_GRPC = "grpc"
49+
TRANSPORT_GRPC_ASYNC = "grpc-async"
50+
TRANSPORT_REST = "rest"
51+
52+
4853
@dataclasses.dataclass(frozen=True)
4954
class Proto:
5055
"""A representation of a particular proto file within an API."""
@@ -414,11 +419,12 @@ def gapic_metadata(self, options: Options) -> gapic_metadata_pb2.GapicMetadata:
414419
# This assumes the options are generated by the class method factory.
415420
transports = []
416421
if "grpc" in options.transport:
417-
transports.append(("grpc", service.client_name))
418-
transports.append(("grpc-async", service.async_client_name))
422+
transports.append((TRANSPORT_GRPC, service.client_name))
423+
transports.append(
424+
(TRANSPORT_GRPC_ASYNC, service.async_client_name))
419425

420426
if "rest" in options.transport:
421-
transports.append(("rest", service.client_name))
427+
transports.append((TRANSPORT_REST, service.client_name))
422428

423429
methods = sorted(service.methods.values(), key=lambda m: m.name)
424430
for tprt, client_name in transports:

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ from google.api_core import retry as retries # type: ignore
1919
from google.api_core import operations_v1 # type: ignore
2020
{% endif %}
2121
from google.auth import credentials as ga_credentials # type: ignore
22+
from google.oauth2 import service_account # type: ignore
2223

2324
{% filter sort_lines %}
2425
{% for method in service.methods.values() %}
@@ -75,6 +76,7 @@ class {{ service.name }}Transport(abc.ABC):
7576
scopes: Optional[Sequence[str]] = None,
7677
quota_project_id: Optional[str] = None,
7778
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
79+
always_use_jwt_access: Optional[bool] = False,
7880
**kwargs,
7981
) -> None:
8082
"""Instantiate the transport.
@@ -98,6 +100,8 @@ class {{ service.name }}Transport(abc.ABC):
98100
API requests. If ``None``, then default info will be used.
99101
Generally, you only need to set this if you're developing
100102
your own client library.
103+
always_use_jwt_access (Optional[bool]): Whether self signed JWT should
104+
be used for service account credentials.
101105
"""
102106
# Save the hostname. Default to port 443 (HTTPS) if none is specified.
103107
if ':' not in host:
@@ -124,6 +128,10 @@ class {{ service.name }}Transport(abc.ABC):
124128
elif credentials is None:
125129
credentials, _ = google.auth.default(**scopes_kwargs, quota_project_id=quota_project_id)
126130

131+
# If the credentials is service account credentials, then always try to use self signed JWT.
132+
if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"):
133+
credentials = credentials.with_always_use_jwt_access(True)
134+
127135
# Save the credentials.
128136
self._credentials = credentials
129137

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
150150
scopes=scopes,
151151
quota_project_id=quota_project_id,
152152
client_info=client_info,
153+
always_use_jwt_access=True,
153154
)
154155

155156
if not self._grpc_channel:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ class {{ service.grpc_asyncio_transport_name }}({{ service.name }}Transport):
195195
scopes=scopes,
196196
quota_project_id=quota_project_id,
197197
client_info=client_info,
198+
always_use_jwt_access=True,
198199
)
199200

200201
if not self._grpc_channel:

gapic/templates/examples/feature_fragments.j2

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,13 @@ request=request
202202
{% endmacro %}
203203

204204

205-
{% macro render_method_call(sample, calling_form, calling_form_enum) %}
205+
{% macro render_method_call(sample, calling_form, calling_form_enum, transport) %}
206206
{# Note: this doesn't deal with enums or unions #}
207+
{# LROs return operation objects and paged requests return pager objects #}
208+
{% if transport == "grpc-async" and calling_form not in
209+
[calling_form_enum.LongRunningRequestPromise, calling_form_enum.RequestPagedAll] %}
210+
await{{ " "}}
211+
{%- endif -%}
207212
{% if calling_form in [calling_form_enum.RequestStreamingBidi,
208213
calling_form_enum.RequestStreamingClient] %}
209214
client.{{ sample.rpc|snake_case }}([{{ render_request_params(sample.request.request_list)|trim }}])
@@ -215,7 +220,7 @@ client.{{ sample.rpc|snake_case }}({{ render_request_params_unary(sample.request
215220

216221
{# Setting up the method invocation is the responsibility of the caller: #}
217222
{# it's just easier to set up client side streaming and other things from outside this macro. #}
218-
{% macro render_calling_form(method_invocation_text, calling_form, calling_form_enum, response_statements ) %}
223+
{% macro render_calling_form(method_invocation_text, calling_form, calling_form_enum, transport, response_statements ) %}
219224
# Make the request
220225
{% if calling_form == calling_form_enum.Request %}
221226
response = {{ method_invocation_text|trim }}
@@ -228,21 +233,21 @@ response = {{ method_invocation_text|trim }}
228233
{% endif %}
229234
{% elif calling_form == calling_form_enum.RequestPagedAll %}
230235
page_result = {{ method_invocation_text|trim }}
231-
for response in page_result:
236+
{% if transport == "grpc-async" %}async {% endif %}for response in page_result:
232237
{% for statement in response_statements %}
233238
{{ dispatch_statement(statement)|trim }}
234239
{% endfor %}
235240
{% elif calling_form == calling_form_enum.RequestPaged %}
236241
page_result = {{ method_invocation_text|trim }}
237-
for page in page_result.pages():
242+
{% if transport == "grpc-async" %}async {% endif %}for page in page_result.pages():
238243
for response in page:
239244
{% for statement in response_statements %}
240245
{{ dispatch_statement(statement)|trim }}
241246
{% endfor %}
242247
{% elif calling_form in [calling_form_enum.RequestStreamingServer,
243248
calling_form_enum.RequestStreamingBidi] %}
244249
stream = {{ method_invocation_text|trim }}
245-
for response in stream:
250+
{% if transport == "grpc-async" %}async {% endif %}for response in stream:
246251
{% for statement in response_statements %}
247252
{{ dispatch_statement(statement)|trim }}
248253
{% endfor %}
@@ -251,7 +256,7 @@ operation = {{ method_invocation_text|trim }}
251256

252257
print("Waiting for operation to complete...")
253258

254-
response = operation.result()
259+
response = {% if transport == "grpc-async" %}await {% endif %}operation.result()
255260
{% for statement in response_statements %}
256261
{{ dispatch_statement(statement)|trim }}
257262
{% endfor %}

gapic/templates/examples/sample.py.j2

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ from {{ sample.module_namespace|join(".") }} import {{ sample.module_name }}
3131

3232

3333
{# also need calling form #}
34-
def sample_{{ frags.render_method_name(sample.rpc)|trim }}({{ frags.print_input_params(sample.request)|trim }}):
34+
{% if sample.transport == "grpc-async" %}async {% endif %}def sample_{{ frags.render_method_name(sample.rpc)|trim }}({{ frags.print_input_params(sample.request)|trim }}):
3535
"""{{ sample.description }}"""
3636

3737
{{ frags.render_client_setup(sample.module_name, sample.client_name)|indent }}
3838
{{ frags.render_request_setup(sample.request, sample.module_name, sample.request_type)|indent }}
39-
{% with method_call = frags.render_method_call(sample, calling_form, calling_form_enum) %}
40-
{{ frags.render_calling_form(method_call, calling_form, calling_form_enum, sample.response)|indent -}}
39+
{% with method_call = frags.render_method_call(sample, calling_form, calling_form_enum, sample.transport) %}
40+
{{ frags.render_calling_form(method_call, calling_form, calling_form_enum, sample.transport, sample.response)|indent -}}
4141
{% endwith %}
4242

4343
# [END {{ sample.id }}]

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,19 @@ def test_{{ service.client_name|snake_case }}_from_service_account_info(client_c
113113
{% endif %}
114114

115115

116+
@pytest.mark.parametrize("client_class", [
117+
{{ service.client_name }},
118+
{% if 'grpc' in opts.transport %}
119+
{{ service.async_client_name }},
120+
{% endif %}
121+
])
122+
def test_{{ service.client_name|snake_case }}_service_account_always_use_jwt(client_class):
123+
with mock.patch.object(service_account.Credentials, 'with_always_use_jwt_access', create=True) as use_jwt:
124+
creds = service_account.Credentials(None, None, None)
125+
client = client_class(credentials=creds)
126+
use_jwt.assert_called_with(True)
127+
128+
116129
@pytest.mark.parametrize("client_class", [
117130
{{ service.client_name }},
118131
{% if 'grpc' in opts.transport %}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from google.api_core import retry as retries # type: ignore
2626
from google.api_core import operations_v1 # type: ignore
2727
from google.auth import credentials as ga_credentials # type: ignore
28+
from google.oauth2 import service_account # type: ignore
2829

2930
from google.cloud.asset_v1.types import asset_service
3031
from google.longrunning import operations_pb2 # type: ignore
@@ -65,6 +66,7 @@ def __init__(
6566
scopes: Optional[Sequence[str]] = None,
6667
quota_project_id: Optional[str] = None,
6768
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
69+
always_use_jwt_access: Optional[bool] = False,
6870
**kwargs,
6971
) -> None:
7072
"""Instantiate the transport.
@@ -88,6 +90,8 @@ def __init__(
8890
API requests. If ``None``, then default info will be used.
8991
Generally, you only need to set this if you're developing
9092
your own client library.
93+
always_use_jwt_access (Optional[bool]): Whether self signed JWT should
94+
be used for service account credentials.
9195
"""
9296
# Save the hostname. Default to port 443 (HTTPS) if none is specified.
9397
if ':' not in host:
@@ -114,6 +118,10 @@ def __init__(
114118
elif credentials is None:
115119
credentials, _ = google.auth.default(**scopes_kwargs, quota_project_id=quota_project_id)
116120

121+
# If the credentials is service account credentials, then always try to use self signed JWT.
122+
if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"):
123+
credentials = credentials.with_always_use_jwt_access(True)
124+
117125
# Save the credentials.
118126
self._credentials = credentials
119127

0 commit comments

Comments
 (0)