google-api-core 2.19.2__tar.gz → 2.20.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {google_api_core-2.19.2/google_api_core.egg-info → google_api_core-2.20.0}/PKG-INFO +1 -1
- google_api_core-2.19.2/google/api_core/rest_streaming.py → google_api_core-2.20.0/google/api_core/_rest_streaming_base.py +18 -32
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/exceptions.py +51 -15
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/gapic_v1/method_async.py +5 -1
- google_api_core-2.20.0/google/api_core/rest_streaming.py +66 -0
- google_api_core-2.20.0/google/api_core/rest_streaming_async.py +83 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/version.py +1 -1
- {google_api_core-2.19.2 → google_api_core-2.20.0/google_api_core.egg-info}/PKG-INFO +1 -1
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google_api_core.egg-info/SOURCES.txt +4 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/gapic/test_method_async.py +11 -0
- google_api_core-2.20.0/tests/asyncio/test_rest_streaming_async.py +378 -0
- google_api_core-2.20.0/tests/helpers.py +71 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_rest_streaming.py +7 -52
- {google_api_core-2.19.2 → google_api_core-2.20.0}/LICENSE +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/MANIFEST.in +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/README.rst +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/bidi.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/client_info.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/client_options.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/datetime_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/extended_operation.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/future/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/future/_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/future/async_future.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/future/base.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/future/polling.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/gapic_v1/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/gapic_v1/client_info.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/gapic_v1/config.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/gapic_v1/config_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/gapic_v1/method.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/gapic_v1/routing_header.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/general_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/grpc_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/grpc_helpers_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/iam.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operation.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operation_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operations_v1/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operations_v1/abstract_operations_client.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operations_v1/operations_async_client.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operations_v1/operations_client.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operations_v1/operations_client_config.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operations_v1/pagers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operations_v1/transports/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operations_v1/transports/base.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/operations_v1/transports/rest.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/page_iterator.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/page_iterator_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/path_template.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/protobuf_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/py.typed +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/rest_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/retry/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/retry/retry_base.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/retry/retry_streaming.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/retry/retry_streaming_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/retry/retry_unary.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/retry/retry_unary_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/retry_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/timeout.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/universe.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google/api_core/version_header.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google_api_core.egg-info/dependency_links.txt +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google_api_core.egg-info/not-zip-safe +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google_api_core.egg-info/requires.txt +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/google_api_core.egg-info/top_level.txt +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/setup.cfg +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/setup.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/future/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/future/test_async_future.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/gapic/test_config_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/operations_v1/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/operations_v1/test_operations_async_client.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/retry/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/retry/test_retry_streaming_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/retry/test_retry_unary_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/test_grpc_helpers_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/test_operation_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/asyncio/test_page_iterator_async.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/future/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/future/test__helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/future/test_polling.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/gapic/test_client_info.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/gapic/test_config.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/gapic/test_method.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/gapic/test_routing_header.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/operations_v1/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/operations_v1/test_operations_client.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/operations_v1/test_operations_rest_client.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/retry/__init__.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/retry/test_retry_base.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/retry/test_retry_imports.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/retry/test_retry_streaming.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/retry/test_retry_unary.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_bidi.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_client_info.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_client_options.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_datetime_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_exceptions.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_extended_operation.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_grpc_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_iam.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_operation.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_packaging.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_page_iterator.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_path_template.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_protobuf_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_rest_helpers.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_timeout.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_universe.py +0 -0
- {google_api_core-2.19.2 → google_api_core-2.20.0}/tests/unit/test_version_header.py +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -17,18 +17,17 @@
|
|
|
17
17
|
from collections import deque
|
|
18
18
|
import string
|
|
19
19
|
from typing import Deque, Union
|
|
20
|
+
import types
|
|
20
21
|
|
|
21
22
|
import proto
|
|
22
|
-
import requests
|
|
23
23
|
import google.protobuf.message
|
|
24
24
|
from google.protobuf.json_format import Parse
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
class
|
|
28
|
-
"""Iterator over REST API responses.
|
|
27
|
+
class BaseResponseIterator:
|
|
28
|
+
"""Base Iterator over REST API responses. This class should not be used directly.
|
|
29
29
|
|
|
30
30
|
Args:
|
|
31
|
-
response (requests.Response): An API response object.
|
|
32
31
|
response_message_cls (Union[proto.Message, google.protobuf.message.Message]): A response
|
|
33
32
|
class expected to be returned from an API.
|
|
34
33
|
|
|
@@ -38,13 +37,9 @@ class ResponseIterator:
|
|
|
38
37
|
|
|
39
38
|
def __init__(
|
|
40
39
|
self,
|
|
41
|
-
response: requests.Response,
|
|
42
40
|
response_message_cls: Union[proto.Message, google.protobuf.message.Message],
|
|
43
41
|
):
|
|
44
|
-
self._response = response
|
|
45
42
|
self._response_message_cls = response_message_cls
|
|
46
|
-
# Inner iterator over HTTP response's content.
|
|
47
|
-
self._response_itr = self._response.iter_content(decode_unicode=True)
|
|
48
43
|
# Contains a list of JSON responses ready to be sent to user.
|
|
49
44
|
self._ready_objs: Deque[str] = deque()
|
|
50
45
|
# Current JSON response being built.
|
|
@@ -57,9 +52,7 @@ class ResponseIterator:
|
|
|
57
52
|
# Whether an escape symbol "\" was encountered.
|
|
58
53
|
self._escape_next = False
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
"""Cancel existing streaming operation."""
|
|
62
|
-
self._response.close()
|
|
55
|
+
self._grab = types.MethodType(self._create_grab(), self)
|
|
63
56
|
|
|
64
57
|
def _process_chunk(self, chunk: str):
|
|
65
58
|
if self._level == 0:
|
|
@@ -104,29 +97,22 @@ class ResponseIterator:
|
|
|
104
97
|
self._obj += char
|
|
105
98
|
self._escape_next = not self._escape_next if char == "\\" else False
|
|
106
99
|
|
|
107
|
-
def
|
|
108
|
-
while not self._ready_objs:
|
|
109
|
-
try:
|
|
110
|
-
chunk = next(self._response_itr)
|
|
111
|
-
self._process_chunk(chunk)
|
|
112
|
-
except StopIteration as e:
|
|
113
|
-
if self._level > 0:
|
|
114
|
-
raise ValueError("Unfinished stream: %s" % self._obj)
|
|
115
|
-
raise e
|
|
116
|
-
return self._grab()
|
|
117
|
-
|
|
118
|
-
def _grab(self):
|
|
119
|
-
# Add extra quotes to make json.loads happy.
|
|
100
|
+
def _create_grab(self):
|
|
120
101
|
if issubclass(self._response_message_cls, proto.Message):
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
102
|
+
|
|
103
|
+
def grab(this):
|
|
104
|
+
return this._response_message_cls.from_json(
|
|
105
|
+
this._ready_objs.popleft(), ignore_unknown_fields=True
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return grab
|
|
124
109
|
elif issubclass(self._response_message_cls, google.protobuf.message.Message):
|
|
125
|
-
|
|
110
|
+
|
|
111
|
+
def grab(this):
|
|
112
|
+
return Parse(this._ready_objs.popleft(), this._response_message_cls())
|
|
113
|
+
|
|
114
|
+
return grab
|
|
126
115
|
else:
|
|
127
116
|
raise ValueError(
|
|
128
117
|
"Response message class must be a subclass of proto.Message or google.protobuf.message.Message."
|
|
129
118
|
)
|
|
130
|
-
|
|
131
|
-
def __iter__(self):
|
|
132
|
-
return self
|
|
@@ -22,7 +22,7 @@ from __future__ import absolute_import
|
|
|
22
22
|
from __future__ import unicode_literals
|
|
23
23
|
|
|
24
24
|
import http.client
|
|
25
|
-
from typing import Dict
|
|
25
|
+
from typing import Optional, Dict
|
|
26
26
|
from typing import Union
|
|
27
27
|
import warnings
|
|
28
28
|
|
|
@@ -442,6 +442,12 @@ class DeadlineExceeded(GatewayTimeout):
|
|
|
442
442
|
grpc_status_code = grpc.StatusCode.DEADLINE_EXCEEDED if grpc is not None else None
|
|
443
443
|
|
|
444
444
|
|
|
445
|
+
class AsyncRestUnsupportedParameterError(NotImplementedError):
|
|
446
|
+
"""Raised when an unsupported parameter is configured against async rest transport."""
|
|
447
|
+
|
|
448
|
+
pass
|
|
449
|
+
|
|
450
|
+
|
|
445
451
|
def exception_class_for_http_status(status_code):
|
|
446
452
|
"""Return the exception class for a specific HTTP status code.
|
|
447
453
|
|
|
@@ -476,22 +482,37 @@ def from_http_status(status_code, message, **kwargs):
|
|
|
476
482
|
return error
|
|
477
483
|
|
|
478
484
|
|
|
479
|
-
def
|
|
480
|
-
|
|
485
|
+
def _format_rest_error_message(error, method, url):
|
|
486
|
+
method = method.upper() if method else None
|
|
487
|
+
message = "{method} {url}: {error}".format(
|
|
488
|
+
method=method,
|
|
489
|
+
url=url,
|
|
490
|
+
error=error,
|
|
491
|
+
)
|
|
492
|
+
return message
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
# NOTE: We're moving away from `from_http_status` because it expects an aiohttp response compared
|
|
496
|
+
# to `format_http_response_error` which expects a more abstract response from google.auth and is
|
|
497
|
+
# compatible with both sync and async response types.
|
|
498
|
+
# TODO(https://github.com/googleapis/python-api-core/issues/691): Add type hint for response.
|
|
499
|
+
def format_http_response_error(
|
|
500
|
+
response, method: str, url: str, payload: Optional[Dict] = None
|
|
501
|
+
):
|
|
502
|
+
"""Create a :class:`GoogleAPICallError` from a google auth rest response.
|
|
481
503
|
|
|
482
504
|
Args:
|
|
483
|
-
response
|
|
505
|
+
response Union[google.auth.transport.Response, google.auth.aio.transport.Response]: The HTTP response.
|
|
506
|
+
method Optional(str): The HTTP request method.
|
|
507
|
+
url Optional(str): The HTTP request url.
|
|
508
|
+
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.
|
|
484
509
|
|
|
485
510
|
Returns:
|
|
486
511
|
GoogleAPICallError: An instance of the appropriate subclass of
|
|
487
512
|
:class:`GoogleAPICallError`, with the message and errors populated
|
|
488
513
|
from the response.
|
|
489
514
|
"""
|
|
490
|
-
|
|
491
|
-
payload = response.json()
|
|
492
|
-
except ValueError:
|
|
493
|
-
payload = {"error": {"message": response.text or "unknown error"}}
|
|
494
|
-
|
|
515
|
+
payload = {} if not payload else payload
|
|
495
516
|
error_message = payload.get("error", {}).get("message", "unknown error")
|
|
496
517
|
errors = payload.get("error", {}).get("errors", ())
|
|
497
518
|
# In JSON, details are already formatted in developer-friendly way.
|
|
@@ -504,12 +525,7 @@ def from_http_response(response):
|
|
|
504
525
|
)
|
|
505
526
|
)
|
|
506
527
|
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
|
-
)
|
|
528
|
+
message = _format_rest_error_message(error_message, method, url)
|
|
513
529
|
|
|
514
530
|
exception = from_http_status(
|
|
515
531
|
response.status_code,
|
|
@@ -522,6 +538,26 @@ def from_http_response(response):
|
|
|
522
538
|
return exception
|
|
523
539
|
|
|
524
540
|
|
|
541
|
+
def from_http_response(response):
|
|
542
|
+
"""Create a :class:`GoogleAPICallError` from a :class:`requests.Response`.
|
|
543
|
+
|
|
544
|
+
Args:
|
|
545
|
+
response (requests.Response): The HTTP response.
|
|
546
|
+
|
|
547
|
+
Returns:
|
|
548
|
+
GoogleAPICallError: An instance of the appropriate subclass of
|
|
549
|
+
:class:`GoogleAPICallError`, with the message and errors populated
|
|
550
|
+
from the response.
|
|
551
|
+
"""
|
|
552
|
+
try:
|
|
553
|
+
payload = response.json()
|
|
554
|
+
except ValueError:
|
|
555
|
+
payload = {"error": {"message": response.text or "unknown error"}}
|
|
556
|
+
return format_http_response_error(
|
|
557
|
+
response, response.request.method, response.request.url, payload
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
|
|
525
561
|
def exception_class_for_grpc_status(status_code):
|
|
526
562
|
"""Return the exception class for a specific :class:`grpc.StatusCode`.
|
|
527
563
|
|
|
@@ -25,6 +25,8 @@ from google.api_core.gapic_v1.method import _GapicCallable
|
|
|
25
25
|
from google.api_core.gapic_v1.method import DEFAULT # noqa: F401
|
|
26
26
|
from google.api_core.gapic_v1.method import USE_DEFAULT_METADATA # noqa: F401
|
|
27
27
|
|
|
28
|
+
_DEFAULT_ASYNC_TRANSPORT_KIND = "grpc_asyncio"
|
|
29
|
+
|
|
28
30
|
|
|
29
31
|
def wrap_method(
|
|
30
32
|
func,
|
|
@@ -32,6 +34,7 @@ def wrap_method(
|
|
|
32
34
|
default_timeout=None,
|
|
33
35
|
default_compression=None,
|
|
34
36
|
client_info=client_info.DEFAULT_CLIENT_INFO,
|
|
37
|
+
kind=_DEFAULT_ASYNC_TRANSPORT_KIND,
|
|
35
38
|
):
|
|
36
39
|
"""Wrap an async RPC method with common behavior.
|
|
37
40
|
|
|
@@ -40,7 +43,8 @@ def wrap_method(
|
|
|
40
43
|
and ``compression`` arguments and applies the common error mapping,
|
|
41
44
|
retry, timeout, metadata, and compression behavior to the low-level RPC method.
|
|
42
45
|
"""
|
|
43
|
-
|
|
46
|
+
if kind == _DEFAULT_ASYNC_TRANSPORT_KIND:
|
|
47
|
+
func = grpc_helpers_async.wrap_errors(func)
|
|
44
48
|
|
|
45
49
|
metadata = [client_info.to_grpc_metadata()] if client_info is not None else None
|
|
46
50
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Copyright 2021 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
|
+
|
|
15
|
+
"""Helpers for server-side streaming in REST."""
|
|
16
|
+
|
|
17
|
+
from typing import Union
|
|
18
|
+
|
|
19
|
+
import proto
|
|
20
|
+
import requests
|
|
21
|
+
import google.protobuf.message
|
|
22
|
+
from google.api_core._rest_streaming_base import BaseResponseIterator
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ResponseIterator(BaseResponseIterator):
|
|
26
|
+
"""Iterator over REST API responses.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
response (requests.Response): An API response object.
|
|
30
|
+
response_message_cls (Union[proto.Message, google.protobuf.message.Message]): A response
|
|
31
|
+
class expected to be returned from an API.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
ValueError:
|
|
35
|
+
- If `response_message_cls` is not a subclass of `proto.Message` or `google.protobuf.message.Message`.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
response: requests.Response,
|
|
41
|
+
response_message_cls: Union[proto.Message, google.protobuf.message.Message],
|
|
42
|
+
):
|
|
43
|
+
self._response = response
|
|
44
|
+
# Inner iterator over HTTP response's content.
|
|
45
|
+
self._response_itr = self._response.iter_content(decode_unicode=True)
|
|
46
|
+
super(ResponseIterator, self).__init__(
|
|
47
|
+
response_message_cls=response_message_cls
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def cancel(self):
|
|
51
|
+
"""Cancel existing streaming operation."""
|
|
52
|
+
self._response.close()
|
|
53
|
+
|
|
54
|
+
def __next__(self):
|
|
55
|
+
while not self._ready_objs:
|
|
56
|
+
try:
|
|
57
|
+
chunk = next(self._response_itr)
|
|
58
|
+
self._process_chunk(chunk)
|
|
59
|
+
except StopIteration as e:
|
|
60
|
+
if self._level > 0:
|
|
61
|
+
raise ValueError("Unfinished stream: %s" % self._obj)
|
|
62
|
+
raise e
|
|
63
|
+
return self._grab()
|
|
64
|
+
|
|
65
|
+
def __iter__(self):
|
|
66
|
+
return self
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Copyright 2024 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
|
+
|
|
15
|
+
"""Helpers for asynchronous server-side streaming in REST."""
|
|
16
|
+
|
|
17
|
+
from typing import Union
|
|
18
|
+
|
|
19
|
+
import proto
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import google.auth.aio.transport
|
|
23
|
+
except ImportError as e: # pragma: NO COVER
|
|
24
|
+
raise ImportError(
|
|
25
|
+
"google-auth>=2.35.0 is required to use asynchronous rest streaming."
|
|
26
|
+
) from e
|
|
27
|
+
|
|
28
|
+
import google.protobuf.message
|
|
29
|
+
from google.api_core._rest_streaming_base import BaseResponseIterator
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AsyncResponseIterator(BaseResponseIterator):
|
|
33
|
+
"""Asynchronous Iterator over REST API responses.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
response (google.auth.aio.transport.Response): An API response object.
|
|
37
|
+
response_message_cls (Union[proto.Message, google.protobuf.message.Message]): A response
|
|
38
|
+
class expected to be returned from an API.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError:
|
|
42
|
+
- If `response_message_cls` is not a subclass of `proto.Message` or `google.protobuf.message.Message`.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
response: google.auth.aio.transport.Response,
|
|
48
|
+
response_message_cls: Union[proto.Message, google.protobuf.message.Message],
|
|
49
|
+
):
|
|
50
|
+
self._response = response
|
|
51
|
+
self._chunk_size = 1024
|
|
52
|
+
self._response_itr = self._response.content().__aiter__()
|
|
53
|
+
super(AsyncResponseIterator, self).__init__(
|
|
54
|
+
response_message_cls=response_message_cls
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
async def __aenter__(self):
|
|
58
|
+
return self
|
|
59
|
+
|
|
60
|
+
async def cancel(self):
|
|
61
|
+
"""Cancel existing streaming operation."""
|
|
62
|
+
await self._response.close()
|
|
63
|
+
|
|
64
|
+
async def __anext__(self):
|
|
65
|
+
while not self._ready_objs:
|
|
66
|
+
try:
|
|
67
|
+
chunk = await self._response_itr.__anext__()
|
|
68
|
+
chunk = chunk.decode("utf-8")
|
|
69
|
+
self._process_chunk(chunk)
|
|
70
|
+
except StopAsyncIteration as e:
|
|
71
|
+
if self._level > 0:
|
|
72
|
+
raise ValueError("i Unfinished stream: %s" % self._obj)
|
|
73
|
+
raise e
|
|
74
|
+
except ValueError as e:
|
|
75
|
+
raise e
|
|
76
|
+
return self._grab()
|
|
77
|
+
|
|
78
|
+
def __aiter__(self):
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
async def __aexit__(self, exc_type, exc, tb):
|
|
82
|
+
"""Cancel existing async streaming operation."""
|
|
83
|
+
await self._response.close()
|
|
@@ -4,6 +4,7 @@ README.rst
|
|
|
4
4
|
setup.cfg
|
|
5
5
|
setup.py
|
|
6
6
|
google/api_core/__init__.py
|
|
7
|
+
google/api_core/_rest_streaming_base.py
|
|
7
8
|
google/api_core/bidi.py
|
|
8
9
|
google/api_core/client_info.py
|
|
9
10
|
google/api_core/client_options.py
|
|
@@ -23,6 +24,7 @@ google/api_core/protobuf_helpers.py
|
|
|
23
24
|
google/api_core/py.typed
|
|
24
25
|
google/api_core/rest_helpers.py
|
|
25
26
|
google/api_core/rest_streaming.py
|
|
27
|
+
google/api_core/rest_streaming_async.py
|
|
26
28
|
google/api_core/retry_async.py
|
|
27
29
|
google/api_core/timeout.py
|
|
28
30
|
google/api_core/universe.py
|
|
@@ -62,10 +64,12 @@ google_api_core.egg-info/not-zip-safe
|
|
|
62
64
|
google_api_core.egg-info/requires.txt
|
|
63
65
|
google_api_core.egg-info/top_level.txt
|
|
64
66
|
tests/__init__.py
|
|
67
|
+
tests/helpers.py
|
|
65
68
|
tests/asyncio/__init__.py
|
|
66
69
|
tests/asyncio/test_grpc_helpers_async.py
|
|
67
70
|
tests/asyncio/test_operation_async.py
|
|
68
71
|
tests/asyncio/test_page_iterator_async.py
|
|
72
|
+
tests/asyncio/test_rest_streaming_async.py
|
|
69
73
|
tests/asyncio/future/__init__.py
|
|
70
74
|
tests/asyncio/future/test_async_future.py
|
|
71
75
|
tests/asyncio/gapic/test_config_async.py
|
|
@@ -252,3 +252,14 @@ async def test_wrap_method_with_overriding_timeout_as_a_number():
|
|
|
252
252
|
|
|
253
253
|
assert result == 42
|
|
254
254
|
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()
|