Skip to content

Commit a2812c9

Browse files
committed
feat: implement pandas-gbq delegation check and telemetry
1 parent 4b049c4 commit a2812c9

3 files changed

Lines changed: 239 additions & 63 deletions

File tree

packages/google-cloud-bigquery/google/cloud/bigquery/_versions_helpers.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@
1616
from typing import Any
1717

1818
import packaging.version
19-
2019
from google.cloud.bigquery import exceptions
2120

22-
2321
_MIN_PYARROW_VERSION = packaging.version.Version("3.0.0")
2422
_MIN_BQ_STORAGE_VERSION = packaging.version.Version("2.0.0")
2523
_BQ_STORAGE_OPTIONAL_READ_SESSION_VERSION = packaging.version.Version("2.6.0")
@@ -247,3 +245,49 @@ def try_import(self, raise_if_error: bool = False) -> Any:
247245
and PYARROW_VERSIONS.try_import() is not None
248246
and PYARROW_VERSIONS.installed_version >= _MIN_PYARROW_VERSION_RANGE
249247
)
248+
249+
250+
class PandasGBQVersions:
251+
"""Version and delegation comparisons for pandas-gbq package."""
252+
253+
def __init__(self):
254+
self._installed_version = None
255+
self._delegation_api_version = None
256+
257+
@property
258+
def installed_version(self) -> packaging.version.Version:
259+
"""Return the parsed version of pandas-gbq"""
260+
if self._installed_version is None:
261+
try:
262+
import pandas_gbq # type: ignore
263+
264+
self._installed_version = packaging.version.parse(
265+
getattr(pandas_gbq, "__version__", "0.0.0")
266+
)
267+
except ImportError:
268+
self._installed_version = packaging.version.parse("0.0.0")
269+
270+
return self._installed_version
271+
272+
@property
273+
def delegation_api_version(self) -> int:
274+
"""Return the delegation API version of pandas-gbq if installed, otherwise 0."""
275+
if self._delegation_api_version is None:
276+
try:
277+
import pandas_gbq # type: ignore
278+
279+
self._delegation_api_version = getattr(
280+
pandas_gbq, "_internal_delegation_api_version", 0
281+
)
282+
except ImportError:
283+
self._delegation_api_version = 0
284+
285+
return self._delegation_api_version
286+
287+
@property
288+
def is_delegation_supported(self) -> bool:
289+
"""True if the installed pandas-gbq version supports query delegation API (version >= 1)."""
290+
return self.delegation_api_version >= 1
291+
292+
293+
PANDAS_GBQ_VERSIONS = PandasGBQVersions()

packages/google-cloud-bigquery/google/cloud/bigquery/table.py

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@
2121
import functools
2222
import operator
2323
import typing
24-
from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple, Union, Sequence
25-
2624
import warnings
25+
from typing import Any, Dict, Iterable, Iterator, List, Optional, Sequence, Tuple, Union
2726

2827
try:
2928
import pandas # type: ignore
@@ -56,30 +55,33 @@
5655
_read_wkt = wkt.loads
5756

5857
import google.api_core.exceptions
59-
from google.api_core.page_iterator import HTTPIterator
60-
6158
import google.cloud._helpers # type: ignore
62-
from google.cloud.bigquery import _helpers
63-
from google.cloud.bigquery import _pandas_helpers
64-
from google.cloud.bigquery import _versions_helpers
59+
from google.api_core.page_iterator import HTTPIterator
60+
from google.cloud.bigquery import (
61+
_helpers,
62+
_pandas_helpers,
63+
_string_references,
64+
_versions_helpers,
65+
external_config,
66+
)
6567
from google.cloud.bigquery import exceptions as bq_exceptions
68+
from google.cloud.bigquery import schema as _schema
6669
from google.cloud.bigquery._tqdm_helpers import get_progress_bar
6770
from google.cloud.bigquery.encryption_configuration import EncryptionConfiguration
6871
from google.cloud.bigquery.enums import DefaultPandasDTypes
6972
from google.cloud.bigquery.external_config import ExternalConfig
70-
from google.cloud.bigquery import schema as _schema
71-
from google.cloud.bigquery.schema import _build_schema_resource
72-
from google.cloud.bigquery.schema import _parse_schema_resource
73-
from google.cloud.bigquery.schema import _to_schema_fields
74-
from google.cloud.bigquery import external_config
75-
from google.cloud.bigquery import _string_references
73+
from google.cloud.bigquery.schema import (
74+
_build_schema_resource,
75+
_parse_schema_resource,
76+
_to_schema_fields,
77+
)
7678

7779
if typing.TYPE_CHECKING: # pragma: NO COVER
7880
# Unconditionally import optional dependencies again to tell pytype that
7981
# they are not None, avoiding false "no attribute" errors.
82+
import geopandas # type: ignore
8083
import pandas
8184
import pyarrow
82-
import geopandas # type: ignore
8385
from google.cloud import bigquery_storage # type: ignore
8486
from google.cloud.bigquery.dataset import DatasetReference
8587

@@ -2709,6 +2711,14 @@ def to_dataframe(
27092711
is not supported dtype.
27102712
27112713
"""
2714+
if not _versions_helpers.PANDAS_GBQ_VERSIONS.is_delegation_supported:
2715+
warnings.warn(
2716+
"Retrieving dataframes via the core client is deprecated. "
2717+
"Please install 'pandas-gbq' for the new high-performance backend.",
2718+
PendingDeprecationWarning,
2719+
stacklevel=2,
2720+
)
2721+
27122722
_pandas_helpers.verify_pandas_imports()
27132723

27142724
if geography_as_object and shapely is None:
@@ -2801,6 +2811,37 @@ def to_dataframe(
28012811
create_bqstorage_client = False
28022812
bqstorage_client = None
28032813

2814+
if _versions_helpers.PANDAS_GBQ_VERSIONS.is_delegation_supported:
2815+
import pandas_gbq # type: ignore
2816+
2817+
if self.client and hasattr(self.client, "_connection") and hasattr(self.client._connection, "_client_info"):
2818+
client_info = self.client._connection._client_info
2819+
if client_info:
2820+
ua = client_info.user_agent or ""
2821+
if "pandas-gbq" not in ua:
2822+
client_info.user_agent = f"{ua} pandas-gbq/{pandas_gbq.__version__}".strip()
2823+
2824+
return pandas_gbq.pandas.from_row_iterator(
2825+
self,
2826+
bqstorage_client=bqstorage_client,
2827+
dtypes=dtypes,
2828+
progress_bar_type=progress_bar_type,
2829+
create_bqstorage_client=create_bqstorage_client,
2830+
geography_as_object=geography_as_object,
2831+
bool_dtype=bool_dtype,
2832+
int_dtype=int_dtype,
2833+
float_dtype=float_dtype,
2834+
string_dtype=string_dtype,
2835+
date_dtype=date_dtype,
2836+
datetime_dtype=datetime_dtype,
2837+
time_dtype=time_dtype,
2838+
timestamp_dtype=timestamp_dtype,
2839+
range_date_dtype=range_date_dtype,
2840+
range_datetime_dtype=range_datetime_dtype,
2841+
range_timestamp_dtype=range_timestamp_dtype,
2842+
timeout=timeout,
2843+
)
2844+
28042845
record_batch = self.to_arrow(
28052846
progress_bar_type=progress_bar_type,
28062847
bqstorage_client=bqstorage_client,

0 commit comments

Comments
 (0)