FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: Add support for creating exceptions from an asynchronous response by ohmayr · Pull Request #688 · googleapis/python-api-core · GitHub

This repository was archived by the owner on Feb 23, 2026. It is now read-only.
/ python-api-core Public archive
60 changes: 45 additions & 15 deletions google/api_core/exceptions.py
Comment thread
vchudnov-g marked this conversation as resolved.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from __future__ import unicode_literals

import http.client
from typing import Dict
from typing import Optional, Dict
from typing import Union
import warnings

Expand Down Expand Up @@ -476,22 +476,37 @@ def from_http_status(status_code, message, **kwargs):
return error


def from_http_response(response):
"""Create a :class:`GoogleAPICallError` from a :class:`requests.Response`.
def _format_rest_error_message(error, method, url):
method = method.upper() if method else None
message = "{method} {url}: {error}".format(
method=method,
url=url,
Comment thread
ohmayr marked this conversation as resolved.
error=error,
)
return message


# NOTE: We're moving away from `from_http_status` because it expects an aiohttp response compared
# to `format_http_response_error` which expects a more abstract response from google.auth and is
# compatible with both sync and async response types.
# TODO(https://github.com/googleapis/python-api-core/issues/691): Add type hint for response.
def format_http_response_error(
response, method: str, url: str, payload: Optional[Dict] = None
):
"""Create a :class:`GoogleAPICallError` from a google auth rest response.

Args:
response (requests.Response): The HTTP response.
response Union[google.auth.transport.Response, google.auth.aio.transport.Response]: The HTTP response.
method Optional(str): The HTTP request method.
url Optional(str): The HTTP request url.
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.

Returns:
GoogleAPICallError: An instance of the appropriate subclass of
:class:`GoogleAPICallError`, with the message and errors populated
from the response.
"""
try:
payload = response.json()
except ValueError:
payload = {"error": {"message": response.text or "unknown error"}}

payload = {} if not payload else payload
error_message = payload.get("error", {}).get("message", "unknown error")
errors = payload.get("error", {}).get("errors", ())
# In JSON, details are already formatted in developer-friendly way.
Expand All @@ -504,12 +519,7 @@ def from_http_response(response):
)
)
error_info = error_info[0] if error_info else None

message = "{method} {url}: {error}".format(
method=response.request.method,
url=response.request.url,
error=error_message,
)
message = _format_rest_error_message(error_message, method, url)

exception = from_http_status(
response.status_code,
Expand All @@ -522,6 +532,26 @@ def from_http_response(response):
return exception


def from_http_response(response):
"""Create a :class:`GoogleAPICallError` from a :class:`requests.Response`.

Args:
response (requests.Response): The HTTP response.

Returns:
GoogleAPICallError: An instance of the appropriate subclass of
:class:`GoogleAPICallError`, with the message and errors populated
from the response.
"""
try:
payload = response.json()
except ValueError:
payload = {"error": {"message": response.text or "unknown error"}}
return format_http_response_error(
response, response.request.method, response.request.url, payload
)


def exception_class_for_grpc_status(status_code):
"""Return the exception class for a specific :class:`grpc.StatusCode`.

Expand Down
6 changes: 5 additions & 1 deletion google/api_core/gapic_v1/method_async.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@
from google.api_core.gapic_v1.method import DEFAULT # noqa: F401
from google.api_core.gapic_v1.method import USE_DEFAULT_METADATA # noqa: F401

_DEFAULT_ASYNC_TRANSPORT_KIND = "grpc_asyncio"


def wrap_method(
func,
default_retry=None,
default_timeout=None,
default_compression=None,
client_info=client_info.DEFAULT_CLIENT_INFO,
kind=_DEFAULT_ASYNC_TRANSPORT_KIND,
):
"""Wrap an async RPC method with common behavior.

Expand All @@ -40,7 +43,8 @@ def wrap_method(
and ``compression`` arguments and applies the common error mapping,
retry, timeout, metadata, and compression behavior to the low-level RPC method.
"""
func = grpc_helpers_async.wrap_errors(func)
if kind == _DEFAULT_ASYNC_TRANSPORT_KIND:
func = grpc_helpers_async.wrap_errors(func)

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

Expand Down
11 changes: 11 additions & 0 deletions tests/asyncio/gapic/test_method_async.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,14 @@ async def test_wrap_method_with_overriding_timeout_as_a_number():

assert result == 42
method.assert_called_once_with(timeout=22, metadata=mock.ANY)


@pytest.mark.asyncio
async def test_wrap_method_without_wrap_errors():
fake_call = mock.AsyncMock()

wrapped_method = gapic_v1.method_async.wrap_method(fake_call, kind="rest")
with mock.patch("google.api_core.grpc_helpers_async.wrap_errors") as method:
await wrapped_method()

method.assert_not_called()

Back | FazBrowse Home | New Git URL