Skip to content

feat: add user-agent delegation telemetry for pandas-gbq - #18184

Open
shuoweil wants to merge 4 commits into
mainfrom
shuowei-gbq-delegation-telemetry-mvp
Open

feat: add user-agent delegation telemetry for pandas-gbq#18184
shuoweil wants to merge 4 commits into
mainfrom
shuowei-gbq-delegation-telemetry-mvp

Conversation

@shuoweil

@shuoweil shuoweil commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Appends pandas-gbq/<version> to ClientInfo.user_agent when RowIterator.to_dataframe() or RowIterator.to_geodataframe() is executed and query delegation is supported by pandas-gbq.

This PR enables backend metrics and BigQuery audit logs to differentiate between direct client-side conversion and delegated conversions, without altering existing DataFrame results or runtime behavior.

Supersedes #17704.

Key Changes

  • google/cloud/bigquery/table.py: Injects pandas-gbq/<version> into client_info.user_agent with deduplication check when PANDAS_GBQ_VERSIONS.is_delegation_supported is True.
  • tests/unit/test_table.py: Adds unit tests for user-agent injection, deduplication, missing client_info/user_agent, and unsupported fallback behavior.

Fixes #<540939659> 🦕

@shuoweil
shuoweil requested review from a team as code owners August 21, 2026 00:03
@shuoweil
shuoweil requested review from tswast and removed request for a team August 21, 2026 00:03

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the to_dataframe method in table.py to append pandas-gbq telemetry to the client's user agent when delegation is supported, and adds comprehensive unit tests to verify this behavior. The review feedback suggests wrapping the user-agent update logic in a try-except block to ensure robust defensive programming and prevent potential crashes. Additionally, it recommends mocking installed_version in the geodataframe tests to avoid import errors in environments where pandas-gbq is not installed.

Comment on lines +2994 to +3002
if _versions_helpers.PANDAS_GBQ_VERSIONS.is_delegation_supported:
client_info = getattr(
getattr(self.client, "_connection", None), "_client_info", None
)
if client_info:
ua = client_info.user_agent or ""
if "pandas-gbq" not in ua:
version = _versions_helpers.PANDAS_GBQ_VERSIONS.installed_version
client_info.user_agent = f"{ua} pandas-gbq/{version}".strip()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Telemetry and user-agent updates should be treated as best-effort operations. If client_info is a mock, a custom read-only object, or lacks the user_agent attribute, accessing or modifying client_info.user_agent directly could raise an AttributeError or other exceptions, causing the core to_dataframe() call to crash.

To ensure robust defensive programming, we should wrap this telemetry update in a try-except block and use getattr to safely access user_agent. Avoid silently passing on exceptions; instead, log the exception using logger.warning to aid in debugging and prevent masking underlying issues.

        if _versions_helpers.PANDAS_GBQ_VERSIONS.is_delegation_supported:
            try:
                client_info = getattr(
                    getattr(self.client, "_connection", None), "_client_info", None
                )
                if client_info:
                    ua = getattr(client_info, "user_agent", None) or ""
                    if "pandas-gbq" not in ua:
                        version = _versions_helpers.PANDAS_GBQ_VERSIONS.installed_version
                        client_info.user_agent = f"{ua} pandas-gbq/{version}".strip()
            except Exception as exc:
                logger.warning("Failed to update telemetry user-agent: %s", exc)
References
  1. Avoid broad except Exception: blocks that silently return None. Instead, log the exception (e.g., using logger.warning) to aid in debugging and prevent masking underlying issues.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread packages/google-cloud-bigquery/tests/unit/test_table.py
shuoweil and others added 2 commits August 20, 2026 17:09
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@shuoweil
shuoweil requested a review from sycai August 21, 2026 00:21
)
client_info.user_agent = f"{ua} pandas-gbq/{version}".strip()
except Exception as exc:
_LOGGER.warning("Failed to update telemetry user-agent: %s", exc)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this logging? I think it would print unwanted contents on the user's terminal or notebook, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Telemetry injection is best-effort and shouldn't emit user-facing warnings on terminals or notebooks if mutating client_info fails. I will downgrade _LOGGER.warning to _LOGGER.debug and update the corresponding unit test.

Comment on lines 2995 to +2996

if _versions_helpers.PANDAS_GBQ_VERSIONS.is_delegation_supported:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you share the link to the code where pandas-gbq is used as the delegation?

@shuoweil shuoweil Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Phase 1 (Storage Read MVP), delegation is implemented in google-cloud-bigquery-storage ReadRowsPage.to_arrow() (merged in PR #17938 / v2.40.0), which delegates row decoding directly to pandas_gbq.arrow.from_read_rows_response() (released in pandas-gbq v0.35.1 via PR #17958).

When RowIterator.to_dataframe() or to_geodataframe() runs, it calls self.to_arrow() using the Storage Read client, executing through this delegation path.

_versions_helpers.PANDAS_GBQ_VERSIONS.is_delegation_supported (merged in PR #17957) checks _internal_delegation_api_version to safely gate the pandas-gbq/{version} user-agent telemetry decoration so we can track delegated usage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants