-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: implement async rest transport constructor #2123
Changes from 5 commits
d9f8d2b
fcc89de
0bbb9ab
d48c6cf
41ded04
1806b89
0a3e32f
c26f72c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove the following variable (and the condition later in this file) for async rest transport once support for it is GA. #} | ||
{% set rest_async_io_enabled = api.all_library_settings[api.naming.proto_package].python_settings.experimental_features.rest_async_io_enabled %} | ||
{% extends '_base.py.j2' %} | ||
|
||
{% block content %} | ||
|
@@ -62,6 +64,10 @@ 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 #} | ||
{% endif %} | ||
|
||
|
||
|
@@ -79,6 +85,10 @@ 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. #} | ||
ohmayr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{% if rest_async_io_enabled %} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. {# TODO(#2121): Remove this condition when async rest is GA #} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LOL. GitHub shortened and hyperlinked the full URL I intended here. |
||
_transport_registry["rest_asyncio"] = Async{{ service.name }}RestTransport | ||
{% endif %}{# if rest_async_io_enabled #} | ||
{% endif %} | ||
|
||
def get_transport_class(cls, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): Remove the following variable (and the condition later in this file) for async rest transport once support for it is GA. #} | ||
{% set rest_async_io_enabled = api.all_library_settings[api.naming.proto_package].python_settings.experimental_features.rest_async_io_enabled %} | ||
{% extends '_base.py.j2' %} | ||
|
||
{% block content %} | ||
|
||
from 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 (https://github.com/googleapis/gapic-generator-python/issues/2128): Update `rest_version` to include the transport dependency version. #} | ||
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( | ||
gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, | ||
grpc_version=None, | ||
rest_version=None, | ||
) | ||
|
||
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, *, | ||
vchudnov-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
host: str{% if service.host %} = '{{ service.host }}'{% endif %}, | ||
{# 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', | ||
) -> 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 (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 | ||
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 (str): 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 %} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1877,33 +1877,19 @@ 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 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 get_client(service, is_async) %} | ||||||||||||||||||||||||||||||||||
{{-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}}")( | ||||||||||||||||||||||||||||||||||
credentials={{get_credentials(is_async)}} | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed. |
||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||
{% endif %} | ||||||||||||||||||||||||||||||||||
assert transport.kind == transport_name | ||||||||||||||||||||||||||||||||||
{% endmacro %} | ||||||||||||||||||||||||||||||||||
assert transport.kind == "{{ transport_name }}" | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
{% endmacro %} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -174,12 +174,31 @@ 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. | ||
ohmayr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def _add_python_settings(tmp_dir, python_settings): | ||
return f""" | ||
vchudnov-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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) | ||
""" | ||
|
||
# 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] = (), | ||
include_service_yaml=True, | ||
retry_config=True, | ||
rest_async_io_enabled=False | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line should also have a TODO to remove the param once async REST is GA? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed. |
||
): | ||
"""Install the generated library into the session for showcase tests.""" | ||
|
||
|
@@ -220,6 +239,25 @@ def showcase_library( | |
external=True, | ||
silent=True, | ||
) | ||
# 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") | ||
|
||
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}") | ||
vchudnov-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# END TODO section to remove. | ||
if retry_config: | ||
session.run( | ||
"curl", | ||
|
@@ -392,6 +430,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( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also use the same format for these GA-TODOs in the other files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed.