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

Commit 344d5cf

Browse files
committed
Merge branch 'main' into shuowei-anywidget-cell-visual
2 parents 59222e7 + 61a9484 commit 344d5cf

File tree

43 files changed

+526
-421
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+526
-421
lines changed

README.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
:orphan:
2-
31
BigQuery DataFrames (BigFrames)
42
===============================
53

4+
65
|GA| |pypi| |versions|
76

87
BigQuery DataFrames (also known as BigFrames) provides a Pythonic DataFrame

bigframes/_tools/docs.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
def inherit_docs(source_class):
17+
"""
18+
A class decorator that copies docstrings from source_class to the
19+
decorated class for any methods or attributes that match names.
20+
"""
21+
22+
def decorator(target_class):
23+
if not target_class.__doc__ and source_class.__doc__:
24+
target_class.__doc__ = source_class.__doc__
25+
26+
for name, source_item in vars(source_class).items():
27+
if name in vars(target_class):
28+
target_item = getattr(target_class, name)
29+
30+
if hasattr(target_item, "__doc__") and not target_item.__doc__:
31+
if hasattr(source_item, "__doc__") and source_item.__doc__:
32+
try:
33+
target_item.__doc__ = source_item.__doc__
34+
except AttributeError:
35+
pass
36+
37+
return target_class
38+
39+
return decorator

bigframes/bigquery/_operations/ai.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ def generate_table(
606606
model: Union[bigframes.ml.base.BaseEstimator, str, pd.Series],
607607
data: Union[dataframe.DataFrame, series.Series, pd.DataFrame, pd.Series],
608608
*,
609-
output_schema: str,
609+
output_schema: Union[str, Mapping[str, str]],
610610
temperature: Optional[float] = None,
611611
top_p: Optional[float] = None,
612612
max_output_tokens: Optional[int] = None,
@@ -642,8 +642,10 @@ def generate_table(
642642
treated as the 'prompt' column. If a DataFrame is provided, it
643643
must contain a 'prompt' column, or you must rename the column you
644644
wish to generate table to 'prompt'.
645-
output_schema (str):
646-
A string defining the output schema (e.g., "col1 STRING, col2 INT64").
645+
output_schema (str | Mapping[str, str]):
646+
A string defining the output schema (e.g., "col1 STRING, col2 INT64"),
647+
or a mapping value that specifies the schema of the output, in the form {field_name: data_type}.
648+
Supported data types include `STRING`, `INT64`, `FLOAT64`, `BOOL`, `ARRAY`, and `STRUCT`.
647649
temperature (float, optional):
648650
A FLOAT64 value that is used for sampling promiscuity. The value
649651
must be in the range ``[0.0, 1.0]``.
@@ -666,8 +668,17 @@ def generate_table(
666668
model_name, session = bq_utils.get_model_name_and_session(model, data)
667669
table_sql = bq_utils.to_sql(data)
668670

671+
if isinstance(output_schema, Mapping):
672+
output_schema_str = ", ".join(
673+
[f"{name} {sql_type}" for name, sql_type in output_schema.items()]
674+
)
675+
# Validate user input
676+
output_schemas.parse_sql_fields(output_schema_str)
677+
else:
678+
output_schema_str = output_schema
679+
669680
struct_fields_bq: Dict[str, bigframes.core.sql.literals.STRUCT_VALUES] = {
670-
"output_schema": output_schema
681+
"output_schema": output_schema_str
671682
}
672683
if temperature is not None:
673684
struct_fields_bq["temperature"] = temperature

bigframes/core/compile/ibis_compiler/scalar_op_registry.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,7 @@ def isin_op_impl(x: ibis_types.Value, op: ops.IsInOp):
962962
# to actually cast it, as that could be lossy (eg float -> int)
963963
item_inferred_type = ibis_types.literal(item).type()
964964
if (
965-
x.type() == item_inferred_type
965+
x.type().name == item_inferred_type.name
966966
or x.type().is_numeric()
967967
and item_inferred_type.is_numeric()
968968
):
@@ -978,7 +978,7 @@ def isin_op_impl(x: ibis_types.Value, op: ops.IsInOp):
978978

979979
@scalar_op_compiler.register_unary_op(ops.ToDatetimeOp, pass_op=True)
980980
def to_datetime_op_impl(x: ibis_types.Value, op: ops.ToDatetimeOp):
981-
if x.type() == ibis_dtypes.str:
981+
if x.type() in (ibis_dtypes.str, ibis_dtypes.Timestamp("UTC")): # type: ignore
982982
return x.try_cast(ibis_dtypes.Timestamp(None)) # type: ignore
983983
else:
984984
# Numerical inputs.
@@ -1001,6 +1001,9 @@ def to_timestamp_op_impl(x: ibis_types.Value, op: ops.ToTimestampOp):
10011001
if op.format
10021002
else timestamp(x)
10031003
)
1004+
elif x.type() == ibis_dtypes.Timestamp(None): # type: ignore
1005+
1006+
return timestamp(x)
10041007
else:
10051008
# Numerical inputs.
10061009
if op.format:
@@ -2016,8 +2019,8 @@ def _ibis_num(number: float):
20162019

20172020

20182021
@ibis_udf.scalar.builtin
2019-
def timestamp(a: str) -> ibis_dtypes.timestamp: # type: ignore
2020-
"""Convert string to timestamp."""
2022+
def timestamp(a) -> ibis_dtypes.timestamp: # type: ignore
2023+
"""Convert string or a datetime to timestamp."""
20212024

20222025

20232026
@ibis_udf.scalar.builtin

bigframes/core/compile/sqlglot/expressions/comparison_ops.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,11 @@
3333
@register_unary_op(ops.IsInOp, pass_op=True)
3434
def _(expr: TypedExpr, op: ops.IsInOp) -> sge.Expression:
3535
values = []
36-
is_numeric_expr = dtypes.is_numeric(expr.dtype, include_bool=False)
3736
for value in op.values:
3837
if _is_null(value):
3938
continue
4039
dtype = dtypes.bigframes_type(type(value))
41-
if (
42-
expr.dtype == dtype
43-
or is_numeric_expr
44-
and dtypes.is_numeric(dtype, include_bool=False)
45-
):
40+
if dtypes.can_compare(expr.dtype, dtype):
4641
values.append(sge.convert(value))
4742

4843
if op.match_nulls:

bigframes/core/compile/sqlglot/expressions/datetime_ops.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ def _(expr: TypedExpr, op: ops.ToDatetimeOp) -> sge.Expression:
371371
)
372372
return sge.Cast(this=result, to="DATETIME")
373373

374-
if expr.dtype == dtypes.STRING_DTYPE:
374+
if expr.dtype in (dtypes.STRING_DTYPE, dtypes.TIMESTAMP_DTYPE):
375375
return sge.TryCast(this=expr.expr, to="DATETIME")
376376

377377
value = expr.expr
@@ -396,7 +396,7 @@ def _(expr: TypedExpr, op: ops.ToTimestampOp) -> sge.Expression:
396396
"PARSE_TIMESTAMP", sge.convert(op.format), expr.expr, sge.convert("UTC")
397397
)
398398

399-
if expr.dtype == dtypes.STRING_DTYPE:
399+
if expr.dtype in (dtypes.STRING_DTYPE, dtypes.DATETIME_DTYPE):
400400
return sge.func("TIMESTAMP", expr.expr)
401401

402402
value = expr.expr

bigframes/core/groupby/dataframe_group_by.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import pandas as pd
2525

2626
from bigframes import session
27+
from bigframes._tools import docs
2728
from bigframes.core import agg_expressions
2829
from bigframes.core import expression as ex
2930
import bigframes.core.block_transforms as block_ops
@@ -44,9 +45,8 @@
4445

4546

4647
@log_adapter.class_logger
47-
class DataFrameGroupBy(vendored_pandas_groupby.DataFrameGroupBy):
48-
__doc__ = vendored_pandas_groupby.GroupBy.__doc__
49-
48+
@docs.inherit_docs(vendored_pandas_groupby.DataFrameGroupBy)
49+
class DataFrameGroupBy:
5050
def __init__(
5151
self,
5252
block: blocks.Block,

bigframes/core/groupby/series_group_by.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import pandas
2525

2626
from bigframes import session
27+
from bigframes._tools import docs
2728
from bigframes.core import expression as ex
2829
import bigframes.core.block_transforms as block_ops
2930
import bigframes.core.blocks as blocks
@@ -43,9 +44,8 @@
4344

4445

4546
@log_adapter.class_logger
47+
@docs.inherit_docs(vendored_pandas_groupby.SeriesGroupBy)
4648
class SeriesGroupBy(vendored_pandas_groupby.SeriesGroupBy):
47-
__doc__ = vendored_pandas_groupby.GroupBy.__doc__
48-
4949
def __init__(
5050
self,
5151
block: blocks.Block,

bigframes/core/indexes/base.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import pandas
2828

2929
from bigframes import dtypes
30+
from bigframes._tools import docs
3031
import bigframes.core.agg_expressions as ex_types
3132
import bigframes.core.block_transforms as block_ops
3233
import bigframes.core.blocks as blocks
@@ -47,8 +48,8 @@
4748
import bigframes.series
4849

4950

50-
class Index(vendored_pandas_index.Index):
51-
__doc__ = vendored_pandas_index.Index.__doc__
51+
@docs.inherit_docs(vendored_pandas_index.Index)
52+
class Index:
5253
_query_job = None
5354
_block: blocks.Block
5455
_linked_frame: Union[
@@ -777,6 +778,11 @@ def to_list(self, *, allow_large_results: Optional[bool] = None) -> list:
777778
def __len__(self):
778779
return self.shape[0]
779780

781+
def __bool__(self):
782+
raise ValueError(
783+
"Cannot convert Index into bool. Consider using .empty(), .item(), .any(), or .all() methods."
784+
)
785+
780786
def item(self):
781787
# Docstring is in third_party/bigframes_vendored/pandas/core/indexes/base.py
782788
return self.to_series().peek(2).item()

bigframes/core/indexes/datetimes.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@
2020
datetimes as vendored_pandas_datetime_index,
2121
)
2222

23+
from bigframes._tools import docs
2324
from bigframes.core import expression as ex
2425
from bigframes.core.indexes.base import Index
2526
from bigframes.operations import date_ops
2627

2728

28-
class DatetimeIndex(Index, vendored_pandas_datetime_index.DatetimeIndex):
29-
__doc__ = vendored_pandas_datetime_index.DatetimeIndex.__doc__
29+
@docs.inherit_docs(vendored_pandas_datetime_index.DatetimeIndex)
30+
class DatetimeIndex(Index):
3031

3132
# Must be above 5000 for pandas to delegate to bigframes for binops
3233
__pandas_priority__ = 12000

0 commit comments

Comments
 (0)