google-cloud-bigquery 3.31.0__py3-none-any.whl
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/cloud/bigquery/__init__.py +249 -0
- google/cloud/bigquery/_helpers.py +1102 -0
- google/cloud/bigquery/_http.py +47 -0
- google/cloud/bigquery/_job_helpers.py +600 -0
- google/cloud/bigquery/_pandas_helpers.py +1181 -0
- google/cloud/bigquery/_pyarrow_helpers.py +147 -0
- google/cloud/bigquery/_tqdm_helpers.py +137 -0
- google/cloud/bigquery/_versions_helpers.py +264 -0
- google/cloud/bigquery/client.py +4406 -0
- google/cloud/bigquery/dataset.py +1076 -0
- google/cloud/bigquery/dbapi/__init__.py +87 -0
- google/cloud/bigquery/dbapi/_helpers.py +522 -0
- google/cloud/bigquery/dbapi/connection.py +128 -0
- google/cloud/bigquery/dbapi/cursor.py +586 -0
- google/cloud/bigquery/dbapi/exceptions.py +58 -0
- google/cloud/bigquery/dbapi/types.py +96 -0
- google/cloud/bigquery/encryption_configuration.py +84 -0
- google/cloud/bigquery/enums.py +389 -0
- google/cloud/bigquery/exceptions.py +35 -0
- google/cloud/bigquery/external_config.py +1188 -0
- google/cloud/bigquery/format_options.py +147 -0
- google/cloud/bigquery/iam.py +38 -0
- google/cloud/bigquery/job/__init__.py +87 -0
- google/cloud/bigquery/job/base.py +1116 -0
- google/cloud/bigquery/job/copy_.py +282 -0
- google/cloud/bigquery/job/extract.py +271 -0
- google/cloud/bigquery/job/load.py +985 -0
- google/cloud/bigquery/job/query.py +2498 -0
- google/cloud/bigquery/magics/__init__.py +20 -0
- google/cloud/bigquery/magics/line_arg_parser/__init__.py +34 -0
- google/cloud/bigquery/magics/line_arg_parser/exceptions.py +25 -0
- google/cloud/bigquery/magics/line_arg_parser/lexer.py +200 -0
- google/cloud/bigquery/magics/line_arg_parser/parser.py +484 -0
- google/cloud/bigquery/magics/line_arg_parser/visitors.py +159 -0
- google/cloud/bigquery/magics/magics.py +776 -0
- google/cloud/bigquery/model.py +517 -0
- google/cloud/bigquery/opentelemetry_tracing.py +164 -0
- google/cloud/bigquery/py.typed +2 -0
- google/cloud/bigquery/query.py +1344 -0
- google/cloud/bigquery/retry.py +207 -0
- google/cloud/bigquery/routine/__init__.py +33 -0
- google/cloud/bigquery/routine/routine.py +744 -0
- google/cloud/bigquery/schema.py +896 -0
- google/cloud/bigquery/standard_sql.py +389 -0
- google/cloud/bigquery/table.py +3594 -0
- google/cloud/bigquery/version.py +15 -0
- google/cloud/bigquery_v2/__init__.py +56 -0
- google/cloud/bigquery_v2/types/__init__.py +54 -0
- google/cloud/bigquery_v2/types/encryption_config.py +48 -0
- google/cloud/bigquery_v2/types/model.py +1994 -0
- google/cloud/bigquery_v2/types/model_reference.py +57 -0
- google/cloud/bigquery_v2/types/standard_sql.py +156 -0
- google/cloud/bigquery_v2/types/table_reference.py +80 -0
- google_cloud_bigquery-3.31.0.dist-info/LICENSE +202 -0
- google_cloud_bigquery-3.31.0.dist-info/METADATA +203 -0
- google_cloud_bigquery-3.31.0.dist-info/RECORD +58 -0
- google_cloud_bigquery-3.31.0.dist-info/WHEEL +5 -0
- google_cloud_bigquery-3.31.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2019 Google LLC
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
"""Define resources for the BigQuery ML Models API."""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations # type: ignore
|
|
20
|
+
|
|
21
|
+
import copy
|
|
22
|
+
import datetime
|
|
23
|
+
import typing
|
|
24
|
+
from typing import Any, Dict, Optional, Sequence, Union
|
|
25
|
+
|
|
26
|
+
import google.cloud._helpers # type: ignore
|
|
27
|
+
from google.cloud.bigquery import _helpers
|
|
28
|
+
from google.cloud.bigquery import standard_sql
|
|
29
|
+
from google.cloud.bigquery.encryption_configuration import EncryptionConfiguration
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Model:
|
|
33
|
+
"""Model represents a machine learning model resource.
|
|
34
|
+
|
|
35
|
+
See
|
|
36
|
+
https://cloud.google.com/bigquery/docs/reference/rest/v2/models
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
model_ref:
|
|
40
|
+
A pointer to a model. If ``model_ref`` is a string, it must
|
|
41
|
+
included a project ID, dataset ID, and model ID, each separated
|
|
42
|
+
by ``.``.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
_PROPERTY_TO_API_FIELD = {
|
|
46
|
+
"expires": "expirationTime",
|
|
47
|
+
"friendly_name": "friendlyName",
|
|
48
|
+
# Even though it's not necessary for field mapping to map when the
|
|
49
|
+
# property name equals the resource name, we add these here so that we
|
|
50
|
+
# have an exhaustive list of all mutable properties.
|
|
51
|
+
"labels": "labels",
|
|
52
|
+
"description": "description",
|
|
53
|
+
"encryption_configuration": "encryptionConfiguration",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def __init__(self, model_ref: Union["ModelReference", str, None]):
|
|
57
|
+
# Use _properties on read-write properties to match the REST API
|
|
58
|
+
# semantics. The BigQuery API makes a distinction between an unset
|
|
59
|
+
# value, a null value, and a default value (0 or ""), but the protocol
|
|
60
|
+
# buffer classes do not.
|
|
61
|
+
self._properties: Dict[str, Any] = {}
|
|
62
|
+
|
|
63
|
+
if isinstance(model_ref, str):
|
|
64
|
+
model_ref = ModelReference.from_string(model_ref)
|
|
65
|
+
|
|
66
|
+
if model_ref:
|
|
67
|
+
self._properties["modelReference"] = model_ref.to_api_repr()
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def reference(self) -> Optional["ModelReference"]:
|
|
71
|
+
"""A model reference pointing to this model.
|
|
72
|
+
|
|
73
|
+
Read-only.
|
|
74
|
+
"""
|
|
75
|
+
resource = self._properties.get("modelReference")
|
|
76
|
+
if resource is None:
|
|
77
|
+
return None
|
|
78
|
+
else:
|
|
79
|
+
return ModelReference.from_api_repr(resource)
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def project(self) -> Optional[str]:
|
|
83
|
+
"""Project bound to the model."""
|
|
84
|
+
ref = self.reference
|
|
85
|
+
return ref.project if ref is not None else None
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def dataset_id(self) -> Optional[str]:
|
|
89
|
+
"""ID of dataset containing the model."""
|
|
90
|
+
ref = self.reference
|
|
91
|
+
return ref.dataset_id if ref is not None else None
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def model_id(self) -> Optional[str]:
|
|
95
|
+
"""The model ID."""
|
|
96
|
+
ref = self.reference
|
|
97
|
+
return ref.model_id if ref is not None else None
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def path(self) -> Optional[str]:
|
|
101
|
+
"""URL path for the model's APIs."""
|
|
102
|
+
ref = self.reference
|
|
103
|
+
return ref.path if ref is not None else None
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def location(self) -> Optional[str]:
|
|
107
|
+
"""The geographic location where the model resides.
|
|
108
|
+
|
|
109
|
+
This value is inherited from the dataset.
|
|
110
|
+
|
|
111
|
+
Read-only.
|
|
112
|
+
"""
|
|
113
|
+
return typing.cast(Optional[str], self._properties.get("location"))
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def etag(self) -> Optional[str]:
|
|
117
|
+
"""ETag for the model resource (:data:`None` until set from the server).
|
|
118
|
+
|
|
119
|
+
Read-only.
|
|
120
|
+
"""
|
|
121
|
+
return typing.cast(Optional[str], self._properties.get("etag"))
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def created(self) -> Optional[datetime.datetime]:
|
|
125
|
+
"""Datetime at which the model was created (:data:`None` until set from the server).
|
|
126
|
+
|
|
127
|
+
Read-only.
|
|
128
|
+
"""
|
|
129
|
+
value = typing.cast(Optional[float], self._properties.get("creationTime"))
|
|
130
|
+
if value is None:
|
|
131
|
+
return None
|
|
132
|
+
else:
|
|
133
|
+
# value will be in milliseconds.
|
|
134
|
+
return google.cloud._helpers._datetime_from_microseconds(
|
|
135
|
+
1000.0 * float(value)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def modified(self) -> Optional[datetime.datetime]:
|
|
140
|
+
"""Datetime at which the model was last modified (:data:`None` until set from the server).
|
|
141
|
+
|
|
142
|
+
Read-only.
|
|
143
|
+
"""
|
|
144
|
+
value = typing.cast(Optional[float], self._properties.get("lastModifiedTime"))
|
|
145
|
+
if value is None:
|
|
146
|
+
return None
|
|
147
|
+
else:
|
|
148
|
+
# value will be in milliseconds.
|
|
149
|
+
return google.cloud._helpers._datetime_from_microseconds(
|
|
150
|
+
1000.0 * float(value)
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def model_type(self) -> str:
|
|
155
|
+
"""Type of the model resource.
|
|
156
|
+
|
|
157
|
+
Read-only.
|
|
158
|
+
"""
|
|
159
|
+
return typing.cast(
|
|
160
|
+
str, self._properties.get("modelType", "MODEL_TYPE_UNSPECIFIED")
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def training_runs(self) -> Sequence[Dict[str, Any]]:
|
|
165
|
+
"""Information for all training runs in increasing order of start time.
|
|
166
|
+
|
|
167
|
+
Dictionaries are in REST API format. See:
|
|
168
|
+
https://cloud.google.com/bigquery/docs/reference/rest/v2/models#trainingrun
|
|
169
|
+
|
|
170
|
+
Read-only.
|
|
171
|
+
"""
|
|
172
|
+
return typing.cast(
|
|
173
|
+
Sequence[Dict[str, Any]], self._properties.get("trainingRuns", [])
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def feature_columns(self) -> Sequence[standard_sql.StandardSqlField]:
|
|
178
|
+
"""Input feature columns that were used to train this model.
|
|
179
|
+
|
|
180
|
+
Read-only.
|
|
181
|
+
"""
|
|
182
|
+
resource: Sequence[Dict[str, Any]] = typing.cast(
|
|
183
|
+
Sequence[Dict[str, Any]], self._properties.get("featureColumns", [])
|
|
184
|
+
)
|
|
185
|
+
return [
|
|
186
|
+
standard_sql.StandardSqlField.from_api_repr(column) for column in resource
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def transform_columns(self) -> Sequence[TransformColumn]:
|
|
191
|
+
"""The input feature columns that were used to train this model.
|
|
192
|
+
The output transform columns used to train this model.
|
|
193
|
+
|
|
194
|
+
See REST API:
|
|
195
|
+
https://cloud.google.com/bigquery/docs/reference/rest/v2/models#transformcolumn
|
|
196
|
+
|
|
197
|
+
Read-only.
|
|
198
|
+
"""
|
|
199
|
+
resources: Sequence[Dict[str, Any]] = typing.cast(
|
|
200
|
+
Sequence[Dict[str, Any]], self._properties.get("transformColumns", [])
|
|
201
|
+
)
|
|
202
|
+
return [TransformColumn(resource) for resource in resources]
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def label_columns(self) -> Sequence[standard_sql.StandardSqlField]:
|
|
206
|
+
"""Label columns that were used to train this model.
|
|
207
|
+
|
|
208
|
+
The output of the model will have a ``predicted_`` prefix to these columns.
|
|
209
|
+
|
|
210
|
+
Read-only.
|
|
211
|
+
"""
|
|
212
|
+
resource: Sequence[Dict[str, Any]] = typing.cast(
|
|
213
|
+
Sequence[Dict[str, Any]], self._properties.get("labelColumns", [])
|
|
214
|
+
)
|
|
215
|
+
return [
|
|
216
|
+
standard_sql.StandardSqlField.from_api_repr(column) for column in resource
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def best_trial_id(self) -> Optional[int]:
|
|
221
|
+
"""The best trial_id across all training runs.
|
|
222
|
+
|
|
223
|
+
.. deprecated::
|
|
224
|
+
This property is deprecated!
|
|
225
|
+
|
|
226
|
+
Read-only.
|
|
227
|
+
"""
|
|
228
|
+
value = typing.cast(Optional[int], self._properties.get("bestTrialId"))
|
|
229
|
+
if value is not None:
|
|
230
|
+
value = int(value)
|
|
231
|
+
return value
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def expires(self) -> Optional[datetime.datetime]:
|
|
235
|
+
"""The datetime when this model expires.
|
|
236
|
+
|
|
237
|
+
If not present, the model will persist indefinitely. Expired models will be
|
|
238
|
+
deleted and their storage reclaimed.
|
|
239
|
+
"""
|
|
240
|
+
value = typing.cast(Optional[float], self._properties.get("expirationTime"))
|
|
241
|
+
if value is None:
|
|
242
|
+
return None
|
|
243
|
+
else:
|
|
244
|
+
# value will be in milliseconds.
|
|
245
|
+
return google.cloud._helpers._datetime_from_microseconds(
|
|
246
|
+
1000.0 * float(value)
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
@expires.setter
|
|
250
|
+
def expires(self, value: Optional[datetime.datetime]):
|
|
251
|
+
if value is None:
|
|
252
|
+
value_to_store: Optional[str] = None
|
|
253
|
+
else:
|
|
254
|
+
value_to_store = str(google.cloud._helpers._millis_from_datetime(value))
|
|
255
|
+
# TODO: Consider using typing.TypedDict when only Python 3.8+ is supported.
|
|
256
|
+
self._properties["expirationTime"] = value_to_store # type: ignore
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def description(self) -> Optional[str]:
|
|
260
|
+
"""Description of the model (defaults to :data:`None`)."""
|
|
261
|
+
return typing.cast(Optional[str], self._properties.get("description"))
|
|
262
|
+
|
|
263
|
+
@description.setter
|
|
264
|
+
def description(self, value: Optional[str]):
|
|
265
|
+
# TODO: Consider using typing.TypedDict when only Python 3.8+ is supported.
|
|
266
|
+
self._properties["description"] = value # type: ignore
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def friendly_name(self) -> Optional[str]:
|
|
270
|
+
"""Title of the table (defaults to :data:`None`)."""
|
|
271
|
+
return typing.cast(Optional[str], self._properties.get("friendlyName"))
|
|
272
|
+
|
|
273
|
+
@friendly_name.setter
|
|
274
|
+
def friendly_name(self, value: Optional[str]):
|
|
275
|
+
# TODO: Consider using typing.TypedDict when only Python 3.8+ is supported.
|
|
276
|
+
self._properties["friendlyName"] = value # type: ignore
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def labels(self) -> Dict[str, str]:
|
|
280
|
+
"""Labels for the table.
|
|
281
|
+
|
|
282
|
+
This method always returns a dict. To change a model's labels, modify the dict,
|
|
283
|
+
then call ``Client.update_model``. To delete a label, set its value to
|
|
284
|
+
:data:`None` before updating.
|
|
285
|
+
"""
|
|
286
|
+
return self._properties.setdefault("labels", {})
|
|
287
|
+
|
|
288
|
+
@labels.setter
|
|
289
|
+
def labels(self, value: Optional[Dict[str, str]]):
|
|
290
|
+
if value is None:
|
|
291
|
+
value = {}
|
|
292
|
+
self._properties["labels"] = value
|
|
293
|
+
|
|
294
|
+
@property
|
|
295
|
+
def encryption_configuration(self) -> Optional[EncryptionConfiguration]:
|
|
296
|
+
"""Custom encryption configuration for the model.
|
|
297
|
+
|
|
298
|
+
Custom encryption configuration (e.g., Cloud KMS keys) or :data:`None`
|
|
299
|
+
if using default encryption.
|
|
300
|
+
|
|
301
|
+
See `protecting data with Cloud KMS keys
|
|
302
|
+
<https://cloud.google.com/bigquery/docs/customer-managed-encryption>`_
|
|
303
|
+
in the BigQuery documentation.
|
|
304
|
+
"""
|
|
305
|
+
prop = self._properties.get("encryptionConfiguration")
|
|
306
|
+
if prop:
|
|
307
|
+
prop = EncryptionConfiguration.from_api_repr(prop)
|
|
308
|
+
return typing.cast(Optional[EncryptionConfiguration], prop)
|
|
309
|
+
|
|
310
|
+
@encryption_configuration.setter
|
|
311
|
+
def encryption_configuration(self, value: Optional[EncryptionConfiguration]):
|
|
312
|
+
api_repr = value.to_api_repr() if value else value
|
|
313
|
+
self._properties["encryptionConfiguration"] = api_repr
|
|
314
|
+
|
|
315
|
+
@classmethod
|
|
316
|
+
def from_api_repr(cls, resource: Dict[str, Any]) -> "Model":
|
|
317
|
+
"""Factory: construct a model resource given its API representation
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
resource:
|
|
321
|
+
Model resource representation from the API
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
Model parsed from ``resource``.
|
|
325
|
+
"""
|
|
326
|
+
this = cls(None)
|
|
327
|
+
resource = copy.deepcopy(resource)
|
|
328
|
+
this._properties = resource
|
|
329
|
+
return this
|
|
330
|
+
|
|
331
|
+
def _build_resource(self, filter_fields):
|
|
332
|
+
"""Generate a resource for ``update``."""
|
|
333
|
+
return _helpers._build_resource_from_properties(self, filter_fields)
|
|
334
|
+
|
|
335
|
+
def __repr__(self):
|
|
336
|
+
return f"Model(reference={self.reference!r})"
|
|
337
|
+
|
|
338
|
+
def to_api_repr(self) -> Dict[str, Any]:
|
|
339
|
+
"""Construct the API resource representation of this model.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Model reference represented as an API resource
|
|
343
|
+
"""
|
|
344
|
+
return copy.deepcopy(self._properties)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class ModelReference:
|
|
348
|
+
"""ModelReferences are pointers to models.
|
|
349
|
+
|
|
350
|
+
See
|
|
351
|
+
https://cloud.google.com/bigquery/docs/reference/rest/v2/models#modelreference
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
def __init__(self):
|
|
355
|
+
self._properties = {}
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def project(self):
|
|
359
|
+
"""str: Project bound to the model"""
|
|
360
|
+
return self._properties.get("projectId")
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def dataset_id(self):
|
|
364
|
+
"""str: ID of dataset containing the model."""
|
|
365
|
+
return self._properties.get("datasetId")
|
|
366
|
+
|
|
367
|
+
@property
|
|
368
|
+
def model_id(self):
|
|
369
|
+
"""str: The model ID."""
|
|
370
|
+
return self._properties.get("modelId")
|
|
371
|
+
|
|
372
|
+
@property
|
|
373
|
+
def path(self) -> str:
|
|
374
|
+
"""URL path for the model's APIs."""
|
|
375
|
+
return f"/projects/{self.project}/datasets/{self.dataset_id}/models/{self.model_id}"
|
|
376
|
+
|
|
377
|
+
@classmethod
|
|
378
|
+
def from_api_repr(cls, resource: Dict[str, Any]) -> "ModelReference":
|
|
379
|
+
"""Factory: construct a model reference given its API representation.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
resource:
|
|
383
|
+
Model reference representation returned from the API
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
Model reference parsed from ``resource``.
|
|
387
|
+
"""
|
|
388
|
+
ref = cls()
|
|
389
|
+
ref._properties = resource
|
|
390
|
+
return ref
|
|
391
|
+
|
|
392
|
+
@classmethod
|
|
393
|
+
def from_string(
|
|
394
|
+
cls, model_id: str, default_project: Optional[str] = None
|
|
395
|
+
) -> "ModelReference":
|
|
396
|
+
"""Construct a model reference from model ID string.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
model_id:
|
|
400
|
+
A model ID in standard SQL format. If ``default_project``
|
|
401
|
+
is not specified, this must included a project ID, dataset
|
|
402
|
+
ID, and model ID, each separated by ``.``.
|
|
403
|
+
default_project:
|
|
404
|
+
The project ID to use when ``model_id`` does not include
|
|
405
|
+
a project ID.
|
|
406
|
+
|
|
407
|
+
Returns:
|
|
408
|
+
Model reference parsed from ``model_id``.
|
|
409
|
+
|
|
410
|
+
Raises:
|
|
411
|
+
ValueError:
|
|
412
|
+
If ``model_id`` is not a fully-qualified table ID in
|
|
413
|
+
standard SQL format.
|
|
414
|
+
"""
|
|
415
|
+
proj, dset, model = _helpers._parse_3_part_id(
|
|
416
|
+
model_id, default_project=default_project, property_name="model_id"
|
|
417
|
+
)
|
|
418
|
+
return cls.from_api_repr(
|
|
419
|
+
{"projectId": proj, "datasetId": dset, "modelId": model}
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
def to_api_repr(self) -> Dict[str, Any]:
|
|
423
|
+
"""Construct the API resource representation of this model reference.
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
Model reference represented as an API resource.
|
|
427
|
+
"""
|
|
428
|
+
return copy.deepcopy(self._properties)
|
|
429
|
+
|
|
430
|
+
def _key(self):
|
|
431
|
+
"""Unique key for this model.
|
|
432
|
+
|
|
433
|
+
This is used for hashing a ModelReference.
|
|
434
|
+
"""
|
|
435
|
+
return self.project, self.dataset_id, self.model_id
|
|
436
|
+
|
|
437
|
+
def __eq__(self, other):
|
|
438
|
+
if not isinstance(other, ModelReference):
|
|
439
|
+
return NotImplemented
|
|
440
|
+
return self._properties == other._properties
|
|
441
|
+
|
|
442
|
+
def __ne__(self, other):
|
|
443
|
+
return not self == other
|
|
444
|
+
|
|
445
|
+
def __hash__(self):
|
|
446
|
+
return hash(self._key())
|
|
447
|
+
|
|
448
|
+
def __repr__(self):
|
|
449
|
+
return "ModelReference(project_id='{}', dataset_id='{}', model_id='{}')".format(
|
|
450
|
+
self.project, self.dataset_id, self.model_id
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
class TransformColumn:
|
|
455
|
+
"""TransformColumn represents a transform column feature.
|
|
456
|
+
|
|
457
|
+
See
|
|
458
|
+
https://cloud.google.com/bigquery/docs/reference/rest/v2/models#transformcolumn
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
resource:
|
|
462
|
+
A dictionary representing a transform column feature.
|
|
463
|
+
"""
|
|
464
|
+
|
|
465
|
+
def __init__(self, resource: Dict[str, Any]):
|
|
466
|
+
self._properties = resource
|
|
467
|
+
|
|
468
|
+
@property
|
|
469
|
+
def name(self) -> Optional[str]:
|
|
470
|
+
"""Name of the column."""
|
|
471
|
+
return self._properties.get("name")
|
|
472
|
+
|
|
473
|
+
@property
|
|
474
|
+
def type_(self) -> Optional[standard_sql.StandardSqlDataType]:
|
|
475
|
+
"""Data type of the column after the transform.
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
Optional[google.cloud.bigquery.standard_sql.StandardSqlDataType]:
|
|
479
|
+
Data type of the column.
|
|
480
|
+
"""
|
|
481
|
+
type_json = self._properties.get("type")
|
|
482
|
+
if type_json is None:
|
|
483
|
+
return None
|
|
484
|
+
return standard_sql.StandardSqlDataType.from_api_repr(type_json)
|
|
485
|
+
|
|
486
|
+
@property
|
|
487
|
+
def transform_sql(self) -> Optional[str]:
|
|
488
|
+
"""The SQL expression used in the column transform."""
|
|
489
|
+
return self._properties.get("transformSql")
|
|
490
|
+
|
|
491
|
+
@classmethod
|
|
492
|
+
def from_api_repr(cls, resource: Dict[str, Any]) -> "TransformColumn":
|
|
493
|
+
"""Constructs a transform column feature given its API representation
|
|
494
|
+
|
|
495
|
+
Args:
|
|
496
|
+
resource:
|
|
497
|
+
Transform column feature representation from the API
|
|
498
|
+
|
|
499
|
+
Returns:
|
|
500
|
+
Transform column feature parsed from ``resource``.
|
|
501
|
+
"""
|
|
502
|
+
this = cls({})
|
|
503
|
+
resource = copy.deepcopy(resource)
|
|
504
|
+
this._properties = resource
|
|
505
|
+
return this
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _model_arg_to_model_ref(value, default_project=None):
|
|
509
|
+
"""Helper to convert a string or Model to ModelReference.
|
|
510
|
+
|
|
511
|
+
This function keeps ModelReference and other kinds of objects unchanged.
|
|
512
|
+
"""
|
|
513
|
+
if isinstance(value, str):
|
|
514
|
+
return ModelReference.from_string(value, default_project=default_project)
|
|
515
|
+
if isinstance(value, Model):
|
|
516
|
+
return value.reference
|
|
517
|
+
return value
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Copyright 2020 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
|
+
import logging
|
|
16
|
+
from contextlib import contextmanager
|
|
17
|
+
from google.api_core.exceptions import GoogleAPICallError # type: ignore
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
try:
|
|
21
|
+
from opentelemetry import trace # type: ignore
|
|
22
|
+
from opentelemetry.instrumentation.utils import http_status_to_status_code # type: ignore
|
|
23
|
+
from opentelemetry.trace.status import Status # type: ignore
|
|
24
|
+
|
|
25
|
+
HAS_OPENTELEMETRY = True
|
|
26
|
+
_warned_telemetry = True
|
|
27
|
+
|
|
28
|
+
except ImportError:
|
|
29
|
+
HAS_OPENTELEMETRY = False
|
|
30
|
+
_warned_telemetry = False
|
|
31
|
+
|
|
32
|
+
_default_attributes = {
|
|
33
|
+
"db.system": "BigQuery"
|
|
34
|
+
} # static, default values assigned to all spans
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@contextmanager
|
|
38
|
+
def create_span(name, attributes=None, client=None, job_ref=None):
|
|
39
|
+
"""Creates a ContextManager for a Span to be exported to the configured exporter.
|
|
40
|
+
If no configuration exists yields None.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
name (str): Name that will be set for the span being created
|
|
44
|
+
attributes (Optional[dict]):
|
|
45
|
+
Additional attributes that pertain to
|
|
46
|
+
the specific API call (i.e. not a default attribute)
|
|
47
|
+
client (Optional[google.cloud.bigquery.client.Client]):
|
|
48
|
+
Pass in a Client object to extract any attributes that may be
|
|
49
|
+
relevant to it and add them to the created spans.
|
|
50
|
+
job_ref (Optional[google.cloud.bigquery.job._AsyncJob])
|
|
51
|
+
Pass in a _AsyncJob object to extract any attributes that may be
|
|
52
|
+
relevant to it and add them to the created spans.
|
|
53
|
+
|
|
54
|
+
Yields:
|
|
55
|
+
opentelemetry.trace.Span: Yields the newly created Span.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
google.api_core.exceptions.GoogleAPICallError:
|
|
59
|
+
Raised if a span could not be yielded or issue with call to
|
|
60
|
+
OpenTelemetry.
|
|
61
|
+
"""
|
|
62
|
+
global _warned_telemetry
|
|
63
|
+
final_attributes = _get_final_span_attributes(attributes, client, job_ref)
|
|
64
|
+
if not HAS_OPENTELEMETRY:
|
|
65
|
+
if not _warned_telemetry:
|
|
66
|
+
logger.debug(
|
|
67
|
+
"This service is instrumented using OpenTelemetry. "
|
|
68
|
+
"OpenTelemetry or one of its components could not be imported; "
|
|
69
|
+
"please add compatible versions of opentelemetry-api and "
|
|
70
|
+
"opentelemetry-instrumentation packages in order to get BigQuery "
|
|
71
|
+
"Tracing data."
|
|
72
|
+
)
|
|
73
|
+
_warned_telemetry = True
|
|
74
|
+
|
|
75
|
+
yield None
|
|
76
|
+
return
|
|
77
|
+
tracer = trace.get_tracer(__name__)
|
|
78
|
+
|
|
79
|
+
# yield new span value
|
|
80
|
+
with tracer.start_as_current_span(name=name, attributes=final_attributes) as span:
|
|
81
|
+
try:
|
|
82
|
+
yield span
|
|
83
|
+
except GoogleAPICallError as error:
|
|
84
|
+
if error.code is not None:
|
|
85
|
+
span.set_status(Status(http_status_to_status_code(error.code)))
|
|
86
|
+
raise
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _get_final_span_attributes(attributes=None, client=None, job_ref=None):
|
|
90
|
+
"""Compiles attributes from: client, job_ref, user-provided attributes.
|
|
91
|
+
|
|
92
|
+
Attributes from all of these sources are merged together. Note the
|
|
93
|
+
attributes are added sequentially based on perceived order of precedence:
|
|
94
|
+
i.e. attributes added last may overwrite attributes added earlier.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
attributes (Optional[dict]):
|
|
98
|
+
Additional attributes that pertain to
|
|
99
|
+
the specific API call (i.e. not a default attribute)
|
|
100
|
+
|
|
101
|
+
client (Optional[google.cloud.bigquery.client.Client]):
|
|
102
|
+
Pass in a Client object to extract any attributes that may be
|
|
103
|
+
relevant to it and add them to the final_attributes
|
|
104
|
+
|
|
105
|
+
job_ref (Optional[google.cloud.bigquery.job._AsyncJob])
|
|
106
|
+
Pass in a _AsyncJob object to extract any attributes that may be
|
|
107
|
+
relevant to it and add them to the final_attributes.
|
|
108
|
+
|
|
109
|
+
Returns: dict
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
collected_attributes = _default_attributes.copy()
|
|
113
|
+
|
|
114
|
+
if client:
|
|
115
|
+
collected_attributes.update(_set_client_attributes(client))
|
|
116
|
+
if job_ref:
|
|
117
|
+
collected_attributes.update(_set_job_attributes(job_ref))
|
|
118
|
+
if attributes:
|
|
119
|
+
collected_attributes.update(attributes)
|
|
120
|
+
|
|
121
|
+
final_attributes = {k: v for k, v in collected_attributes.items() if v is not None}
|
|
122
|
+
return final_attributes
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _set_client_attributes(client):
|
|
126
|
+
return {"db.name": client.project, "location": client.location}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _set_job_attributes(job_ref):
|
|
130
|
+
job_attributes = {
|
|
131
|
+
"db.name": job_ref.project,
|
|
132
|
+
"job_id": job_ref.job_id,
|
|
133
|
+
"state": job_ref.state,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
job_attributes["hasErrors"] = job_ref.error_result is not None
|
|
137
|
+
|
|
138
|
+
if job_ref.created is not None:
|
|
139
|
+
job_attributes["timeCreated"] = job_ref.created.isoformat()
|
|
140
|
+
|
|
141
|
+
if job_ref.started is not None:
|
|
142
|
+
job_attributes["timeStarted"] = job_ref.started.isoformat()
|
|
143
|
+
|
|
144
|
+
if job_ref.ended is not None:
|
|
145
|
+
job_attributes["timeEnded"] = job_ref.ended.isoformat()
|
|
146
|
+
|
|
147
|
+
if job_ref.location is not None:
|
|
148
|
+
job_attributes["location"] = job_ref.location
|
|
149
|
+
|
|
150
|
+
if job_ref.parent_job_id is not None:
|
|
151
|
+
job_attributes["parent_job_id"] = job_ref.parent_job_id
|
|
152
|
+
|
|
153
|
+
if job_ref.num_child_jobs is not None:
|
|
154
|
+
job_attributes["num_child_jobs"] = job_ref.num_child_jobs
|
|
155
|
+
|
|
156
|
+
total_bytes_billed = getattr(job_ref, "total_bytes_billed", None)
|
|
157
|
+
if total_bytes_billed is not None:
|
|
158
|
+
job_attributes["total_bytes_billed"] = total_bytes_billed
|
|
159
|
+
|
|
160
|
+
total_bytes_processed = getattr(job_ref, "total_bytes_processed", None)
|
|
161
|
+
if total_bytes_processed is not None:
|
|
162
|
+
job_attributes["total_bytes_processed"] = total_bytes_processed
|
|
163
|
+
|
|
164
|
+
return job_attributes
|