Skip to content

Commit

Permalink
Merge branch 'master' into vchudnov-showcase
Browse files Browse the repository at this point in the history
  • Loading branch information
vchudnov-g authored Jun 16, 2021
2 parents 83ead51 + da119c7 commit 2d4f737
Show file tree
Hide file tree
Showing 163 changed files with 3,521 additions and 161 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## [0.49.0](https://www.github.com/googleapis/gapic-generator-python/compare/v0.48.1...v0.49.0) (2021-06-11)


### Features

* 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))

### [0.48.1](https://www.github.com/googleapis/gapic-generator-python/compare/v0.48.0...v0.48.1) (2021-06-09)


Expand Down
17 changes: 12 additions & 5 deletions gapic/samplegen/samplegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,15 @@ def preprocess_sample(sample, api_schema: api.API, rpc: wrappers.Method):
sample["module_name"] = api_schema.naming.versioned_module_name
sample["module_namespace"] = api_schema.naming.module_namespace

sample["client_name"] = api_schema.services[sample["service"]].client_name
# Assume the gRPC transport if the transport is not specified
sample.setdefault("transport", api.TRANSPORT_GRPC)

if sample["transport"] == api.TRANSPORT_GRPC_ASYNC:
sample["client_name"] = api_schema.services[sample["service"]
].async_client_name
else:
sample["client_name"] = api_schema.services[sample["service"]].client_name

# the type of the request object passed to the rpc e.g, `ListRequest`
sample["request_type"] = rpc.input.ident.name

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

for service_name, service in gapic_metadata.services.items():
api_short_name = api_schema.services[f"{api_schema.naming.proto_package}.{service_name}"].shortname
for transport_type, client in service.clients.items():
if transport_type == "grpc-async":
# TODO(busunkim): Enable generation of async samples
continue
for transport, client in service.clients.items():
transport_type = "async" if transport == api.TRANSPORT_GRPC_ASYNC else "sync"
for rpc_name, method_list in client.rpcs.items():
# Region Tag Format:
# [{START|END} ${apishortname}_generated_${api}_${apiVersion}_${serviceName}_${rpcName}_{sync|async}_${overloadDisambiguation}]
region_tag = f"{api_short_name}_generated_{api_schema.naming.versioned_module_name}_{service_name}_{rpc_name}_{transport_type}"
spec = {
"sample_type": "standalone",
"rpc": rpc_name,
"transport": transport,
"request": [],
# response is populated in `preprocess_sample`
"service": f"{api_schema.naming.proto_package}.{service_name}",
Expand Down
12 changes: 9 additions & 3 deletions gapic/schema/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
from gapic.utils import RESERVED_NAMES


TRANSPORT_GRPC = "grpc"
TRANSPORT_GRPC_ASYNC = "grpc-async"
TRANSPORT_REST = "rest"


@dataclasses.dataclass(frozen=True)
class Proto:
"""A representation of a particular proto file within an API."""
Expand Down Expand Up @@ -414,11 +419,12 @@ def gapic_metadata(self, options: Options) -> gapic_metadata_pb2.GapicMetadata:
# This assumes the options are generated by the class method factory.
transports = []
if "grpc" in options.transport:
transports.append(("grpc", service.client_name))
transports.append(("grpc-async", service.async_client_name))
transports.append((TRANSPORT_GRPC, service.client_name))
transports.append(
(TRANSPORT_GRPC_ASYNC, service.async_client_name))

if "rest" in options.transport:
transports.append(("rest", service.client_name))
transports.append((TRANSPORT_REST, service.client_name))

methods = sorted(service.methods.values(), key=lambda m: m.name)
for tprt, client_name in transports:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ from google.api_core import retry as retries # type: ignore
from google.api_core import operations_v1 # type: ignore
{% endif %}
from google.auth import credentials as ga_credentials # type: ignore
from google.oauth2 import service_account # type: ignore

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

# If the credentials is service account credentials, then always try to use self signed JWT.
if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"):
credentials = credentials.with_always_use_jwt_access(True)

# Save the credentials.
self._credentials = credentials

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
scopes=scopes,
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=True,
)

if not self._grpc_channel:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ class {{ service.grpc_asyncio_transport_name }}({{ service.name }}Transport):
scopes=scopes,
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=True,
)

if not self._grpc_channel:
Expand Down
17 changes: 11 additions & 6 deletions gapic/templates/examples/feature_fragments.j2
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,13 @@ request=request
{% endmacro %}


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

{# Setting up the method invocation is the responsibility of the caller: #}
{# it's just easier to set up client side streaming and other things from outside this macro. #}
{% macro render_calling_form(method_invocation_text, calling_form, calling_form_enum, response_statements ) %}
{% macro render_calling_form(method_invocation_text, calling_form, calling_form_enum, transport, response_statements ) %}
# Make the request
{% if calling_form == calling_form_enum.Request %}
response = {{ method_invocation_text|trim }}
Expand All @@ -228,21 +233,21 @@ response = {{ method_invocation_text|trim }}
{% endif %}
{% elif calling_form == calling_form_enum.RequestPagedAll %}
page_result = {{ method_invocation_text|trim }}
for response in page_result:
{% if transport == "grpc-async" %}async {% endif %}for response in page_result:
{% for statement in response_statements %}
{{ dispatch_statement(statement)|trim }}
{% endfor %}
{% elif calling_form == calling_form_enum.RequestPaged %}
page_result = {{ method_invocation_text|trim }}
for page in page_result.pages():
{% if transport == "grpc-async" %}async {% endif %}for page in page_result.pages():
for response in page:
{% for statement in response_statements %}
{{ dispatch_statement(statement)|trim }}
{% endfor %}
{% elif calling_form in [calling_form_enum.RequestStreamingServer,
calling_form_enum.RequestStreamingBidi] %}
stream = {{ method_invocation_text|trim }}
for response in stream:
{% if transport == "grpc-async" %}async {% endif %}for response in stream:
{% for statement in response_statements %}
{{ dispatch_statement(statement)|trim }}
{% endfor %}
Expand All @@ -251,7 +256,7 @@ operation = {{ method_invocation_text|trim }}

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

response = operation.result()
response = {% if transport == "grpc-async" %}await {% endif %}operation.result()
{% for statement in response_statements %}
{{ dispatch_statement(statement)|trim }}
{% endfor %}
Expand Down
6 changes: 3 additions & 3 deletions gapic/templates/examples/sample.py.j2
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ from {{ sample.module_namespace|join(".") }} import {{ sample.module_name }}


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

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

# [END {{ sample.id }}]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ def test_{{ service.client_name|snake_case }}_from_service_account_info(client_c
{% endif %}


@pytest.mark.parametrize("client_class", [
{{ service.client_name }},
{% if 'grpc' in opts.transport %}
{{ service.async_client_name }},
{% endif %}
])
def test_{{ service.client_name|snake_case }}_service_account_always_use_jwt(client_class):
with mock.patch.object(service_account.Credentials, 'with_always_use_jwt_access', create=True) as use_jwt:
creds = service_account.Credentials(None, None, None)
client = client_class(credentials=creds)
use_jwt.assert_called_with(True)


@pytest.mark.parametrize("client_class", [
{{ service.client_name }},
{% if 'grpc' in opts.transport %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from google.api_core import retry as retries # type: ignore
from google.api_core import operations_v1 # type: ignore
from google.auth import credentials as ga_credentials # type: ignore
from google.oauth2 import service_account # type: ignore

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

# If the credentials is service account credentials, then always try to use self signed JWT.
if always_use_jwt_access and isinstance(credentials, service_account.Credentials) and hasattr(service_account.Credentials, "with_always_use_jwt_access"):
credentials = credentials.with_always_use_jwt_access(True)

# Save the credentials.
self._credentials = credentials

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ def __init__(self, *,
scopes=scopes,
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=True,
)

if not self._grpc_channel:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ def __init__(self, *,
scopes=scopes,
quota_project_id=quota_project_id,
client_info=client_info,
always_use_jwt_access=True,
)

if not self._grpc_channel:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# Copyright 2020 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.
#
# Generated code. DO NOT EDIT!
#
# Snippet for AnalyzeIamPolicy
# NOTE: This snippet has been automatically generated for illustrative purposes only.
# It may require modifications to work in your environment.

# To install the latest published package dependency, execute the following:
# python3 -m pip install google-cloud-asset


# [START cloudasset_generated_asset_v1_AssetService_AnalyzeIamPolicy_async]
from google.cloud import asset_v1


async def sample_analyze_iam_policy():
"""Snippet for analyze_iam_policy"""

# Create a client
client = asset_v1.AssetServiceAsyncClient()

# Initialize request argument(s)
request = asset_v1.AnalyzeIamPolicyRequest(
)

# Make the request
response = await client.analyze_iam_policy(request=request)

# Handle response
print("{}".format(response))

# [END cloudasset_generated_asset_v1_AssetService_AnalyzeIamPolicy_async]
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright 2020 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.
#
# Generated code. DO NOT EDIT!
#
# Snippet for AnalyzeIamPolicyLongrunning
# NOTE: This snippet has been automatically generated for illustrative purposes only.
# It may require modifications to work in your environment.

# To install the latest published package dependency, execute the following:
# python3 -m pip install google-cloud-asset


# [START cloudasset_generated_asset_v1_AssetService_AnalyzeIamPolicyLongrunning_async]
from google.cloud import asset_v1


async def sample_analyze_iam_policy_longrunning():
"""Snippet for analyze_iam_policy_longrunning"""

# Create a client
client = asset_v1.AssetServiceAsyncClient()

# Initialize request argument(s)
request = asset_v1.AnalyzeIamPolicyLongrunningRequest(
)

# Make the request
operation = client.analyze_iam_policy_longrunning(request=request)

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

response = await operation.result()
print("{}".format(response))

# [END cloudasset_generated_asset_v1_AssetService_AnalyzeIamPolicyLongrunning_async]
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
# python3 -m pip install google-cloud-asset


# [START cloudasset_generated_asset_v1_AssetService_AnalyzeIamPolicyLongrunning_grpc]
# [START cloudasset_generated_asset_v1_AssetService_AnalyzeIamPolicyLongrunning_sync]
from google.cloud import asset_v1


Expand All @@ -45,4 +45,4 @@ def sample_analyze_iam_policy_longrunning():
response = operation.result()
print("{}".format(response))

# [END cloudasset_generated_asset_v1_AssetService_AnalyzeIamPolicyLongrunning_grpc]
# [END cloudasset_generated_asset_v1_AssetService_AnalyzeIamPolicyLongrunning_sync]
Loading

0 comments on commit 2d4f737

Please sign in to comment.