Skip to content

Commit a82f289

Browse files
authored
feat: First batch of AIO integration (#26)
This change includes: * Nox configuration support for AsynciO unit tests * No pre release gRPC Python required * AsyncIO retry module * AsyncIO config parsing module * Exception parsing patch * Corresponding unit test cases
1 parent 335c109 commit a82f289

File tree

10 files changed

+863
-9
lines changed

10 files changed

+863
-9
lines changed

docs/retry.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,10 @@ Retry
44
.. automodule:: google.api_core.retry
55
:members:
66
:show-inheritance:
7+
8+
Retry in AsyncIO
9+
----------------
10+
11+
.. automodule:: google.api_core.retry_async
12+
:members:
13+
:show-inheritance:

google/api_core/exceptions.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,10 @@ def from_grpc_status(status_code, message, **kwargs):
444444
return error
445445

446446

447+
def _is_informative_grpc_error(rpc_exc):
448+
return hasattr(rpc_exc, "code") and hasattr(rpc_exc, "details")
449+
450+
447451
def from_grpc_error(rpc_exc):
448452
"""Create a :class:`GoogleAPICallError` from a :class:`grpc.RpcError`.
449453
@@ -454,7 +458,9 @@ def from_grpc_error(rpc_exc):
454458
GoogleAPICallError: An instance of the appropriate subclass of
455459
:class:`GoogleAPICallError`.
456460
"""
457-
if isinstance(rpc_exc, grpc.Call):
461+
# NOTE(lidiz) All gRPC error shares the parent class grpc.RpcError.
462+
# However, check for grpc.RpcError breaks backward compatibility.
463+
if isinstance(rpc_exc, grpc.Call) or _is_informative_grpc_error(rpc_exc):
458464
return from_grpc_status(
459465
rpc_exc.code(), rpc_exc.details(), errors=(rpc_exc,), response=rpc_exc
460466
)

google/api_core/gapic_v1/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import sys
16+
1517
from google.api_core.gapic_v1 import client_info
1618
from google.api_core.gapic_v1 import config
1719
from google.api_core.gapic_v1 import method
1820
from google.api_core.gapic_v1 import routing_header
1921

2022
__all__ = ["client_info", "config", "method", "routing_header"]
23+
24+
if sys.version_info >= (3, 6):
25+
from google.api_core.gapic_v1 import config_async # noqa: F401
26+
__all__.append("config_async")

google/api_core/gapic_v1/config.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def _exception_class_for_grpc_status_name(name):
4545
return exceptions.exception_class_for_grpc_status(getattr(grpc.StatusCode, name))
4646

4747

48-
def _retry_from_retry_config(retry_params, retry_codes):
48+
def _retry_from_retry_config(retry_params, retry_codes, retry_impl=retry.Retry):
4949
"""Creates a Retry object given a gapic retry configuration.
5050
5151
Args:
@@ -70,7 +70,7 @@ def _retry_from_retry_config(retry_params, retry_codes):
7070
exception_classes = [
7171
_exception_class_for_grpc_status_name(code) for code in retry_codes
7272
]
73-
return retry.Retry(
73+
return retry_impl(
7474
retry.if_exception_type(*exception_classes),
7575
initial=(retry_params["initial_retry_delay_millis"] / _MILLIS_PER_SECOND),
7676
maximum=(retry_params["max_retry_delay_millis"] / _MILLIS_PER_SECOND),
@@ -110,7 +110,7 @@ def _timeout_from_retry_config(retry_params):
110110
MethodConfig = collections.namedtuple("MethodConfig", ["retry", "timeout"])
111111

112112

113-
def parse_method_configs(interface_config):
113+
def parse_method_configs(interface_config, retry_impl=retry.Retry):
114114
"""Creates default retry and timeout objects for each method in a gapic
115115
interface config.
116116
@@ -120,6 +120,8 @@ def parse_method_configs(interface_config):
120120
an interface named ``google.example.v1.ExampleService`` you would
121121
pass in just that interface's configuration, for example
122122
``gapic_config['interfaces']['google.example.v1.ExampleService']``.
123+
retry_impl (Callable): The constructor that creates a retry decorator
124+
that will be applied to the method based on method configs.
123125
124126
Returns:
125127
Mapping[str, MethodConfig]: A mapping of RPC method names to their
@@ -151,7 +153,7 @@ def parse_method_configs(interface_config):
151153
if retry_params_name is not None:
152154
retry_params = retry_params_map[retry_params_name]
153155
retry_ = _retry_from_retry_config(
154-
retry_params, retry_codes_map[method_params["retry_codes_name"]]
156+
retry_params, retry_codes_map[method_params["retry_codes_name"]], retry_impl
155157
)
156158
timeout_ = _timeout_from_retry_config(retry_params)
157159

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2020 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""AsyncIO helpers for loading gapic configuration data.
15+
16+
The Google API generator creates supplementary configuration for each RPC
17+
method to tell the client library how to deal with retries and timeouts.
18+
"""
19+
20+
from google.api_core import retry_async
21+
from google.api_core.gapic_v1 import config
22+
from google.api_core.gapic_v1.config import MethodConfig # noqa: F401
23+
24+
25+
def parse_method_configs(interface_config):
26+
"""Creates default retry and timeout objects for each method in a gapic
27+
interface config with AsyncIO semantics.
28+
29+
Args:
30+
interface_config (Mapping): The interface config section of the full
31+
gapic library config. For example, If the full configuration has
32+
an interface named ``google.example.v1.ExampleService`` you would
33+
pass in just that interface's configuration, for example
34+
``gapic_config['interfaces']['google.example.v1.ExampleService']``.
35+
36+
Returns:
37+
Mapping[str, MethodConfig]: A mapping of RPC method names to their
38+
configuration.
39+
"""
40+
return config.parse_method_configs(
41+
interface_config,
42+
retry_impl=retry_async.AsyncRetry)

0 commit comments

Comments
 (0)