Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1c4b0d0

Browse filesBrowse files
feat: Add support for creating exceptions from an asynchronous response (#688)
* feat: add suport for mapping exceptions to rest callables * avoid wrapping errors for rest callable * fix lint issues * add test coverage * address PR comments * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix lint issues * fix for none type method --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 082bce2 commit 1c4b0d0
Copy full SHA for 1c4b0d0

File tree

3 files changed

+61
-16
lines changed
Filter options

3 files changed

+61
-16
lines changed

‎google/api_core/exceptions.py

Copy file name to clipboardExpand all lines: google/api_core/exceptions.py
+45-15Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from __future__ import unicode_literals
2323

2424
import http.client
25-
from typing import Dict
25+
from typing import Optional, Dict
2626
from typing import Union
2727
import warnings
2828

@@ -476,22 +476,37 @@ def from_http_status(status_code, message, **kwargs):
476476
return error
477477

478478

479-
def from_http_response(response):
480-
"""Create a :class:`GoogleAPICallError` from a :class:`requests.Response`.
479+
def _format_rest_error_message(error, method, url):
480+
method = method.upper() if method else None
481+
message = "{method} {url}: {error}".format(
482+
method=method,
483+
url=url,
484+
error=error,
485+
)
486+
return message
487+
488+
489+
# NOTE: We're moving away from `from_http_status` because it expects an aiohttp response compared
490+
# to `format_http_response_error` which expects a more abstract response from google.auth and is
491+
# compatible with both sync and async response types.
492+
# TODO(https://github.com/googleapis/python-api-core/issues/691): Add type hint for response.
493+
def format_http_response_error(
494+
response, method: str, url: str, payload: Optional[Dict] = None
495+
):
496+
"""Create a :class:`GoogleAPICallError` from a google auth rest response.
481497
482498
Args:
483-
response (requests.Response): The HTTP response.
499+
response Union[google.auth.transport.Response, google.auth.aio.transport.Response]: The HTTP response.
500+
method Optional(str): The HTTP request method.
501+
url Optional(str): The HTTP request url.
502+
payload Optional(dict): The HTTP response payload. If not passed in, it is read from response for a response type of google.auth.transport.Response.
484503
485504
Returns:
486505
GoogleAPICallError: An instance of the appropriate subclass of
487506
:class:`GoogleAPICallError`, with the message and errors populated
488507
from the response.
489508
"""
490-
try:
491-
payload = response.json()
492-
except ValueError:
493-
payload = {"error": {"message": response.text or "unknown error"}}
494-
509+
payload = {} if not payload else payload
495510
error_message = payload.get("error", {}).get("message", "unknown error")
496511
errors = payload.get("error", {}).get("errors", ())
497512
# In JSON, details are already formatted in developer-friendly way.
@@ -504,12 +519,7 @@ def from_http_response(response):
504519
)
505520
)
506521
error_info = error_info[0] if error_info else None
507-
508-
message = "{method} {url}: {error}".format(
509-
method=response.request.method,
510-
url=response.request.url,
511-
error=error_message,
512-
)
522+
message = _format_rest_error_message(error_message, method, url)
513523

514524
exception = from_http_status(
515525
response.status_code,
@@ -522,6 +532,26 @@ def from_http_response(response):
522532
return exception
523533

524534

535+
def from_http_response(response):
536+
"""Create a :class:`GoogleAPICallError` from a :class:`requests.Response`.
537+
538+
Args:
539+
response (requests.Response): The HTTP response.
540+
541+
Returns:
542+
GoogleAPICallError: An instance of the appropriate subclass of
543+
:class:`GoogleAPICallError`, with the message and errors populated
544+
from the response.
545+
"""
546+
try:
547+
payload = response.json()
548+
except ValueError:
549+
payload = {"error": {"message": response.text or "unknown error"}}
550+
return format_http_response_error(
551+
response, response.request.method, response.request.url, payload
552+
)
553+
554+
525555
def exception_class_for_grpc_status(status_code):
526556
"""Return the exception class for a specific :class:`grpc.StatusCode`.
527557

‎google/api_core/gapic_v1/method_async.py

Copy file name to clipboardExpand all lines: google/api_core/gapic_v1/method_async.py
+5-1Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@
2525
from google.api_core.gapic_v1.method import DEFAULT # noqa: F401
2626
from google.api_core.gapic_v1.method import USE_DEFAULT_METADATA # noqa: F401
2727

28+
_DEFAULT_ASYNC_TRANSPORT_KIND = "grpc_asyncio"
29+
2830

2931
def wrap_method(
3032
func,
3133
default_retry=None,
3234
default_timeout=None,
3335
default_compression=None,
3436
client_info=client_info.DEFAULT_CLIENT_INFO,
37+
kind=_DEFAULT_ASYNC_TRANSPORT_KIND,
3538
):
3639
"""Wrap an async RPC method with common behavior.
3740
@@ -40,7 +43,8 @@ def wrap_method(
4043
and ``compression`` arguments and applies the common error mapping,
4144
retry, timeout, metadata, and compression behavior to the low-level RPC method.
4245
"""
43-
func = grpc_helpers_async.wrap_errors(func)
46+
if kind == _DEFAULT_ASYNC_TRANSPORT_KIND:
47+
func = grpc_helpers_async.wrap_errors(func)
4448

4549
metadata = [client_info.to_grpc_metadata()] if client_info is not None else None
4650

‎tests/asyncio/gapic/test_method_async.py

Copy file name to clipboardExpand all lines: tests/asyncio/gapic/test_method_async.py
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,14 @@ async def test_wrap_method_with_overriding_timeout_as_a_number():
252252

253253
assert result == 42
254254
method.assert_called_once_with(timeout=22, metadata=mock.ANY)
255+
256+
257+
@pytest.mark.asyncio
258+
async def test_wrap_method_without_wrap_errors():
259+
fake_call = mock.AsyncMock()
260+
261+
wrapped_method = gapic_v1.method_async.wrap_method(fake_call, kind="rest")
262+
with mock.patch("google.api_core.grpc_helpers_async.wrap_errors") as method:
263+
await wrapped_method()
264+
265+
method.assert_not_called()

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.