Skip to content
This repository was archived by the owner on Mar 13, 2026. It is now read-only.

Commit e9840a2

Browse files
authored
Merge branch 'main' into renovate/all
2 parents 7a4ef44 + 746c61a commit e9840a2

16 files changed

Lines changed: 591 additions & 124 deletions

.coveragerc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17-
# Generated by synthtool. DO NOT EDIT!
1817
[run]
1918
branch = True
2019
omit =
2120
google/__init__.py
2221
google/cloud/__init__.py
2322

2423
[report]
25-
fail_under = 96
24+
fail_under = 95
2625
show_missing = True
2726
exclude_lines =
2827
# Re-enable the standard pragma
@@ -34,6 +33,5 @@ exclude_lines =
3433
omit =
3534
*/gapic/*.py
3635
*/proto/*.py
37-
*/core/*.py
3836
*/site-packages/*.py
3937
google/cloud/__init__.py

.github/workflows/unittest.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,4 @@ jobs:
5858
run: |
5959
find .coverage-results -type f -name '*.zip' -exec unzip {} \;
6060
coverage combine .coverage-results/**/.coverage*
61-
coverage report --show-missing --fail-under=96
61+
coverage report --show-missing --fail-under=95

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ docs.metadata
5151
# Virtual environment
5252
env/
5353
venv/
54+
.venv/
5455

5556
# Test logs
5657
coverage.xml

.librarian/state.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:c8612d3fffb3f6a32353b2d1abd16b61e87811866f7ec9d65b59b02eb452a620
22
libraries:
33
- id: pandas-gbq
4-
version: 0.33.0
4+
version: 0.34.0
55
last_generated_commit: ""
66
apis: []
77
source_roots:

CHANGELOG.md

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

55
[1]: https://pypi.org/project/pandas-gbq/#history
66

7+
## [0.34.0](https://github.com/googleapis/google-cloud-python/compare/pandas-gbq-v0.33.0...pandas-gbq-v0.34.0) (2026-03-05)
8+
9+
10+
### Documentation
11+
12+
* Update bigframes links to new homepage (#1028) ([72f772ec7b36ba081ed18ef4409f8262a412a1b9](https://github.com/googleapis/google-cloud-python/commit/72f772ec7b36ba081ed18ef4409f8262a412a1b9))
13+
14+
15+
### Features
16+
17+
* support biglake tables in pandas_gbq.sample (#1014) ([16d08696f251781f5d84c509844ef05f98ec8dd0](https://github.com/googleapis/google-cloud-python/commit/16d08696f251781f5d84c509844ef05f98ec8dd0))
18+
19+
20+
### Bug Fixes
21+
22+
* support pandas 3.0 (prerelease) (#998) ([4ea2d57f45c6685c13b8258923b4326fdd3b71d1](https://github.com/googleapis/google-cloud-python/commit/4ea2d57f45c6685c13b8258923b4326fdd3b71d1))
23+
724
## [0.33.0](https://github.com/googleapis/python-bigquery-pandas/compare/v0.32.0...v0.33.0) (2026-01-05)
825

926

noxfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ def cover(session):
405405
test runs (not system test runs), and then erases coverage data.
406406
"""
407407
session.install("coverage", "pytest-cov")
408-
session.run("coverage", "report", "--show-missing", "--fail-under=96")
408+
session.run("coverage", "report", "--show-missing", "--fail-under=95")
409409

410410
# Make sure there is no dead code in our test directories.
411411
session.run(

pandas_gbq/core/biglake.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright (c) 2026 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+
Utilities for working with BigLake tables.
7+
"""
8+
9+
# TODO(tswast): Synchronize with bigframes/session/iceberg.py, which uses
10+
# pyiceberg and the BigLake APIs, rather than relying on dry run.
11+
12+
from __future__ import annotations
13+
14+
import dataclasses
15+
from typing import Sequence
16+
17+
import google.cloud.bigquery
18+
19+
import pandas_gbq.core.resource_references
20+
21+
22+
_DRY_RUN_TEMPLATE = """
23+
SELECT *
24+
FROM `{project}.{catalog}.{namespace}.{table}`
25+
"""
26+
27+
28+
_COUNT_TEMPLATE = """
29+
SELECT COUNT(*) as total_rows
30+
FROM `{project}.{catalog}.{namespace}.{table}`
31+
"""
32+
33+
34+
@dataclasses.dataclass(frozen=True)
35+
class BigLakeTableMetadata:
36+
schema: Sequence[google.cloud.bigquery.SchemaField]
37+
num_rows: int
38+
39+
40+
def get_table_metadata(
41+
*,
42+
reference: pandas_gbq.core.resource_references.BigLakeTableId,
43+
bqclient: google.cloud.bigquery.Client,
44+
) -> BigLakeTableMetadata:
45+
"""
46+
Get the schema for a BigLake table.
47+
48+
Currently, this does some BigQuery queries. In the future, we'll want to get
49+
other metadata like the number of rows and storage bytes so that we can do a
50+
more accurate estimate of how many rows to sample.
51+
"""
52+
dry_run_config = google.cloud.bigquery.QueryJobConfig(dry_run=True)
53+
query = _DRY_RUN_TEMPLATE.format(
54+
project=reference.project,
55+
catalog=reference.catalog,
56+
namespace=".".join(reference.namespace),
57+
table=reference.table,
58+
)
59+
job = bqclient.query(query, job_config=dry_run_config)
60+
job.result()
61+
schema = job.schema
62+
63+
count_rows = list(
64+
bqclient.query_and_wait(
65+
_COUNT_TEMPLATE.format(
66+
project=reference.project,
67+
catalog=reference.catalog,
68+
namespace=".".join(reference.namespace),
69+
table=reference.table,
70+
)
71+
)
72+
)
73+
assert (
74+
len(count_rows) == 1
75+
), "got unexpected query response when determining number of rows"
76+
total_rows = count_rows[0].total_rows
77+
78+
return BigLakeTableMetadata(
79+
schema=schema if schema is not None else [],
80+
num_rows=total_rows,
81+
)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Copyright (c) 2026 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+
import dataclasses
6+
import re
7+
from typing import Union
8+
9+
10+
_TABLE_REFEREENCE_PATTERN = re.compile(
11+
# In the past, organizations could prefix their project IDs with a domain
12+
# name. Such projects still exist, especially at Google.
13+
r"^(?P<legacy_project_domain>[^:]+:)?"
14+
r"(?P<project>[^.]+)\."
15+
# Match dataset or catalog + namespace.
16+
#
17+
# Namespace could be arbitrarily deeply nested in Iceberg/BigLake. Support
18+
# this without catastrophic backtracking by moving the trailing "." to the
19+
# table group.
20+
r"(?P<inner_parts>.*)"
21+
# Table names can't contain ".", as that's used as the separator.
22+
r"\.(?P<table>[^.]+)$"
23+
)
24+
25+
26+
@dataclasses.dataclass(frozen=True)
27+
class BigLakeTableId:
28+
project: str
29+
catalog: str
30+
namespace: tuple[str, ...]
31+
table: str
32+
33+
34+
@dataclasses.dataclass(frozen=True)
35+
class BigQueryTableId:
36+
project_id: str
37+
dataset_id: str
38+
table_id: str
39+
40+
41+
def parse_table_id(table_id: str) -> Union[BigLakeTableId, BigQueryTableId]:
42+
"""Turn a string into a BigLakeTableId or BigQueryTableId.
43+
44+
Raises:
45+
ValueError: If the table ID is invalid.
46+
"""
47+
regex_match = _TABLE_REFEREENCE_PATTERN.match(table_id)
48+
if not regex_match:
49+
raise ValueError(f"Invalid table ID: {table_id}")
50+
51+
inner_parts = regex_match.group("inner_parts").split(".")
52+
if any(part == "" for part in inner_parts):
53+
raise ValueError(f"Invalid table ID: {table_id}")
54+
55+
if len(inner_parts) == 1:
56+
return BigQueryTableId(
57+
project_id=regex_match.group("project"),
58+
dataset_id=inner_parts[0],
59+
table_id=regex_match.group("table"),
60+
)
61+
62+
return BigLakeTableId(
63+
project=regex_match.group("project"),
64+
catalog=inner_parts[0],
65+
namespace=tuple(inner_parts[1:]),
66+
table=regex_match.group("table"),
67+
)

0 commit comments

Comments
 (0)