forked from googleapis/google-cloud-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoperation.py
More file actions
270 lines (203 loc) · 8.87 KB
/
Copy pathoperation.py
File metadata and controls
270 lines (203 loc) · 8.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# Copyright 2016 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Wrap long-running operations returned from Google Cloud APIs."""
from google.longrunning import operations_pb2
from google.protobuf import json_format
_GOOGLE_APIS_PREFIX = 'type.googleapis.com'
_TYPE_URL_MAP = {
}
def _compute_type_url(klass, prefix=_GOOGLE_APIS_PREFIX):
"""Compute a type URL for a klass.
:type klass: type
:param klass: class to be used as a factory for the given type
:type prefix: str
:param prefix: URL prefix for the type
:rtype: str
:returns: the URL, prefixed as appropriate
"""
name = klass.DESCRIPTOR.full_name
return '%s/%s' % (prefix, name)
def register_type(klass, type_url=None):
"""Register a klass as the factory for a given type URL.
:type klass: :class:`type`
:param klass: class to be used as a factory for the given type
:type type_url: str
:param type_url: (Optional) URL naming the type. If not provided,
infers the URL from the type descriptor.
:raises ValueError: if a registration already exists for the URL.
"""
if type_url is None:
type_url = _compute_type_url(klass)
if type_url in _TYPE_URL_MAP:
if _TYPE_URL_MAP[type_url] is not klass:
raise ValueError("Conflict: %s" % (_TYPE_URL_MAP[type_url],))
_TYPE_URL_MAP[type_url] = klass
def _from_any(any_pb):
"""Convert an ``Any`` protobuf into the actual class.
Uses the type URL to do the conversion.
.. note::
This assumes that the type URL is already registered.
:type any_pb: :class:`google.protobuf.any_pb2.Any`
:param any_pb: An any object to be converted.
:rtype: object
:returns: The instance (of the correct type) stored in the any
instance.
"""
klass = _TYPE_URL_MAP[any_pb.type_url]
return klass.FromString(any_pb.value)
class Operation(object):
"""Representation of a Google API Long-Running Operation.
.. _protobuf: https://github.com/googleapis/googleapis/blob/\
050400df0fdb16f63b63e9dee53819044bffc857/\
google/longrunning/operations.proto#L80
.. _service: https://github.com/googleapis/googleapis/blob/\
050400df0fdb16f63b63e9dee53819044bffc857/\
google/longrunning/operations.proto#L38
.. _JSON: https://cloud.google.com/speech/reference/rest/\
v1beta1/operations#Operation
This wraps an operation `protobuf`_ object and attempts to
interact with the long-running operations `service`_ (specific
to a given API). (Some services also offer a `JSON`_
API that maps the same underlying data type.)
:type name: str
:param name: The fully-qualified path naming the operation.
:type client: :class:`~google.cloud.client.Client`
:param client: The client used to poll for the status of the operation.
If the operation was created via JSON/HTTP, the client
must own a :class:`~google.cloud._http.Connection`
to send polling requests. If created via protobuf, the
client must have a gRPC stub in the ``_operations_stub``
attribute.
:type caller_metadata: dict
:param caller_metadata: caller-assigned metadata about the operation
"""
target = None
"""Instance assocated with the operations: callers may set."""
response = None
"""Response returned from completed operation.
Only one of this and :attr:`error` can be populated.
"""
error = None
"""Error that resulted from a failed (complete) operation.
Only one of this and :attr:`response` can be populated.
"""
metadata = None
"""Metadata about the current operation (as a protobuf).
Code that uses operations must register the metadata types (via
:func:`register_type`) to ensure that the metadata fields can be
converted into the correct types.
"""
_from_grpc = True
def __init__(self, name, client, **caller_metadata):
self.name = name
self.client = client
self.caller_metadata = caller_metadata.copy()
self._complete = False
@classmethod
def from_pb(cls, operation_pb, client, **caller_metadata):
"""Factory: construct an instance from a protobuf.
:type operation_pb:
:class:`~google.longrunning.operations_pb2.Operation`
:param operation_pb: Protobuf to be parsed.
:type client: object: must provide ``_operations_stub`` accessor.
:param client: The client used to poll for the status of the operation.
:type caller_metadata: dict
:param caller_metadata: caller-assigned metadata about the operation
:rtype: :class:`Operation`
:returns: new instance, with attributes based on the protobuf.
"""
result = cls(operation_pb.name, client, **caller_metadata)
result._update_state(operation_pb)
result._from_grpc = True
return result
@classmethod
def from_dict(cls, operation, client, **caller_metadata):
"""Factory: construct an instance from a dictionary.
:type operation: dict
:param operation: Operation as a JSON object.
:type client: :class:`~google.cloud.client.Client`
:param client: The client used to poll for the status of the operation.
:type caller_metadata: dict
:param caller_metadata: caller-assigned metadata about the operation
:rtype: :class:`Operation`
:returns: new instance, with attributes based on the protobuf.
"""
operation_pb = json_format.ParseDict(
operation, operations_pb2.Operation())
result = cls(operation_pb.name, client, **caller_metadata)
result._update_state(operation_pb)
result._from_grpc = False
return result
@property
def complete(self):
"""Has the operation already completed?
:rtype: bool
:returns: True if already completed, else false.
"""
return self._complete
def _get_operation_rpc(self):
"""Polls the status of the current operation.
Uses gRPC request to check.
:rtype: :class:`~google.longrunning.operations_pb2.Operation`
:returns: The latest status of the current operation.
"""
request_pb = operations_pb2.GetOperationRequest(name=self.name)
return self.client._operations_stub.GetOperation(request_pb)
def _get_operation_http(self):
"""Checks the status of the current operation.
Uses HTTP request to check.
:rtype: :class:`~google.longrunning.operations_pb2.Operation`
:returns: The latest status of the current operation.
"""
path = 'operations/%s' % (self.name,)
api_response = self.client._connection.api_request(
method='GET', path=path)
return json_format.ParseDict(
api_response, operations_pb2.Operation())
def _get_operation(self):
"""Checks the status of the current operation.
:rtype: :class:`~google.longrunning.operations_pb2.Operation`
:returns: The latest status of the current operation.
"""
if self._from_grpc:
return self._get_operation_rpc()
else:
return self._get_operation_http()
def _update_state(self, operation_pb):
"""Update the state of the current object based on operation.
:type operation_pb:
:class:`~google.longrunning.operations_pb2.Operation`
:param operation_pb: Protobuf to be parsed.
"""
if operation_pb.done:
self._complete = True
if operation_pb.HasField('metadata'):
self.metadata = _from_any(operation_pb.metadata)
result_type = operation_pb.WhichOneof('result')
if result_type == 'error':
self.error = operation_pb.error
elif result_type == 'response':
self.response = _from_any(operation_pb.response)
def poll(self):
"""Check if the operation has finished.
:rtype: bool
:returns: A boolean indicating if the current operation has completed.
:raises ValueError: if the operation
has already completed.
"""
if self.complete:
raise ValueError('The operation has completed.')
operation_pb = self._get_operation()
self._update_state(operation_pb)
return self.complete