Skip to content

Commit d43457b

Browse files
feat: adds the capability to include custom user agent string (#819)
* adds the capability to include customer user agent string * Adds deprecation warning * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * clarifies purpose and asert for two tests regarding old verbose arg * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * updates comment regarding verbosity * Update pandas_gbq/gbq.py --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent b3faa0b commit d43457b

File tree

3 files changed

+120
-4
lines changed

3 files changed

+120
-4
lines changed

pandas_gbq/gbq.py

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ def __init__(
267267
auth_redirect_uri=None,
268268
client_id=None,
269269
client_secret=None,
270+
user_agent=None,
271+
rfc9110_delimiter=False,
270272
):
271273
global context
272274
from google.api_core.exceptions import ClientError, GoogleAPIError
@@ -284,6 +286,8 @@ def __init__(
284286
self.auth_redirect_uri = auth_redirect_uri
285287
self.client_id = client_id
286288
self.client_secret = client_secret
289+
self.user_agent = user_agent
290+
self.rfc9110_delimiter = rfc9110_delimiter
287291

288292
default_project = None
289293

@@ -337,11 +341,15 @@ def log_elapsed_seconds(self, prefix="Elapsed", postfix="s.", overlong=6):
337341

338342
def get_client(self):
339343
import google.api_core.client_info
340-
import pandas
341344

342345
bigquery = FEATURES.bigquery_try_import()
346+
347+
user_agent = create_user_agent(
348+
user_agent=self.user_agent, rfc9110_delimiter=self.rfc9110_delimiter
349+
)
350+
343351
client_info = google.api_core.client_info.ClientInfo(
344-
user_agent="pandas-{}".format(pandas.__version__)
352+
user_agent=user_agent,
345353
)
346354
return bigquery.Client(
347355
project=self.project_id,
@@ -961,6 +969,8 @@ def to_gbq(
961969
auth_redirect_uri=None,
962970
client_id=None,
963971
client_secret=None,
972+
user_agent=None,
973+
rfc9110_delimiter=False,
964974
):
965975
"""Write a DataFrame to a Google BigQuery table.
966976
@@ -1072,6 +1082,13 @@ def to_gbq(
10721082
client_secret : str
10731083
The Client Secret associated with the Client ID for the Google Cloud Project
10741084
the user is attempting to connect to.
1085+
user_agent : str
1086+
Custom user agent string used as a prefix to the pandas version.
1087+
rfc9110_delimiter : bool
1088+
Sets user agent delimiter to a hyphen or a slash.
1089+
Default is False, meaning a hyphen will be used.
1090+
1091+
.. versionadded:: 0.23.3
10751092
"""
10761093

10771094
_test_google_api_imports()
@@ -1130,6 +1147,8 @@ def to_gbq(
11301147
auth_redirect_uri=auth_redirect_uri,
11311148
client_id=client_id,
11321149
client_secret=client_secret,
1150+
user_agent=user_agent,
1151+
rfc9110_delimiter=rfc9110_delimiter,
11331152
)
11341153
bqclient = connector.client
11351154

@@ -1409,3 +1428,59 @@ def create(self, dataset_id):
14091428
self.client.create_dataset(dataset)
14101429
except self.http_error as ex:
14111430
self.process_http_error(ex)
1431+
1432+
1433+
def create_user_agent(
1434+
user_agent: Optional[str] = None, rfc9110_delimiter: bool = False
1435+
) -> str:
1436+
"""Creates a user agent string.
1437+
1438+
The legacy format of our the user agent string was: `product-x.y.z` (where x,
1439+
y, and z are the major, minor, and micro version numbers).
1440+
1441+
Users are able to prepend this string with their own user agent identifier
1442+
to render something similar to `<my_user_agent> pandas-x.y.z`.
1443+
1444+
The legacy format used a hyphen to separate the product from the product
1445+
version which differs slightly from the format recommended by RFC9110, which is:
1446+
`product/x.y.z`. To produce a user agent more in line with the RFC, set
1447+
rfc9110_delimiter to True. This setting does not depend on whether a
1448+
user_agent is also supplied.
1449+
1450+
Reference:
1451+
https://www.rfc-editor.org/info/rfc9110
1452+
1453+
Args:
1454+
user_agent (Optional[str]): User agent string.
1455+
1456+
rfc9110_delimiter (Optional[bool]): Sets delimiter to a hyphen or a slash.
1457+
Default is False, meaning a hyphen will be used.
1458+
1459+
Returns (str):
1460+
Customized user agent string.
1461+
1462+
Deprecation Warning:
1463+
In a future major release, the default delimiter will be changed to
1464+
a `/` in accordance with RFC9110.
1465+
"""
1466+
import pandas as pd
1467+
1468+
if rfc9110_delimiter:
1469+
delimiter = "/"
1470+
else:
1471+
warnings.warn(
1472+
"In a future major release, the default delimiter will be "
1473+
"changed to a `/` in accordance with RFC9110.",
1474+
PendingDeprecationWarning,
1475+
stacklevel=2,
1476+
)
1477+
delimiter = "-"
1478+
1479+
identity = f"pandas{delimiter}{pd.__version__}"
1480+
1481+
if user_agent is None:
1482+
user_agent = identity
1483+
else:
1484+
user_agent = f"{user_agent} {identity}"
1485+
1486+
return user_agent

tests/unit/test_gbq.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,15 @@ def test_read_gbq_wo_verbose_w_new_pandas_no_warnings(monkeypatch, recwarn):
635635
mock.PropertyMock(return_value=False),
636636
)
637637
gbq.read_gbq("SELECT 1", project_id="my-project", dialect="standard")
638-
assert len(recwarn) == 0
638+
# This test was intended to check for warnings about the deprecation of
639+
# the argument `verbose` (which was removed from gbq (~v0.4.0) and
640+
# pandas (~v0.23.0). (See https://github.com/googleapis/python-bigquery-pandas/pull/158/files)
641+
# This test should not fail upon seeing a warning in regards to a pending
642+
# deprecation related to rfc9110 delimiters.
643+
# TODO this and related tests have likely outlived their usefulness,
644+
# consider removing.
645+
for warning in recwarn.list:
646+
assert "delimiter" in str(warning.message)
639647

640648

641649
def test_read_gbq_with_old_bq_raises_importerror(monkeypatch):
@@ -660,7 +668,15 @@ def test_read_gbq_with_verbose_old_pandas_no_warnings(monkeypatch, recwarn):
660668
dialect="standard",
661669
verbose=True,
662670
)
663-
assert len(recwarn) == 0
671+
# This test was intended to check for warnings about the deprecation of
672+
# the argument `verbose` (which was removed from gbq (~v0.4.0) and
673+
# pandas (~v0.23.0). (See https://github.com/googleapis/python-bigquery-pandas/pull/158/files)
674+
# This test should not fail upon seeing a warning in regards to a pending
675+
# deprecation related to rfc9110 delimiters.
676+
# TODO this and related tests have likely outlived their usefulness,
677+
# consider removing.
678+
for warning in recwarn.list:
679+
assert "delimiter" in str(warning.message)
664680

665681

666682
def test_read_gbq_with_private_raises_notimplmentederror():

tests/unit/test_to_gbq.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import google.api_core.exceptions
66
import google.cloud.bigquery
7+
import pandas as pd
78
from pandas import DataFrame
89
import pytest
910

@@ -158,3 +159,27 @@ def test_to_gbq_with_if_exists_unknown():
158159
project_id="myproj",
159160
if_exists="unknown",
160161
)
162+
163+
164+
@pytest.mark.parametrize(
165+
"user_agent,rfc9110_delimiter,expected",
166+
[
167+
(
168+
"test_user_agent/2.0.42",
169+
False,
170+
f"test_user_agent/2.0.42 pandas-{pd.__version__}",
171+
),
172+
(None, False, f"pandas-{pd.__version__}"),
173+
(
174+
"test_user_agent/2.0.42",
175+
True,
176+
f"test_user_agent/2.0.42 pandas/{pd.__version__}",
177+
),
178+
(None, True, f"pandas/{pd.__version__}"),
179+
],
180+
)
181+
def test_create_user_agent(user_agent, rfc9110_delimiter, expected):
182+
from pandas_gbq.gbq import create_user_agent
183+
184+
result = create_user_agent(user_agent, rfc9110_delimiter)
185+
assert result == expected

0 commit comments

Comments
 (0)