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,147 @@
|
|
|
1
|
+
# Copyright 2023 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
|
+
"""Shared helper functions for connecting BigQuery and pyarrow.
|
|
16
|
+
|
|
17
|
+
NOTE: This module is DEPRECATED. Please make updates in the pandas-gbq package,
|
|
18
|
+
instead. See: go/pandas-gbq-and-bigframes-redundancy,
|
|
19
|
+
https://github.com/googleapis/python-bigquery-pandas/blob/main/pandas_gbq/schema/bigquery_to_pyarrow.py
|
|
20
|
+
and
|
|
21
|
+
https://github.com/googleapis/python-bigquery-pandas/blob/main/pandas_gbq/schema/pyarrow_to_bigquery.py
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
import pyarrow # type: ignore
|
|
28
|
+
except ImportError:
|
|
29
|
+
pyarrow = None
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
import db_dtypes # type: ignore
|
|
33
|
+
|
|
34
|
+
db_dtypes_import_exception = None
|
|
35
|
+
except ImportError as exc:
|
|
36
|
+
db_dtypes = None
|
|
37
|
+
db_dtypes_import_exception = exc
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def pyarrow_datetime():
|
|
41
|
+
return pyarrow.timestamp("us", tz=None)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def pyarrow_numeric():
|
|
45
|
+
return pyarrow.decimal128(38, 9)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def pyarrow_bignumeric():
|
|
49
|
+
# 77th digit is partial.
|
|
50
|
+
# https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#decimal_types
|
|
51
|
+
return pyarrow.decimal256(76, 38)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def pyarrow_time():
|
|
55
|
+
return pyarrow.time64("us")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def pyarrow_timestamp():
|
|
59
|
+
return pyarrow.timestamp("us", tz="UTC")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
_BQ_TO_ARROW_SCALARS = {}
|
|
63
|
+
_ARROW_SCALAR_IDS_TO_BQ = {}
|
|
64
|
+
|
|
65
|
+
if pyarrow:
|
|
66
|
+
# This dictionary is duplicated in bigquery_storage/test/unite/test_reader.py
|
|
67
|
+
# When modifying it be sure to update it there as well.
|
|
68
|
+
# Note(todo!!): type "BIGNUMERIC"'s matching pyarrow type is added in _pandas_helpers.py
|
|
69
|
+
_BQ_TO_ARROW_SCALARS = {
|
|
70
|
+
"BOOL": pyarrow.bool_,
|
|
71
|
+
"BOOLEAN": pyarrow.bool_,
|
|
72
|
+
"BYTES": pyarrow.binary,
|
|
73
|
+
"DATE": pyarrow.date32,
|
|
74
|
+
"DATETIME": pyarrow_datetime,
|
|
75
|
+
"FLOAT": pyarrow.float64,
|
|
76
|
+
"FLOAT64": pyarrow.float64,
|
|
77
|
+
"GEOGRAPHY": pyarrow.string,
|
|
78
|
+
"INT64": pyarrow.int64,
|
|
79
|
+
"INTEGER": pyarrow.int64,
|
|
80
|
+
# Normally, we'd prefer JSON type built-in to pyarrow (added in 19.0.0),
|
|
81
|
+
# but we'd like this to map as closely to the BQ Storage API as
|
|
82
|
+
# possible, which uses the string() dtype, as JSON support in Arrow
|
|
83
|
+
# predates JSON support in BigQuery by several years.
|
|
84
|
+
"JSON": pyarrow.string,
|
|
85
|
+
"NUMERIC": pyarrow_numeric,
|
|
86
|
+
"STRING": pyarrow.string,
|
|
87
|
+
"TIME": pyarrow_time,
|
|
88
|
+
"TIMESTAMP": pyarrow_timestamp,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# DEPRECATED: update pandas_gbq.schema.pyarrow_to_bigquery, instead.
|
|
92
|
+
_ARROW_SCALAR_IDS_TO_BQ = {
|
|
93
|
+
# https://arrow.apache.org/docs/python/api/datatypes.html#type-classes
|
|
94
|
+
pyarrow.bool_().id: "BOOL",
|
|
95
|
+
pyarrow.int8().id: "INT64",
|
|
96
|
+
pyarrow.int16().id: "INT64",
|
|
97
|
+
pyarrow.int32().id: "INT64",
|
|
98
|
+
pyarrow.int64().id: "INT64",
|
|
99
|
+
pyarrow.uint8().id: "INT64",
|
|
100
|
+
pyarrow.uint16().id: "INT64",
|
|
101
|
+
pyarrow.uint32().id: "INT64",
|
|
102
|
+
pyarrow.uint64().id: "INT64",
|
|
103
|
+
pyarrow.float16().id: "FLOAT64",
|
|
104
|
+
pyarrow.float32().id: "FLOAT64",
|
|
105
|
+
pyarrow.float64().id: "FLOAT64",
|
|
106
|
+
pyarrow.time32("ms").id: "TIME",
|
|
107
|
+
pyarrow.time64("ns").id: "TIME",
|
|
108
|
+
pyarrow.timestamp("ns").id: "TIMESTAMP",
|
|
109
|
+
pyarrow.date32().id: "DATE",
|
|
110
|
+
pyarrow.date64().id: "DATETIME", # because millisecond resolution
|
|
111
|
+
pyarrow.binary().id: "BYTES",
|
|
112
|
+
pyarrow.string().id: "STRING", # also alias for pyarrow.utf8()
|
|
113
|
+
pyarrow.large_string().id: "STRING",
|
|
114
|
+
# The exact scale and precision don't matter, see below.
|
|
115
|
+
pyarrow.decimal128(38, scale=9).id: "NUMERIC",
|
|
116
|
+
# NOTE: all extension types (e.g. json_, uuid, db_dtypes.JSONArrowType)
|
|
117
|
+
# have the same id (31 as of version 19.0.1), so these should not be
|
|
118
|
+
# matched by id.
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
_BQ_TO_ARROW_SCALARS["BIGNUMERIC"] = pyarrow_bignumeric
|
|
122
|
+
# The exact decimal's scale and precision are not important, as only
|
|
123
|
+
# the type ID matters, and it's the same for all decimal256 instances.
|
|
124
|
+
_ARROW_SCALAR_IDS_TO_BQ[pyarrow.decimal256(76, scale=38).id] = "BIGNUMERIC"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def bq_to_arrow_scalars(bq_scalar: str):
|
|
128
|
+
"""
|
|
129
|
+
DEPRECATED: update pandas_gbq.schema.bigquery_to_pyarrow, instead, which is
|
|
130
|
+
to be added in https://github.com/googleapis/python-bigquery-pandas/pull/893.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
The Arrow scalar type that the input BigQuery scalar type maps to.
|
|
134
|
+
If it cannot find the BigQuery scalar, return None.
|
|
135
|
+
"""
|
|
136
|
+
return _BQ_TO_ARROW_SCALARS.get(bq_scalar)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def arrow_scalar_ids_to_bq(arrow_scalar: Any):
|
|
140
|
+
"""
|
|
141
|
+
DEPRECATED: update pandas_gbq.schema.pyarrow_to_bigquery, instead.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
The BigQuery scalar type that the input arrow scalar type maps to.
|
|
145
|
+
If it cannot find the arrow scalar, return None.
|
|
146
|
+
"""
|
|
147
|
+
return _ARROW_SCALAR_IDS_TO_BQ.get(arrow_scalar)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Copyright 2019 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
|
+
"""Shared helper functions for tqdm progress bar."""
|
|
16
|
+
|
|
17
|
+
import concurrent.futures
|
|
18
|
+
import sys
|
|
19
|
+
import time
|
|
20
|
+
import typing
|
|
21
|
+
from typing import Optional
|
|
22
|
+
import warnings
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
import tqdm # type: ignore
|
|
26
|
+
except ImportError:
|
|
27
|
+
tqdm = None
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import tqdm.notebook as tqdm_notebook # type: ignore
|
|
31
|
+
except ImportError:
|
|
32
|
+
tqdm_notebook = None
|
|
33
|
+
|
|
34
|
+
if typing.TYPE_CHECKING: # pragma: NO COVER
|
|
35
|
+
from google.cloud.bigquery import QueryJob
|
|
36
|
+
from google.cloud.bigquery.table import RowIterator
|
|
37
|
+
|
|
38
|
+
_NO_TQDM_ERROR = (
|
|
39
|
+
"A progress bar was requested, but there was an error loading the tqdm "
|
|
40
|
+
"library. Please install tqdm to use the progress bar functionality."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
_PROGRESS_BAR_UPDATE_INTERVAL = 0.5
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_progress_bar(progress_bar_type, description, total, unit):
|
|
47
|
+
"""Construct a tqdm progress bar object, if tqdm is installed."""
|
|
48
|
+
if tqdm is None or tqdm_notebook is None and progress_bar_type == "tqdm_notebook":
|
|
49
|
+
if progress_bar_type is not None:
|
|
50
|
+
warnings.warn(_NO_TQDM_ERROR, UserWarning, stacklevel=3)
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
if progress_bar_type == "tqdm":
|
|
55
|
+
return tqdm.tqdm(
|
|
56
|
+
bar_format="{l_bar}{bar}|",
|
|
57
|
+
colour="green",
|
|
58
|
+
desc=description,
|
|
59
|
+
file=sys.stdout,
|
|
60
|
+
total=total,
|
|
61
|
+
unit=unit,
|
|
62
|
+
)
|
|
63
|
+
elif progress_bar_type == "tqdm_notebook":
|
|
64
|
+
return tqdm_notebook.tqdm(
|
|
65
|
+
bar_format="{l_bar}{bar}|",
|
|
66
|
+
desc=description,
|
|
67
|
+
file=sys.stdout,
|
|
68
|
+
total=total,
|
|
69
|
+
unit=unit,
|
|
70
|
+
)
|
|
71
|
+
elif progress_bar_type == "tqdm_gui":
|
|
72
|
+
return tqdm.tqdm_gui(desc=description, total=total, unit=unit)
|
|
73
|
+
except (KeyError, TypeError): # pragma: NO COVER
|
|
74
|
+
# Protect ourselves from any tqdm errors. In case of
|
|
75
|
+
# unexpected tqdm behavior, just fall back to showing
|
|
76
|
+
# no progress bar.
|
|
77
|
+
warnings.warn(_NO_TQDM_ERROR, UserWarning, stacklevel=3)
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def wait_for_query(
|
|
82
|
+
query_job: "QueryJob",
|
|
83
|
+
progress_bar_type: Optional[str] = None,
|
|
84
|
+
max_results: Optional[int] = None,
|
|
85
|
+
) -> "RowIterator":
|
|
86
|
+
"""Return query result and display a progress bar while the query running, if tqdm is installed.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
query_job:
|
|
90
|
+
The job representing the execution of the query on the server.
|
|
91
|
+
progress_bar_type:
|
|
92
|
+
The type of progress bar to use to show query progress.
|
|
93
|
+
max_results:
|
|
94
|
+
The maximum number of rows the row iterator should return.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
A row iterator over the query results.
|
|
98
|
+
"""
|
|
99
|
+
default_total = 1
|
|
100
|
+
current_stage = None
|
|
101
|
+
start_time = time.perf_counter()
|
|
102
|
+
|
|
103
|
+
progress_bar = get_progress_bar(
|
|
104
|
+
progress_bar_type, "Query is running", default_total, "query"
|
|
105
|
+
)
|
|
106
|
+
if progress_bar is None:
|
|
107
|
+
return query_job.result(max_results=max_results)
|
|
108
|
+
|
|
109
|
+
i = 0
|
|
110
|
+
while True:
|
|
111
|
+
if query_job.query_plan:
|
|
112
|
+
default_total = len(query_job.query_plan)
|
|
113
|
+
current_stage = query_job.query_plan[i]
|
|
114
|
+
progress_bar.total = len(query_job.query_plan)
|
|
115
|
+
progress_bar.set_description(
|
|
116
|
+
f"Query executing stage {current_stage.name} and status {current_stage.status} : {time.perf_counter() - start_time:.2f}s"
|
|
117
|
+
)
|
|
118
|
+
try:
|
|
119
|
+
query_result = query_job.result(
|
|
120
|
+
timeout=_PROGRESS_BAR_UPDATE_INTERVAL, max_results=max_results
|
|
121
|
+
)
|
|
122
|
+
progress_bar.update(default_total)
|
|
123
|
+
progress_bar.set_description(
|
|
124
|
+
f"Job ID {query_job.job_id} successfully executed",
|
|
125
|
+
)
|
|
126
|
+
break
|
|
127
|
+
except concurrent.futures.TimeoutError:
|
|
128
|
+
query_job.reload() # Refreshes the state via a GET request.
|
|
129
|
+
if current_stage:
|
|
130
|
+
if current_stage.status == "COMPLETE":
|
|
131
|
+
if i < default_total - 1:
|
|
132
|
+
progress_bar.update(i + 1)
|
|
133
|
+
i += 1
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
progress_bar.close()
|
|
137
|
+
return query_result
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# Copyright 2023 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
|
+
"""Shared helper functions for verifying versions of installed modules."""
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
import packaging.version
|
|
21
|
+
|
|
22
|
+
from google.cloud.bigquery import exceptions
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_MIN_PYARROW_VERSION = packaging.version.Version("3.0.0")
|
|
26
|
+
_MIN_BQ_STORAGE_VERSION = packaging.version.Version("2.0.0")
|
|
27
|
+
_BQ_STORAGE_OPTIONAL_READ_SESSION_VERSION = packaging.version.Version("2.6.0")
|
|
28
|
+
_MIN_PANDAS_VERSION = packaging.version.Version("1.1.0")
|
|
29
|
+
|
|
30
|
+
_MIN_PANDAS_VERSION_RANGE = packaging.version.Version("1.5.0")
|
|
31
|
+
_MIN_PYARROW_VERSION_RANGE = packaging.version.Version("10.0.1")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PyarrowVersions:
|
|
35
|
+
"""Version comparisons for pyarrow package."""
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self._installed_version = None
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def installed_version(self) -> packaging.version.Version:
|
|
42
|
+
"""Return the parsed version of pyarrow."""
|
|
43
|
+
if self._installed_version is None:
|
|
44
|
+
import pyarrow # type: ignore
|
|
45
|
+
|
|
46
|
+
self._installed_version = packaging.version.parse(
|
|
47
|
+
# Use 0.0.0, since it is earlier than any released version.
|
|
48
|
+
# Legacy versions also have the same property, but
|
|
49
|
+
# creating a LegacyVersion has been deprecated.
|
|
50
|
+
# https://github.com/pypa/packaging/issues/321
|
|
51
|
+
getattr(pyarrow, "__version__", "0.0.0")
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return self._installed_version
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def use_compliant_nested_type(self) -> bool:
|
|
58
|
+
return self.installed_version.major >= 4
|
|
59
|
+
|
|
60
|
+
def try_import(self, raise_if_error: bool = False) -> Any:
|
|
61
|
+
"""Verifies that a recent enough version of pyarrow extra is installed.
|
|
62
|
+
|
|
63
|
+
The function assumes that pyarrow extra is installed, and should thus
|
|
64
|
+
be used in places where this assumption holds.
|
|
65
|
+
|
|
66
|
+
Because `pip` can install an outdated version of this extra despite
|
|
67
|
+
the constraints in `setup.py`, the calling code can use this helper
|
|
68
|
+
to verify the version compatibility at runtime.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
The ``pyarrow`` module or ``None``.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
exceptions.LegacyPyarrowError:
|
|
75
|
+
If the pyarrow package is outdated and ``raise_if_error`` is
|
|
76
|
+
``True``.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
import pyarrow
|
|
80
|
+
except ImportError as exc:
|
|
81
|
+
if raise_if_error:
|
|
82
|
+
raise exceptions.LegacyPyarrowError(
|
|
83
|
+
"pyarrow package not found. Install pyarrow version >="
|
|
84
|
+
f" {_MIN_PYARROW_VERSION}."
|
|
85
|
+
) from exc
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
if self.installed_version < _MIN_PYARROW_VERSION:
|
|
89
|
+
if raise_if_error:
|
|
90
|
+
msg = (
|
|
91
|
+
"Dependency pyarrow is outdated, please upgrade"
|
|
92
|
+
f" it to version >= {_MIN_PYARROW_VERSION}"
|
|
93
|
+
f" (version found: {self.installed_version})."
|
|
94
|
+
)
|
|
95
|
+
raise exceptions.LegacyPyarrowError(msg)
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
return pyarrow
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
PYARROW_VERSIONS = PyarrowVersions()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class BQStorageVersions:
|
|
105
|
+
"""Version comparisons for google-cloud-bigqueyr-storage package."""
|
|
106
|
+
|
|
107
|
+
def __init__(self):
|
|
108
|
+
self._installed_version = None
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def installed_version(self) -> packaging.version.Version:
|
|
112
|
+
"""Return the parsed version of google-cloud-bigquery-storage."""
|
|
113
|
+
if self._installed_version is None:
|
|
114
|
+
from google.cloud import bigquery_storage
|
|
115
|
+
|
|
116
|
+
self._installed_version = packaging.version.parse(
|
|
117
|
+
# Use 0.0.0, since it is earlier than any released version.
|
|
118
|
+
# Legacy versions also have the same property, but
|
|
119
|
+
# creating a LegacyVersion has been deprecated.
|
|
120
|
+
# https://github.com/pypa/packaging/issues/321
|
|
121
|
+
getattr(bigquery_storage, "__version__", "0.0.0")
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return self._installed_version # type: ignore
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def is_read_session_optional(self) -> bool:
|
|
128
|
+
"""True if read_session is optional to rows().
|
|
129
|
+
|
|
130
|
+
See: https://github.com/googleapis/python-bigquery-storage/pull/228
|
|
131
|
+
"""
|
|
132
|
+
return self.installed_version >= _BQ_STORAGE_OPTIONAL_READ_SESSION_VERSION
|
|
133
|
+
|
|
134
|
+
def try_import(self, raise_if_error: bool = False) -> Any:
|
|
135
|
+
"""Tries to import the bigquery_storage module, and returns results
|
|
136
|
+
accordingly. It also verifies the module version is recent enough.
|
|
137
|
+
|
|
138
|
+
If the import succeeds, returns the ``bigquery_storage`` module.
|
|
139
|
+
|
|
140
|
+
If the import fails,
|
|
141
|
+
returns ``None`` when ``raise_if_error == False``,
|
|
142
|
+
raises Error when ``raise_if_error == True``.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
The ``bigquery_storage`` module or ``None``.
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
exceptions.BigQueryStorageNotFoundError:
|
|
149
|
+
If google-cloud-bigquery-storage is not installed
|
|
150
|
+
exceptions.LegacyBigQueryStorageError:
|
|
151
|
+
If google-cloud-bigquery-storage package is outdated
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
from google.cloud import bigquery_storage # type: ignore
|
|
155
|
+
except ImportError:
|
|
156
|
+
if raise_if_error:
|
|
157
|
+
msg = (
|
|
158
|
+
"Package google-cloud-bigquery-storage not found. "
|
|
159
|
+
"Install google-cloud-bigquery-storage version >= "
|
|
160
|
+
f"{_MIN_BQ_STORAGE_VERSION}."
|
|
161
|
+
)
|
|
162
|
+
raise exceptions.BigQueryStorageNotFoundError(msg)
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
if self.installed_version < _MIN_BQ_STORAGE_VERSION:
|
|
166
|
+
if raise_if_error:
|
|
167
|
+
msg = (
|
|
168
|
+
"Dependency google-cloud-bigquery-storage is outdated, "
|
|
169
|
+
f"please upgrade it to version >= {_MIN_BQ_STORAGE_VERSION} "
|
|
170
|
+
f"(version found: {self.installed_version})."
|
|
171
|
+
)
|
|
172
|
+
raise exceptions.LegacyBigQueryStorageError(msg)
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
return bigquery_storage
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
BQ_STORAGE_VERSIONS = BQStorageVersions()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class PandasVersions:
|
|
182
|
+
"""Version comparisons for pandas package."""
|
|
183
|
+
|
|
184
|
+
def __init__(self):
|
|
185
|
+
self._installed_version = None
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def installed_version(self) -> packaging.version.Version:
|
|
189
|
+
"""Return the parsed version of pandas"""
|
|
190
|
+
if self._installed_version is None:
|
|
191
|
+
import pandas # type: ignore
|
|
192
|
+
|
|
193
|
+
self._installed_version = packaging.version.parse(
|
|
194
|
+
# Use 0.0.0, since it is earlier than any released version.
|
|
195
|
+
# Legacy versions also have the same property, but
|
|
196
|
+
# creating a LegacyVersion has been deprecated.
|
|
197
|
+
# https://github.com/pypa/packaging/issues/321
|
|
198
|
+
getattr(pandas, "__version__", "0.0.0")
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
return self._installed_version
|
|
202
|
+
|
|
203
|
+
def try_import(self, raise_if_error: bool = False) -> Any:
|
|
204
|
+
"""Verify that a recent enough version of pandas extra is installed.
|
|
205
|
+
The function assumes that pandas extra is installed, and should thus
|
|
206
|
+
be used in places where this assumption holds.
|
|
207
|
+
Because `pip` can install an outdated version of this extra despite
|
|
208
|
+
the constraints in `setup.py`, the calling code can use this helper
|
|
209
|
+
to verify the version compatibility at runtime.
|
|
210
|
+
Returns:
|
|
211
|
+
The ``pandas`` module or ``None``.
|
|
212
|
+
Raises:
|
|
213
|
+
exceptions.LegacyPandasError:
|
|
214
|
+
If the pandas package is outdated and ``raise_if_error`` is
|
|
215
|
+
``True``.
|
|
216
|
+
"""
|
|
217
|
+
try:
|
|
218
|
+
import pandas
|
|
219
|
+
except ImportError as exc:
|
|
220
|
+
if raise_if_error:
|
|
221
|
+
raise exceptions.LegacyPandasError(
|
|
222
|
+
"pandas package not found. Install pandas version >="
|
|
223
|
+
f" {_MIN_PANDAS_VERSION}"
|
|
224
|
+
) from exc
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
if self.installed_version < _MIN_PANDAS_VERSION:
|
|
228
|
+
if raise_if_error:
|
|
229
|
+
msg = (
|
|
230
|
+
"Dependency pandas is outdated, please upgrade"
|
|
231
|
+
f" it to version >= {_MIN_PANDAS_VERSION}"
|
|
232
|
+
f" (version found: {self.installed_version})."
|
|
233
|
+
)
|
|
234
|
+
raise exceptions.LegacyPandasError(msg)
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
return pandas
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
PANDAS_VERSIONS = PandasVersions()
|
|
241
|
+
|
|
242
|
+
# Since RANGE support in pandas requires specific versions
|
|
243
|
+
# of both pyarrow and pandas, we make this a separate
|
|
244
|
+
# constant instead of as a property of PANDAS_VERSIONS
|
|
245
|
+
# or PYARROW_VERSIONS.
|
|
246
|
+
SUPPORTS_RANGE_PYARROW = (
|
|
247
|
+
PANDAS_VERSIONS.try_import() is not None
|
|
248
|
+
and PANDAS_VERSIONS.installed_version >= _MIN_PANDAS_VERSION_RANGE
|
|
249
|
+
and PYARROW_VERSIONS.try_import() is not None
|
|
250
|
+
and PYARROW_VERSIONS.installed_version >= _MIN_PYARROW_VERSION_RANGE
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def extract_runtime_version():
|
|
255
|
+
# Retrieve the version information
|
|
256
|
+
version_info = sys.version_info
|
|
257
|
+
|
|
258
|
+
# Extract the major, minor, and micro components
|
|
259
|
+
major = version_info.major
|
|
260
|
+
minor = version_info.minor
|
|
261
|
+
micro = version_info.micro
|
|
262
|
+
|
|
263
|
+
# Display the version number in a clear format
|
|
264
|
+
return major, minor, micro
|