Movatterモバイル変換


[0]ホーム

URL:


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

Sign up
Appearance settings

Commit043785e

Browse files
partheaohmayr
andauthored
feat: implement async client for LROs (#707)
* feat: implement `AbstractOperationsAsyncClient` to support long running operations* remove coverage guards* address presubmit failures* fix coverage for cancel operation* tests cleanup* fix incorrect tests* file bugs* add auth import* address PR comments* address PR comments* fix unit tests and address more comments* disable retry parameter* add retry parameter* address PR comments---------Co-authored-by: ohmayr <omairnaveed@ymail.com>Co-authored-by: ohmayr <omairn@google.com>
1 parent3c7e43e commit043785e

11 files changed

+1593
-528
lines changed

‎google/api_core/operations_v1/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414

1515
"""Package for interacting with the google.longrunning.operations meta-API."""
1616

17-
fromgoogle.api_core.operations_v1.abstract_operations_clientimport (
18-
AbstractOperationsClient,
19-
)
17+
fromgoogle.api_core.operations_v1.abstract_operations_clientimportAbstractOperationsClient
2018
fromgoogle.api_core.operations_v1.operations_async_clientimportOperationsAsyncClient
2119
fromgoogle.api_core.operations_v1.operations_clientimportOperationsClient
2220
fromgoogle.api_core.operations_v1.transports.restimportOperationsRestTransport
@@ -29,10 +27,14 @@
2927
]
3028

3129
try:
32-
fromgoogle.api_core.operations_v1.transports.rest_asyncioimportOperationsRestAsyncTransport# noqa: F401
33-
__all__.append("OperationsRestAsyncTransport")
30+
fromgoogle.api_core.operations_v1.transports.rest_asyncioimport (
31+
AsyncOperationsRestTransport,
32+
)
33+
fromgoogle.api_core.operations_v1.operations_rest_client_asyncimportAsyncOperationsRestClient
34+
35+
__all__+= ["AsyncOperationsRestClient","AsyncOperationsRestTransport"]
3436
exceptImportError:
3537
# This import requires the `async_rest` extra.
36-
# Don't raise an exception if `OperationsRestAsyncTransport` cannot be imported
38+
# Don't raise an exception if `AsyncOperationsRestTransport` cannot be imported
3739
# as other transports are still available.
3840
pass
Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2024 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
fromcollectionsimportOrderedDict
17+
importos
18+
importre
19+
fromtypingimportDict,Optional,Type,Union
20+
21+
fromgoogle.api_coreimportclient_optionsasclient_options_lib# type: ignore
22+
fromgoogle.api_coreimportgapic_v1# type: ignore
23+
fromgoogle.api_core.operations_v1.transports.baseimport (
24+
DEFAULT_CLIENT_INFO,
25+
OperationsTransport,
26+
)
27+
fromgoogle.api_core.operations_v1.transports.restimportOperationsRestTransport
28+
29+
try:
30+
fromgoogle.api_core.operations_v1.transports.rest_asyncioimport (
31+
AsyncOperationsRestTransport,
32+
)
33+
34+
HAS_ASYNC_REST_DEPENDENCIES=True
35+
exceptImportErrorase:
36+
HAS_ASYNC_REST_DEPENDENCIES=False
37+
ASYNC_REST_EXCEPTION=e
38+
39+
fromgoogle.authimportcredentialsasga_credentials# type: ignore
40+
fromgoogle.auth.exceptionsimportMutualTLSChannelError# type: ignore
41+
fromgoogle.auth.transportimportmtls# type: ignore
42+
43+
44+
classAbstractOperationsBaseClientMeta(type):
45+
"""Metaclass for the Operations Base client.
46+
47+
This provides base class-level methods for building and retrieving
48+
support objects (e.g. transport) without polluting the client instance
49+
objects.
50+
"""
51+
52+
_transport_registry=OrderedDict()# type: Dict[str, Type[OperationsTransport]]
53+
_transport_registry["rest"]=OperationsRestTransport
54+
ifHAS_ASYNC_REST_DEPENDENCIES:
55+
_transport_registry["rest_asyncio"]=AsyncOperationsRestTransport
56+
57+
defget_transport_class(
58+
cls,
59+
label:Optional[str]=None,
60+
)->Type[OperationsTransport]:
61+
"""Returns an appropriate transport class.
62+
63+
Args:
64+
label: The name of the desired transport. If none is
65+
provided, then the first transport in the registry is used.
66+
67+
Returns:
68+
The transport class to use.
69+
"""
70+
# If a specific transport is requested, return that one.
71+
if (
72+
label=="rest_asyncio"andnotHAS_ASYNC_REST_DEPENDENCIES
73+
):# pragma: NO COVER
74+
raiseASYNC_REST_EXCEPTION
75+
76+
iflabel:
77+
returncls._transport_registry[label]
78+
79+
# No transport is requested; return the default (that is, the first one
80+
# in the dictionary).
81+
returnnext(iter(cls._transport_registry.values()))
82+
83+
84+
classAbstractOperationsBaseClient(metaclass=AbstractOperationsBaseClientMeta):
85+
"""Manages long-running operations with an API service.
86+
87+
When an API method normally takes long time to complete, it can be
88+
designed to return [Operation][google.api_core.operations_v1.Operation] to the
89+
client, and the client can use this interface to receive the real
90+
response asynchronously by polling the operation resource, or pass
91+
the operation resource to another API (such as Google Cloud Pub/Sub
92+
API) to receive the response. Any API service that returns
93+
long-running operations should implement the ``Operations``
94+
interface so developers can have a consistent client experience.
95+
"""
96+
97+
@staticmethod
98+
def_get_default_mtls_endpoint(api_endpoint):
99+
"""Converts api endpoint to mTLS endpoint.
100+
101+
Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to
102+
"*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively.
103+
Args:
104+
api_endpoint (Optional[str]): the api endpoint to convert.
105+
Returns:
106+
str: converted mTLS api endpoint.
107+
"""
108+
ifnotapi_endpoint:
109+
returnapi_endpoint
110+
111+
mtls_endpoint_re=re.compile(
112+
r"(?P<name>[^.]+)(?P<mtls>\.mtls)?(?P<sandbox>\.sandbox)?(?P<googledomain>\.googleapis\.com)?"
113+
)
114+
115+
m=mtls_endpoint_re.match(api_endpoint)
116+
name,mtls,sandbox,googledomain=m.groups()
117+
ifmtlsornotgoogledomain:
118+
returnapi_endpoint
119+
120+
ifsandbox:
121+
returnapi_endpoint.replace(
122+
"sandbox.googleapis.com","mtls.sandbox.googleapis.com"
123+
)
124+
125+
returnapi_endpoint.replace(".googleapis.com",".mtls.googleapis.com")
126+
127+
DEFAULT_ENDPOINT="longrunning.googleapis.com"
128+
DEFAULT_MTLS_ENDPOINT=_get_default_mtls_endpoint.__func__(# type: ignore
129+
DEFAULT_ENDPOINT
130+
)
131+
132+
@classmethod
133+
deffrom_service_account_info(cls,info:dict,*args,**kwargs):
134+
"""
135+
This class method should be overridden by the subclasses.
136+
137+
Args:
138+
info (dict): The service account private key info.
139+
args: Additional arguments to pass to the constructor.
140+
kwargs: Additional arguments to pass to the constructor.
141+
142+
Raises:
143+
NotImplementedError: If the method is called on the base class.
144+
"""
145+
raiseNotImplementedError("`from_service_account_info` is not implemented.")
146+
147+
@classmethod
148+
deffrom_service_account_file(cls,filename:str,*args,**kwargs):
149+
"""
150+
This class method should be overridden by the subclasses.
151+
152+
Args:
153+
filename (str): The path to the service account private key json
154+
file.
155+
args: Additional arguments to pass to the constructor.
156+
kwargs: Additional arguments to pass to the constructor.
157+
158+
Raises:
159+
NotImplementedError: If the method is called on the base class.
160+
"""
161+
raiseNotImplementedError("`from_service_account_file` is not implemented.")
162+
163+
from_service_account_json=from_service_account_file
164+
165+
@property
166+
deftransport(self)->OperationsTransport:
167+
"""Returns the transport used by the client instance.
168+
169+
Returns:
170+
OperationsTransport: The transport used by the client
171+
instance.
172+
"""
173+
returnself._transport
174+
175+
@staticmethod
176+
defcommon_billing_account_path(
177+
billing_account:str,
178+
)->str:
179+
"""Returns a fully-qualified billing_account string."""
180+
return"billingAccounts/{billing_account}".format(
181+
billing_account=billing_account,
182+
)
183+
184+
@staticmethod
185+
defparse_common_billing_account_path(path:str)->Dict[str,str]:
186+
"""Parse a billing_account path into its component segments."""
187+
m=re.match(r"^billingAccounts/(?P<billing_account>.+?)$",path)
188+
returnm.groupdict()ifmelse {}
189+
190+
@staticmethod
191+
defcommon_folder_path(
192+
folder:str,
193+
)->str:
194+
"""Returns a fully-qualified folder string."""
195+
return"folders/{folder}".format(
196+
folder=folder,
197+
)
198+
199+
@staticmethod
200+
defparse_common_folder_path(path:str)->Dict[str,str]:
201+
"""Parse a folder path into its component segments."""
202+
m=re.match(r"^folders/(?P<folder>.+?)$",path)
203+
returnm.groupdict()ifmelse {}
204+
205+
@staticmethod
206+
defcommon_organization_path(
207+
organization:str,
208+
)->str:
209+
"""Returns a fully-qualified organization string."""
210+
return"organizations/{organization}".format(
211+
organization=organization,
212+
)
213+
214+
@staticmethod
215+
defparse_common_organization_path(path:str)->Dict[str,str]:
216+
"""Parse a organization path into its component segments."""
217+
m=re.match(r"^organizations/(?P<organization>.+?)$",path)
218+
returnm.groupdict()ifmelse {}
219+
220+
@staticmethod
221+
defcommon_project_path(
222+
project:str,
223+
)->str:
224+
"""Returns a fully-qualified project string."""
225+
return"projects/{project}".format(
226+
project=project,
227+
)
228+
229+
@staticmethod
230+
defparse_common_project_path(path:str)->Dict[str,str]:
231+
"""Parse a project path into its component segments."""
232+
m=re.match(r"^projects/(?P<project>.+?)$",path)
233+
returnm.groupdict()ifmelse {}
234+
235+
@staticmethod
236+
defcommon_location_path(
237+
project:str,
238+
location:str,
239+
)->str:
240+
"""Returns a fully-qualified location string."""
241+
return"projects/{project}/locations/{location}".format(
242+
project=project,
243+
location=location,
244+
)
245+
246+
@staticmethod
247+
defparse_common_location_path(path:str)->Dict[str,str]:
248+
"""Parse a location path into its component segments."""
249+
m=re.match(r"^projects/(?P<project>.+?)/locations/(?P<location>.+?)$",path)
250+
returnm.groupdict()ifmelse {}
251+
252+
def__init__(
253+
self,
254+
*,
255+
credentials:Optional[ga_credentials.Credentials]=None,
256+
transport:Union[str,OperationsTransport,None]=None,
257+
client_options:Optional[client_options_lib.ClientOptions]=None,
258+
client_info:gapic_v1.client_info.ClientInfo=DEFAULT_CLIENT_INFO,
259+
)->None:
260+
"""Instantiates the operations client.
261+
262+
Args:
263+
credentials (Optional[google.auth.credentials.Credentials]): The
264+
authorization credentials to attach to requests. These
265+
credentials identify the application to the service; if none
266+
are specified, the client will attempt to ascertain the
267+
credentials from the environment.
268+
transport (Union[str, OperationsTransport]): The
269+
transport to use. If set to None, a transport is chosen
270+
automatically.
271+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
272+
client. It won't take effect if a ``transport`` instance is provided.
273+
(1) The ``api_endpoint`` property can be used to override the
274+
default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
275+
environment variable can also be used to override the endpoint:
276+
"always" (always use the default mTLS endpoint), "never" (always
277+
use the default regular endpoint) and "auto" (auto switch to the
278+
default mTLS endpoint if client certificate is present, this is
279+
the default value). However, the ``api_endpoint`` property takes
280+
precedence if provided.
281+
(2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
282+
is "true", then the ``client_cert_source`` property can be used
283+
to provide client certificate for mutual TLS transport. If
284+
not provided, the default SSL client certificate will be used if
285+
present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
286+
set, no client certificate will be used.
287+
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
288+
The client info used to send a user-agent string along with
289+
API requests. If ``None``, then default info will be used.
290+
Generally, you only need to set this if you're developing
291+
your own client library.
292+
293+
Raises:
294+
google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport
295+
creation failed for any reason.
296+
"""
297+
ifisinstance(client_options,dict):
298+
client_options=client_options_lib.from_dict(client_options)
299+
ifclient_optionsisNone:
300+
client_options=client_options_lib.ClientOptions()
301+
302+
# Create SSL credentials for mutual TLS if needed.
303+
use_client_cert=os.getenv(
304+
"GOOGLE_API_USE_CLIENT_CERTIFICATE","false"
305+
).lower()
306+
ifuse_client_certnotin ("true","false"):
307+
raiseValueError(
308+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
309+
)
310+
client_cert_source_func=None
311+
is_mtls=False
312+
ifuse_client_cert=="true":
313+
ifclient_options.client_cert_source:
314+
is_mtls=True
315+
client_cert_source_func=client_options.client_cert_source
316+
else:
317+
is_mtls=mtls.has_default_client_cert_source()
318+
ifis_mtls:
319+
client_cert_source_func=mtls.default_client_cert_source()
320+
else:
321+
client_cert_source_func=None
322+
323+
# Figure out which api endpoint to use.
324+
ifclient_options.api_endpointisnotNone:
325+
api_endpoint=client_options.api_endpoint
326+
else:
327+
use_mtls_env=os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT","auto")
328+
ifuse_mtls_env=="never":
329+
api_endpoint=self.DEFAULT_ENDPOINT
330+
elifuse_mtls_env=="always":
331+
api_endpoint=self.DEFAULT_MTLS_ENDPOINT
332+
elifuse_mtls_env=="auto":
333+
ifis_mtls:
334+
api_endpoint=self.DEFAULT_MTLS_ENDPOINT
335+
else:
336+
api_endpoint=self.DEFAULT_ENDPOINT
337+
else:
338+
raiseMutualTLSChannelError(
339+
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
340+
"values: never, auto, always"
341+
)
342+
343+
# Save or instantiate the transport.
344+
# Ordinarily, we provide the transport, but allowing a custom transport
345+
# instance provides an extensibility point for unusual situations.
346+
ifisinstance(transport,OperationsTransport):
347+
# transport is a OperationsTransport instance.
348+
ifcredentialsorclient_options.credentials_file:
349+
raiseValueError(
350+
"When providing a transport instance, "
351+
"provide its credentials directly."
352+
)
353+
ifclient_options.scopes:
354+
raiseValueError(
355+
"When providing a transport instance, provide its scopes "
356+
"directly."
357+
)
358+
self._transport=transport
359+
else:
360+
Transport=type(self).get_transport_class(transport)
361+
self._transport=Transport(
362+
credentials=credentials,
363+
credentials_file=client_options.credentials_file,
364+
host=api_endpoint,
365+
scopes=client_options.scopes,
366+
client_cert_source_for_mtls=client_cert_source_func,
367+
quota_project_id=client_options.quota_project_id,
368+
client_info=client_info,
369+
always_use_jwt_access=True,
370+
)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp