Skip to content

Commit 0b0b85a

Browse files
authored
Merge branch 'pangea-v1alpha' into feat-b358215039-update-schema-object
2 parents 1f00e76 + 100708d commit 0b0b85a

File tree

9 files changed

+261
-115
lines changed

9 files changed

+261
-115
lines changed

.github/.OwlBot.lock.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
# limitations under the License.
1414
docker:
1515
image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
16-
digest: sha256:2ed982f884312e4883e01b5ab8af8b6935f0216a5a2d82928d273081fc3be562
17-
# created: 2024-11-12T12:09:45.821174897Z
16+
digest: sha256:8e3e7e18255c22d1489258d0374c901c01f9c4fd77a12088670cd73d580aa737
17+
# created: 2024-12-17T00:59:58.625514486Z

.kokoro/docker/docs/requirements.txt

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
# This file is autogenerated by pip-compile with Python 3.10
33
# by the following command:
44
#
5-
# pip-compile --allow-unsafe --generate-hashes requirements.in
5+
# pip-compile --allow-unsafe --generate-hashes synthtool/gcp/templates/python_library/.kokoro/docker/docs/requirements.in
66
#
7-
argcomplete==3.5.1 \
8-
--hash=sha256:1a1d148bdaa3e3b93454900163403df41448a248af01b6e849edc5ac08e6c363 \
9-
--hash=sha256:eb1ee355aa2557bd3d0145de7b06b2a45b0ce461e1e7813f5d066039ab4177b4
7+
argcomplete==3.5.2 \
8+
--hash=sha256:036d020d79048a5d525bc63880d7a4b8d1668566b8a76daf1144c0bbe0f63472 \
9+
--hash=sha256:23146ed7ac4403b70bd6026402468942ceba34a6732255b9edf5b7354f68a6bb
1010
# via nox
1111
colorlog==6.9.0 \
1212
--hash=sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff \
@@ -23,7 +23,7 @@ filelock==3.16.1 \
2323
nox==2024.10.9 \
2424
--hash=sha256:1d36f309a0a2a853e9bccb76bbef6bb118ba92fa92674d15604ca99adeb29eab \
2525
--hash=sha256:7aa9dc8d1c27e9f45ab046ffd1c3b2c4f7c91755304769df231308849ebded95
26-
# via -r requirements.in
26+
# via -r synthtool/gcp/templates/python_library/.kokoro/docker/docs/requirements.in
2727
packaging==24.2 \
2828
--hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \
2929
--hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f
@@ -32,11 +32,41 @@ platformdirs==4.3.6 \
3232
--hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \
3333
--hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb
3434
# via virtualenv
35-
tomli==2.0.2 \
36-
--hash=sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38 \
37-
--hash=sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed
35+
tomli==2.2.1 \
36+
--hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \
37+
--hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \
38+
--hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \
39+
--hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \
40+
--hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \
41+
--hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \
42+
--hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \
43+
--hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \
44+
--hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \
45+
--hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \
46+
--hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \
47+
--hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \
48+
--hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \
49+
--hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \
50+
--hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \
51+
--hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \
52+
--hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \
53+
--hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \
54+
--hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \
55+
--hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \
56+
--hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \
57+
--hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \
58+
--hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \
59+
--hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \
60+
--hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff \
61+
--hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \
62+
--hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \
63+
--hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \
64+
--hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \
65+
--hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \
66+
--hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \
67+
--hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7
3868
# via nox
39-
virtualenv==20.27.1 \
40-
--hash=sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba \
41-
--hash=sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4
69+
virtualenv==20.28.0 \
70+
--hash=sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0 \
71+
--hash=sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa
4272
# via nox

.kokoro/requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,9 @@ jeepney==0.8.0 \
254254
# via
255255
# keyring
256256
# secretstorage
257-
jinja2==3.1.4 \
258-
--hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \
259-
--hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d
257+
jinja2==3.1.5 \
258+
--hash=sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb \
259+
--hash=sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb
260260
# via gcp-releasetool
261261
keyring==25.4.1 \
262262
--hash=sha256:5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf \

google/cloud/bigquery/client.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import uuid
4545
import warnings
4646

47+
import requests
48+
4749
from google import resumable_media # type: ignore
4850
from google.resumable_media.requests import MultipartUpload # type: ignore
4951
from google.resumable_media.requests import ResumableUpload
@@ -65,6 +67,7 @@
6567
DEFAULT_BQSTORAGE_CLIENT_INFO = None # type: ignore
6668

6769

70+
from google.auth.credentials import Credentials
6871
from google.cloud.bigquery._http import Connection
6972
from google.cloud.bigquery import _job_helpers
7073
from google.cloud.bigquery import _pandas_helpers
@@ -126,15 +129,14 @@
126129
_versions_helpers.PANDAS_VERSIONS.try_import()
127130
) # mypy check fails because pandas import is outside module, there are type: ignore comments related to this
128131

132+
129133
ResumableTimeoutType = Union[
130134
None, float, Tuple[float, float]
131135
] # for resumable media methods
132136

133137
if typing.TYPE_CHECKING: # pragma: NO COVER
134138
# os.PathLike is only subscriptable in Python 3.9+, thus shielding with a condition.
135139
PathType = Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]
136-
import requests # required by api-core
137-
138140
_DEFAULT_CHUNKSIZE = 100 * 1024 * 1024 # 100 MB
139141
_MAX_MULTIPART_SIZE = 5 * 1024 * 1024
140142
_DEFAULT_NUM_RETRIES = 6
@@ -231,30 +233,34 @@ class Client(ClientWithProject):
231233

232234
def __init__(
233235
self,
234-
project=None,
235-
credentials=None,
236-
_http=None,
237-
location=None,
238-
default_query_job_config=None,
239-
default_load_job_config=None,
240-
client_info=None,
241-
client_options=None,
236+
project: Optional[str] = None,
237+
credentials: Optional[Credentials] = None,
238+
_http: Optional[requests.Session] = None,
239+
location: Optional[str] = None,
240+
default_query_job_config: Optional[QueryJobConfig] = None,
241+
default_load_job_config: Optional[LoadJobConfig] = None,
242+
client_info: Optional[google.api_core.client_info.ClientInfo] = None,
243+
client_options: Optional[
244+
Union[google.api_core.client_options.ClientOptions, Dict[str, Any]]
245+
] = None,
242246
) -> None:
247+
if client_options is None:
248+
client_options = {}
249+
if isinstance(client_options, dict):
250+
client_options = google.api_core.client_options.from_dict(client_options)
251+
# assert isinstance(client_options, google.api_core.client_options.ClientOptions)
252+
243253
super(Client, self).__init__(
244254
project=project,
245255
credentials=credentials,
246256
client_options=client_options,
247257
_http=_http,
248258
)
249259

250-
kw_args = {"client_info": client_info}
260+
kw_args: Dict[str, Any] = {"client_info": client_info}
251261
bq_host = _get_bigquery_host()
252262
kw_args["api_endpoint"] = bq_host if bq_host != _DEFAULT_HOST else None
253263
client_universe = None
254-
if client_options is None:
255-
client_options = {}
256-
if isinstance(client_options, dict):
257-
client_options = google.api_core.client_options.from_dict(client_options)
258264
if client_options.api_endpoint:
259265
api_endpoint = client_options.api_endpoint
260266
kw_args["api_endpoint"] = api_endpoint

google/cloud/bigquery/schema.py

Lines changed: 29 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020

2121
import copy
2222
import enum
23-
from typing import Any, Dict, Iterable, Optional, Union, cast, List, Mapping
23+
from typing import Any, cast, Dict, Iterable, Optional, Union
2424

25+
from google.cloud.bigquery import _helpers
2526
from google.cloud.bigquery import standard_sql
2627
from google.cloud.bigquery._helpers import (
2728
_isinstance_or_raise,
@@ -218,7 +219,10 @@ def __init__(
218219
rounding_mode: Union[RoundingMode, str, None] = None,
219220
foreign_type_definition: Optional[str] = None,
220221
):
221-
self._properties: Dict[str, Any] = {}
222+
self._properties: Dict[str, Any] = {
223+
"name": name,
224+
"type": field_type,
225+
}
222226

223227
self._properties["name"] = name
224228
if mode is not None:
@@ -257,16 +261,8 @@ def __init__(
257261
raise ValueError(
258262
"If the 'field_type' is 'FOREIGN', then 'foreign_type_definition' is required."
259263
)
260-
self._properties["type"] = field_type
261-
262-
self._fields = tuple(fields)
263-
264-
@staticmethod
265-
def __get_int(api_repr, name):
266-
v = api_repr.get(name, _DEFAULT_VALUE)
267-
if v is not _DEFAULT_VALUE:
268-
v = int(v)
269-
return v
264+
if fields: # Don't set the property if it's not set.
265+
self._properties["fields"] = [field.to_api_repr() for field in fields]
270266

271267
@classmethod
272268
def from_api_repr(cls, api_repr: Mapping[str, Any]) -> "SchemaField":
@@ -280,48 +276,19 @@ def from_api_repr(cls, api_repr: Mapping[str, Any]) -> "SchemaField":
280276
Returns:
281277
google.cloud.bigquery.schema.SchemaField: The ``SchemaField`` object.
282278
"""
283-
field_type = api_repr["type"].upper()
279+
placeholder = cls("this_will_be_replaced", "PLACEHOLDER")
284280

285-
# Handle optional properties with default values
286-
mode = api_repr.get("mode", "NULLABLE")
287-
description = api_repr.get("description", _DEFAULT_VALUE)
288-
fields = api_repr.get("fields", ())
289-
policy_tags = api_repr.get("policyTags", _DEFAULT_VALUE)
281+
# Note: we don't make a copy of api_repr because this can cause
282+
# unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
283+
# fields. See https://github.com/googleapis/python-bigquery/issues/6
284+
placeholder._properties = api_repr
290285

291-
default_value_expression = api_repr.get("defaultValueExpression", None)
292-
293-
if policy_tags is not None and policy_tags is not _DEFAULT_VALUE:
294-
policy_tags = PolicyTagList.from_api_repr(policy_tags)
295-
296-
if api_repr.get("rangeElementType"):
297-
range_element_type = cast(dict, api_repr.get("rangeElementType"))
298-
element_type = range_element_type.get("type")
299-
else:
300-
element_type = None
301-
302-
rounding_mode = api_repr.get("roundingMode")
303-
foreign_type_definition = api_repr.get("foreignTypeDefinition")
304-
305-
return cls(
306-
field_type=field_type,
307-
fields=[cls.from_api_repr(f) for f in fields],
308-
mode=mode.upper(),
309-
default_value_expression=default_value_expression,
310-
description=description,
311-
name=api_repr["name"],
312-
policy_tags=policy_tags,
313-
precision=cls.__get_int(api_repr, "precision"),
314-
scale=cls.__get_int(api_repr, "scale"),
315-
max_length=cls.__get_int(api_repr, "maxLength"),
316-
range_element_type=element_type,
317-
rounding_mode=rounding_mode,
318-
foreign_type_definition=foreign_type_definition,
319-
)
286+
return placeholder
320287

321288
@property
322289
def name(self):
323290
"""str: The name of the field."""
324-
return self._properties["name"]
291+
return self._properties.get("name", "")
325292

326293
@property
327294
def field_type(self):
@@ -330,7 +297,10 @@ def field_type(self):
330297
See:
331298
https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.type
332299
"""
333-
return self._properties["type"]
300+
type_ = self._properties.get("type")
301+
if type_ is None: # Shouldn't happen, but some unit tests do this.
302+
return None
303+
return cast(str, type_).upper()
334304

335305
@property
336306
def mode(self):
@@ -339,7 +309,7 @@ def mode(self):
339309
See:
340310
https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#TableFieldSchema.FIELDS.mode
341311
"""
342-
return self._properties.get("mode")
312+
return cast(str, self._properties.get("mode", "NULLABLE")).upper()
343313

344314
@property
345315
def is_nullable(self):
@@ -359,17 +329,17 @@ def description(self):
359329
@property
360330
def precision(self):
361331
"""Optional[int]: Precision (number of digits) for the NUMERIC field."""
362-
return self._properties.get("precision")
332+
return _helpers._int_or_none(self._properties.get("precision"))
363333

364334
@property
365335
def scale(self):
366336
"""Optional[int]: Scale (digits after decimal) for the NUMERIC field."""
367-
return self._properties.get("scale")
337+
return _helpers._int_or_none(self._properties.get("scale"))
368338

369339
@property
370340
def max_length(self):
371341
"""Optional[int]: Maximum length for the STRING or BYTES field."""
372-
return self._properties.get("maxLength")
342+
return _helpers._int_or_none(self._properties.get("maxLength"))
373343

374344
@property
375345
def range_element_type(self):
@@ -405,7 +375,7 @@ def fields(self):
405375
406376
Must be empty unset if ``field_type`` is not 'RECORD'.
407377
"""
408-
return self._fields
378+
return tuple(_to_schema_fields(self._properties.get("fields", [])))
409379

410380
@property
411381
def policy_tags(self):
@@ -421,15 +391,10 @@ def to_api_repr(self) -> dict:
421391
Returns:
422392
Dict: A dictionary representing the SchemaField in a serialized form.
423393
"""
424-
answer = self._properties.copy()
425-
426-
# If this is a RECORD type, then sub-fields are also included,
427-
# add this to the serialized representation.
428-
if self.field_type.upper() in _STRUCT_TYPES:
429-
answer["fields"] = [f.to_api_repr() for f in self.fields]
430-
431-
# Done; return the serialized dictionary.
432-
return answer
394+
# Note: we don't make a copy of _properties because this can cause
395+
# unnecessary slowdowns, especially on deeply nested STRUCT / RECORD
396+
# fields. See https://github.com/googleapis/python-bigquery/issues/6
397+
return self._properties
433398

434399
def _key(self):
435400
"""A tuple key that uniquely describes this field.
@@ -465,7 +430,7 @@ def _key(self):
465430
self.mode.upper(), # pytype: disable=attribute-error
466431
self.default_value_expression,
467432
self.description,
468-
self._fields,
433+
self.fields,
469434
policy_tags,
470435
)
471436

0 commit comments

Comments
 (0)