Skip to content

Commit e143c1f

Browse files
committed
feat: instrument vscode, jupyter and 3p plugin usage
1 parent d525eba commit e143c1f

File tree

2 files changed

+107
-5
lines changed

2 files changed

+107
-5
lines changed

pandas_gbq/environment.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Copyright (c) 2025 pandas-gbq Authors All rights reserved.
2+
# Use of this source code is governed by a BSD-style
3+
# license that can be found in the LICENSE file.
4+
5+
6+
import importlib
7+
import json
8+
import os
9+
10+
# The identifier for GCP VS Code extension
11+
# https://cloud.google.com/code/docs/vscode/install
12+
GOOGLE_CLOUD_CODE_EXTENSION_NAME = "googlecloudtools.cloudcode"
13+
14+
15+
# The identifier for BigQuery Jupyter notebook plugin
16+
# https://cloud.google.com/bigquery/docs/jupyterlab-plugin
17+
BIGQUERY_JUPYTER_PLUGIN_NAME = "bigquery_jupyter_plugin"
18+
19+
20+
def _is_vscode_extension_installed(extension_id: str) -> bool:
21+
"""
22+
Checks if a given Visual Studio Code extension is installed.
23+
24+
Args:
25+
extension_id: The ID of the extension (e.g., "ms-python.python").
26+
27+
Returns:
28+
True if the extension is installed, False otherwise.
29+
"""
30+
try:
31+
# Determine the user's VS Code extensions directory.
32+
user_home = os.path.expanduser("~")
33+
if os.name == "nt": # Windows
34+
vscode_extensions_dir = os.path.join(user_home, ".vscode", "extensions")
35+
elif os.name == "posix": # macOS and Linux
36+
vscode_extensions_dir = os.path.join(user_home, ".vscode", "extensions")
37+
else:
38+
raise OSError("Unsupported operating system.")
39+
40+
# Check if the extensions directory exists.
41+
if os.path.exists(vscode_extensions_dir):
42+
# Iterate through the subdirectories in the extensions directory.
43+
for item in os.listdir(vscode_extensions_dir):
44+
item_path = os.path.join(vscode_extensions_dir, item)
45+
if os.path.isdir(item_path) and item.startswith(extension_id + "-"):
46+
# Check if the folder starts with the extension ID.
47+
# Further check for manifest file, as a more robust check.
48+
manifest_path = os.path.join(item_path, "package.json")
49+
if os.path.exists(manifest_path):
50+
try:
51+
with open(manifest_path, "r", encoding="utf-8") as f:
52+
json.load(f)
53+
return True
54+
except (FileNotFoundError, json.JSONDecodeError):
55+
# Corrupted or incomplete extension, or manifest missing.
56+
pass
57+
except Exception:
58+
pass
59+
60+
return False
61+
62+
63+
def _is_package_installed(package_name: str) -> bool:
64+
"""
65+
Checks if a Python package is installed.
66+
67+
Args:
68+
package_name: The name of the package to check (e.g., "requests", "numpy").
69+
70+
Returns:
71+
True if the package is installed, False otherwise.
72+
"""
73+
try:
74+
importlib.import_module(package_name)
75+
return True
76+
except Exception:
77+
return False
78+
79+
80+
def is_vscode() -> bool:
81+
return os.getenv("VSCODE_PID") is not None
82+
83+
84+
def is_jupyter() -> bool:
85+
return os.getenv("JPY_PARENT_PID") is not None
86+
87+
88+
def is_vscode_google_cloud_code_extension_installed() -> bool:
89+
return _is_vscode_extension_installed(GOOGLE_CLOUD_CODE_EXTENSION_NAME)
90+
91+
92+
def is_jupyter_bigquery_plugin_installed() -> bool:
93+
return _is_package_installed(BIGQUERY_JUPYTER_PLUGIN_NAME)

pandas_gbq/gbq_connector.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import numpy as np
1313

14+
from . import environment
15+
1416
# Only import at module-level at type checking time to avoid circular
1517
# dependencies in the pandas package, which has an optional dependency on
1618
# pandas-gbq.
@@ -517,11 +519,18 @@ def create_user_agent(
517519
)
518520
delimiter = "-"
519521

520-
identity = f"pandas{delimiter}{pd.__version__}"
522+
identities = [f"pandas{delimiter}{pd.__version__}"]
521523

522-
if user_agent is None:
523-
user_agent = identity
524-
else:
525-
user_agent = f"{user_agent} {identity}"
524+
if user_agent is not None:
525+
user_agent = identities.append(user_agent)
526+
527+
if environment.is_vscode():
528+
identities.append("vscode")
529+
if environment.is_vscode_google_cloud_code_extension_installed():
530+
identities.append(environment.GOOGLE_CLOUD_CODE_EXTENSION_NAME)
531+
elif environment.is_jupyter():
532+
identities.append("jupyter")
533+
if environment.is_jupyter_bigquery_plugin_installed():
534+
identities.append(environment.BIGQUERY_JUPYTER_PLUGIN_NAME)
526535

527536
return user_agent

0 commit comments

Comments
 (0)