From a7877ae86c3a1d0b49a68775ab9dc3ad70196dca Mon Sep 17 00:00:00 2001 From: Trevor Bergeron Date: Wed, 18 Feb 2026 23:06:33 +0000 Subject: [PATCH 1/6] refactor: Decorator to take docs rather than inheritance --- bigframes/_tools/docs.py | 48 ++++++++ bigframes/core/groupby/dataframe_group_by.py | 6 +- bigframes/core/groupby/series_group_by.py | 4 +- bigframes/core/indexes/base.py | 5 +- bigframes/core/indexes/datetimes.py | 5 +- bigframes/core/indexes/multi.py | 6 +- bigframes/core/window/rolling.py | 6 +- bigframes/dataframe.py | 116 +------------------ bigframes/geopandas/geoseries.py | 6 +- bigframes/ml/base.py | 4 +- bigframes/ml/model_selection.py | 6 +- bigframes/operations/lists.py | 6 +- bigframes/operations/plotting.py | 6 +- bigframes/operations/strings.py | 6 +- bigframes/operations/structs.py | 9 +- bigframes/series.py | 72 +----------- 16 files changed, 94 insertions(+), 217 deletions(-) create mode 100644 bigframes/_tools/docs.py diff --git a/bigframes/_tools/docs.py b/bigframes/_tools/docs.py new file mode 100644 index 00000000000..22b137f8f35 --- /dev/null +++ b/bigframes/_tools/docs.py @@ -0,0 +1,48 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def inherit_docs(source_class): + """ + A class decorator that copies docstrings from source_class to the + decorated class for any methods or attributes that match names. + """ + + def decorator(target_class): + # 1. Steal the main class docstring if the target doesn't have one + if not target_class.__doc__ and source_class.__doc__: + target_class.__doc__ = source_class.__doc__ + + # 2. Iterate over all attributes in the source class + for name, source_item in vars(source_class).items(): + # Check if the target class has the same attribute + if name in vars(target_class): + target_item = getattr(target_class, name) + + # Only copy if the target doesn't have a docstring + # and the source does + if hasattr(target_item, "__doc__") and not target_item.__doc__: + if hasattr(source_item, "__doc__") and source_item.__doc__: + try: + # Use functools.update_wrapper or manual assignment + # for methods, properties, and static methods + target_item.__doc__ = source_item.__doc__ + except AttributeError: + # Read-only attributes or certain built-ins + # might skip docstring assignment + pass + + return target_class + + return decorator diff --git a/bigframes/core/groupby/dataframe_group_by.py b/bigframes/core/groupby/dataframe_group_by.py index 7f9e5d627ab..d61eceb1783 100644 --- a/bigframes/core/groupby/dataframe_group_by.py +++ b/bigframes/core/groupby/dataframe_group_by.py @@ -24,6 +24,7 @@ import pandas as pd from bigframes import session +from bigframes._tools import docs from bigframes.core import agg_expressions from bigframes.core import expression as ex import bigframes.core.block_transforms as block_ops @@ -44,9 +45,8 @@ @log_adapter.class_logger -class DataFrameGroupBy(vendored_pandas_groupby.DataFrameGroupBy): - __doc__ = vendored_pandas_groupby.GroupBy.__doc__ - +@docs.inherit_docs(vendored_pandas_groupby.DataFrameGroupBy) +class DataFrameGroupBy: def __init__( self, block: blocks.Block, diff --git a/bigframes/core/groupby/series_group_by.py b/bigframes/core/groupby/series_group_by.py index a8900cf5455..221c5ecef9c 100644 --- a/bigframes/core/groupby/series_group_by.py +++ b/bigframes/core/groupby/series_group_by.py @@ -24,6 +24,7 @@ import pandas from bigframes import session +from bigframes._tools import docs from bigframes.core import expression as ex import bigframes.core.block_transforms as block_ops import bigframes.core.blocks as blocks @@ -43,9 +44,8 @@ @log_adapter.class_logger +@docs.inherit_docs(vendored_pandas_groupby.SeriesGroupBy) class SeriesGroupBy(vendored_pandas_groupby.SeriesGroupBy): - __doc__ = vendored_pandas_groupby.GroupBy.__doc__ - def __init__( self, block: blocks.Block, diff --git a/bigframes/core/indexes/base.py b/bigframes/core/indexes/base.py index 383534fa4df..9e72cd533c1 100644 --- a/bigframes/core/indexes/base.py +++ b/bigframes/core/indexes/base.py @@ -27,6 +27,7 @@ import pandas from bigframes import dtypes +from bigframes._tools import docs import bigframes.core.agg_expressions as ex_types import bigframes.core.block_transforms as block_ops import bigframes.core.blocks as blocks @@ -47,8 +48,8 @@ import bigframes.series -class Index(vendored_pandas_index.Index): - __doc__ = vendored_pandas_index.Index.__doc__ +@docs.inherit_docs(vendored_pandas_index.Index) +class Index: _query_job = None _block: blocks.Block _linked_frame: Union[ diff --git a/bigframes/core/indexes/datetimes.py b/bigframes/core/indexes/datetimes.py index 23ad8b03b4d..ec5174e8a9a 100644 --- a/bigframes/core/indexes/datetimes.py +++ b/bigframes/core/indexes/datetimes.py @@ -20,13 +20,14 @@ datetimes as vendored_pandas_datetime_index, ) +from bigframes._tools import docs from bigframes.core import expression as ex from bigframes.core.indexes.base import Index from bigframes.operations import date_ops -class DatetimeIndex(Index, vendored_pandas_datetime_index.DatetimeIndex): - __doc__ = vendored_pandas_datetime_index.DatetimeIndex.__doc__ +@docs.inherit_docs(vendored_pandas_datetime_index.DatetimeIndex) +class DatetimeIndex(Index): # Must be above 5000 for pandas to delegate to bigframes for binops __pandas_priority__ = 12000 diff --git a/bigframes/core/indexes/multi.py b/bigframes/core/indexes/multi.py index cfabd9e70d1..d9f26ea8730 100644 --- a/bigframes/core/indexes/multi.py +++ b/bigframes/core/indexes/multi.py @@ -19,6 +19,7 @@ import bigframes_vendored.pandas.core.indexes.multi as vendored_pandas_multindex import pandas +from bigframes._tools import docs from bigframes.core import blocks from bigframes.core import expression as ex from bigframes.core.indexes.base import Index @@ -27,9 +28,8 @@ import bigframes.session -class MultiIndex(Index, vendored_pandas_multindex.MultiIndex): - __doc__ = vendored_pandas_multindex.MultiIndex.__doc__ - +@docs.inherit_docs(vendored_pandas_multindex.MultiIndex) +class MultiIndex(Index): @classmethod def from_tuples( cls, diff --git a/bigframes/core/window/rolling.py b/bigframes/core/window/rolling.py index b7bb62372cc..97af59edf5b 100644 --- a/bigframes/core/window/rolling.py +++ b/bigframes/core/window/rolling.py @@ -22,6 +22,7 @@ import pandas from bigframes import dtypes +from bigframes._tools import docs from bigframes.core import agg_expressions from bigframes.core import expression as ex from bigframes.core import ordering, utils, window_spec @@ -36,9 +37,8 @@ @log_adapter.class_logger -class Window(vendored_pandas_rolling.Window): - __doc__ = vendored_pandas_rolling.Window.__doc__ - +@docs.inherit_docs(vendored_pandas_rolling.Window) +class Window: def __init__( self, block: blocks.Block, diff --git a/bigframes/dataframe.py b/bigframes/dataframe.py index b195ce9902d..b4777c6745c 100644 --- a/bigframes/dataframe.py +++ b/bigframes/dataframe.py @@ -53,6 +53,7 @@ import pyarrow import tabulate +from bigframes._tools import docs import bigframes.constants import bigframes.core from bigframes.core import agg_expressions @@ -120,8 +121,8 @@ # Inherits from pandas DataFrame so that we can use the same docstrings. @log_adapter.class_logger -class DataFrame(vendored_pandas_frame.DataFrame): - __doc__ = vendored_pandas_frame.DataFrame.__doc__ +@docs.inherit_docs(vendored_pandas_frame.DataFrame) +class DataFrame: # internal flag to disable cache at all _disable_cache_override: bool = False # Must be above 5000 for pandas to delegate to bigframes for binops @@ -387,8 +388,6 @@ def __len__(self): rows, _ = self.shape return rows - __len__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__len__) - def __iter__(self): return iter(self.columns) @@ -599,45 +598,6 @@ def select_dtypes(self, include=None, exclude=None) -> DataFrame: def _set_internal_query_job(self, query_job: Optional[bigquery.QueryJob]): self._query_job = query_job - @overload - def __getitem__( - self, - key: bigframes.series.Series, - ) -> DataFrame: - ... - - @overload - def __getitem__( - self, - key: slice, - ) -> DataFrame: - ... - - @overload - def __getitem__( - self, - key: List[str], - ) -> DataFrame: - ... - - @overload - def __getitem__( - self, - key: List[blocks.Label], - ) -> DataFrame: - ... - - @overload - def __getitem__(self, key: pandas.Index) -> DataFrame: - ... - - @overload - def __getitem__( - self, - key: blocks.Label, - ) -> bigframes.series.Series: - ... - def __getitem__( self, key: Union[ @@ -670,8 +630,6 @@ def __getitem__( # TODO(tswast): What case is this supposed to be handling? return self._getitem_columns([cast(Hashable, key)]) - __getitem__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__getitem__) - def _getitem_columns(self, key: Sequence[blocks.Label]) -> DataFrame: selected_ids: Tuple[str, ...] = () for label in key: @@ -858,8 +816,6 @@ def __setitem__( df = self._assign_single_item(key, value) self._set_block(df._get_block()) - __setitem__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__setitem__) - def _apply_binop( self, other: float | int | bigframes.series.Series | DataFrame, @@ -970,53 +926,39 @@ def eq(self, other: typing.Any, axis: str | int = "columns") -> DataFrame: def __eq__(self, other) -> DataFrame: # type: ignore return self.eq(other) - __eq__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__eq__) - def ne(self, other: typing.Any, axis: str | int = "columns") -> DataFrame: return self._apply_binop(other, ops.ne_op, axis=axis) def __ne__(self, other) -> DataFrame: # type: ignore return self.ne(other) - __ne__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__ne__) - def __invert__(self) -> DataFrame: return self._apply_unary_op(ops.invert_op) - __invert__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__invert__) - def le(self, other: typing.Any, axis: str | int = "columns") -> DataFrame: return self._apply_binop(other, ops.le_op, axis=axis) def __le__(self, other) -> DataFrame: return self.le(other) - __le__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__le__) - def lt(self, other: typing.Any, axis: str | int = "columns") -> DataFrame: return self._apply_binop(other, ops.lt_op, axis=axis) def __lt__(self, other) -> DataFrame: return self.lt(other) - __lt__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__lt__) - def ge(self, other: typing.Any, axis: str | int = "columns") -> DataFrame: return self._apply_binop(other, ops.ge_op, axis=axis) def __ge__(self, other) -> DataFrame: return self.ge(other) - __ge__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__ge__) - def gt(self, other: typing.Any, axis: str | int = "columns") -> DataFrame: return self._apply_binop(other, ops.gt_op, axis=axis) def __gt__(self, other) -> DataFrame: return self.gt(other) - __gt__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__gt__) - def add( self, other: float | int | bigframes.series.Series | DataFrame, @@ -1038,13 +980,9 @@ def radd( def __add__(self, other) -> DataFrame: return self.add(other) - __add__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__add__) - def __radd__(self, other) -> DataFrame: return self.radd(other) - __radd__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__radd__) - def sub( self, other: float | int | bigframes.series.Series | DataFrame, @@ -1053,13 +991,10 @@ def sub( return self._apply_binop(other, ops.sub_op, axis=axis) subtract = sub - subtract.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.sub) def __sub__(self, other): return self.sub(other) - __sub__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__sub__) - def rsub( self, other: float | int | bigframes.series.Series | DataFrame, @@ -1070,8 +1005,6 @@ def rsub( def __rsub__(self, other): return self.rsub(other) - __rsub__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__rsub__) - def mul( self, other: float | int | bigframes.series.Series | DataFrame, @@ -1080,13 +1013,10 @@ def mul( return self._apply_binop(other, ops.mul_op, axis=axis) multiply = mul - multiply.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.mul) def __mul__(self, other): return self.mul(other) - __mul__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__mul__) - def rmul( self, other: float | int | bigframes.series.Series | DataFrame, @@ -1097,8 +1027,6 @@ def rmul( def __rmul__(self, other): return self.rmul(other) - __rmul__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__rmul__) - def truediv( self, other: float | int | bigframes.series.Series | DataFrame, @@ -1106,14 +1034,11 @@ def truediv( ) -> DataFrame: return self._apply_binop(other, ops.div_op, axis=axis) - truediv.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.truediv) div = divide = truediv def __truediv__(self, other): return self.truediv(other) - __truediv__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__truediv__) - def rtruediv( self, other: float | int | bigframes.series.Series | DataFrame, @@ -1122,13 +1047,10 @@ def rtruediv( return self._apply_binop(other, ops.div_op, axis=axis, reverse=True) rdiv = rtruediv - rdiv.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.rtruediv) def __rtruediv__(self, other): return self.rtruediv(other) - __rtruediv__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__rtruediv__) - def floordiv( self, other: float | int | bigframes.series.Series | DataFrame, @@ -1139,8 +1061,6 @@ def floordiv( def __floordiv__(self, other): return self.floordiv(other) - __floordiv__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__floordiv__) - def rfloordiv( self, other: float | int | bigframes.series.Series | DataFrame, @@ -1151,26 +1071,18 @@ def rfloordiv( def __rfloordiv__(self, other): return self.rfloordiv(other) - __rfloordiv__.__doc__ = inspect.getdoc( - vendored_pandas_frame.DataFrame.__rfloordiv__ - ) - def mod(self, other: int | bigframes.series.Series | DataFrame, axis: str | int = "columns") -> DataFrame: # type: ignore return self._apply_binop(other, ops.mod_op, axis=axis) def __mod__(self, other): return self.mod(other) - __mod__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__mod__) - def rmod(self, other: int | bigframes.series.Series | DataFrame, axis: str | int = "columns") -> DataFrame: # type: ignore return self._apply_binop(other, ops.mod_op, axis=axis, reverse=True) def __rmod__(self, other): return self.rmod(other) - __rmod__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__rmod__) - def pow( self, other: int | bigframes.series.Series, axis: str | int = "columns" ) -> DataFrame: @@ -1179,8 +1091,6 @@ def pow( def __pow__(self, other): return self.pow(other) - __pow__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__pow__) - def rpow( self, other: int | bigframes.series.Series, axis: str | int = "columns" ) -> DataFrame: @@ -1189,27 +1099,19 @@ def rpow( def __rpow__(self, other): return self.rpow(other) - __rpow__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__rpow__) - def __and__(self, other: bool | int | bigframes.series.Series) -> DataFrame: return self._apply_binop(other, ops.and_op) - __and__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__and__) - __rand__ = __and__ def __or__(self, other: bool | int | bigframes.series.Series) -> DataFrame: return self._apply_binop(other, ops.or_op) - __or__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__or__) - __ror__ = __or__ def __xor__(self, other: bool | int | bigframes.series.Series) -> DataFrame: return self._apply_binop(other, ops.xor_op) - __xor__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__xor__) - __rxor__ = __xor__ def __pos__(self) -> DataFrame: @@ -1221,8 +1123,6 @@ def __neg__(self) -> DataFrame: def __abs__(self) -> DataFrame: return self._apply_unary_op(ops.abs_op) - __abs__.__doc__ = abs.__doc__ - def align( self, other: typing.Union[DataFrame, bigframes.series.Series], @@ -3186,7 +3086,6 @@ def prod( return bigframes.series.Series(block) product = prod - product.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.prod) def count(self, *, numeric_only: bool = False) -> bigframes.series.Series: if not numeric_only: @@ -3265,7 +3164,6 @@ def agg(self, func) -> DataFrame | bigframes.series.Series: ) aggregate = agg - aggregate.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.agg) @validations.requires_index @validations.requires_ordering() @@ -3338,7 +3236,6 @@ def kurt(self, *, numeric_only: bool = False): return bigframes.series.Series(result_block) kurtosis = kurt - kurtosis.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.kurt) def _pivot( self, @@ -3998,13 +3895,11 @@ def isna(self) -> DataFrame: return self._apply_unary_op(ops.isnull_op) isnull = isna - isnull.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.isna) def notna(self) -> DataFrame: return self._apply_unary_op(ops.notnull_op) notnull = notna - notnull.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.notna) @validations.requires_ordering() def cumsum(self): @@ -4407,8 +4302,6 @@ def __array__(self, dtype=None, copy: Optional[bool] = None) -> numpy.ndarray: raise ValueError("Cannot convert to array without copy.") return self.to_numpy(dtype=dtype) - __array__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__array__) - def to_parquet( self, path=None, @@ -4946,7 +4839,6 @@ def first_valid_index(self): return applymap = map - applymap.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.map) def _slice( self, @@ -5148,8 +5040,6 @@ def scatter( def __matmul__(self, other) -> DataFrame: return self.dot(other) - __matmul__.__doc__ = inspect.getdoc(vendored_pandas_frame.DataFrame.__matmul__) - @property def struct(self): return bigframes.operations.structs.StructFrameAccessor(self) diff --git a/bigframes/geopandas/geoseries.py b/bigframes/geopandas/geoseries.py index 660f1939a94..deae960d753 100644 --- a/bigframes/geopandas/geoseries.py +++ b/bigframes/geopandas/geoseries.py @@ -19,14 +19,14 @@ import bigframes_vendored.geopandas.geoseries as vendored_geoseries import geopandas.array # type: ignore +from bigframes._tools import docs import bigframes.operations as ops import bigframes.series import bigframes.session -class GeoSeries(vendored_geoseries.GeoSeries, bigframes.series.Series): - __doc__ = vendored_geoseries.GeoSeries.__doc__ - +@docs.inherit_docs(vendored_geoseries.GeoSeries) +class GeoSeries(bigframes.series.Series): def __init__(self, data=None, index=None, **kwargs): super().__init__( data=data, index=index, dtype=geopandas.array.GeometryDtype(), **kwargs diff --git a/bigframes/ml/base.py b/bigframes/ml/base.py index 3f6ccecaa2b..76a99304738 100644 --- a/bigframes/ml/base.py +++ b/bigframes/ml/base.py @@ -30,13 +30,15 @@ import bigframes_vendored.sklearn.base +from bigframes._tools import docs import bigframes.exceptions as bfe from bigframes.ml import core import bigframes.ml.utils as utils import bigframes.pandas as bpd -class BaseEstimator(bigframes_vendored.sklearn.base.BaseEstimator, abc.ABC): +@docs.inherit_docs(bigframes_vendored.sklearn.base.BaseEstimator) +class BaseEstimator(abc.ABC): """ A BigQuery DataFrames machine learning component follows sklearn API design Ref: https://bit.ly/3NyhKjN diff --git a/bigframes/ml/model_selection.py b/bigframes/ml/model_selection.py index 3d23fbf5684..7dd13ec4d73 100644 --- a/bigframes/ml/model_selection.py +++ b/bigframes/ml/model_selection.py @@ -27,6 +27,7 @@ import bigframes_vendored.sklearn.model_selection._validation as vendored_model_selection_validation import pandas as pd +from bigframes._tools import docs from bigframes.core.logging import log_adapter from bigframes.ml import utils import bigframes.pandas as bpd @@ -133,9 +134,8 @@ def _stratify_split(df: bpd.DataFrame, stratify: bpd.Series) -> List[bpd.DataFra @log_adapter.class_logger -class KFold(vendored_model_selection_split.KFold): - __doc__ = inspect.getdoc(vendored_model_selection_split.KFold) - +@docs.inherit_docs(vendored_model_selection_split.KFold) +class KFold: def __init__(self, n_splits: int = 5, *, random_state: Union[int, None] = None): if n_splits < 2: raise ValueError(f"n_splits must be at least 2. Got {n_splits}") diff --git a/bigframes/operations/lists.py b/bigframes/operations/lists.py index 9974e686933..e9d560791da 100644 --- a/bigframes/operations/lists.py +++ b/bigframes/operations/lists.py @@ -19,6 +19,7 @@ import bigframes_vendored.pandas.core.arrays.arrow.accessors as vendoracessors +from bigframes._tools import docs from bigframes.core.logging import log_adapter import bigframes.operations as ops from bigframes.operations._op_converters import convert_index, convert_slice @@ -26,9 +27,8 @@ @log_adapter.class_logger -class ListAccessor(vendoracessors.ListAccessor): - __doc__ = vendoracessors.ListAccessor.__doc__ - +@docs.inherit_docs(vendoracessors.ListAccessor) +class ListAccessor: def __init__(self, data: series.Series): self._data = data diff --git a/bigframes/operations/plotting.py b/bigframes/operations/plotting.py index 21a23a9ab54..c459af9550b 100644 --- a/bigframes/operations/plotting.py +++ b/bigframes/operations/plotting.py @@ -17,14 +17,14 @@ import bigframes_vendored.constants as constants import bigframes_vendored.pandas.plotting._core as vendordt +from bigframes._tools import docs from bigframes.core.logging import log_adapter import bigframes.operations._matplotlib as bfplt @log_adapter.class_logger -class PlotAccessor(vendordt.PlotAccessor): - __doc__ = vendordt.PlotAccessor.__doc__ - +@docs.inherit_docs(vendordt.PlotAccessor) +class PlotAccessor: _common_kinds = ("line", "area", "hist", "bar", "barh", "pie") _dataframe_kinds = ("scatter", "hexbin,") _all_kinds = _common_kinds + _dataframe_kinds diff --git a/bigframes/operations/strings.py b/bigframes/operations/strings.py index 922d26a23c1..8b5b57b259e 100644 --- a/bigframes/operations/strings.py +++ b/bigframes/operations/strings.py @@ -20,6 +20,7 @@ import bigframes_vendored.constants as constants import bigframes_vendored.pandas.core.strings.accessor as vendorstr +from bigframes._tools import docs import bigframes.core.indexes.base as indices from bigframes.core.logging import log_adapter import bigframes.dataframe as df @@ -39,9 +40,8 @@ @log_adapter.class_logger -class StringMethods(vendorstr.StringMethods, Generic[T]): - __doc__ = vendorstr.StringMethods.__doc__ - +@docs.inherit_docs(vendorstr.StringMethods) +class StringMethods(Generic[T]): def __init__(self, data: T): self._data: T = data diff --git a/bigframes/operations/structs.py b/bigframes/operations/structs.py index ec0b5dae526..31aeb345728 100644 --- a/bigframes/operations/structs.py +++ b/bigframes/operations/structs.py @@ -17,6 +17,7 @@ import bigframes_vendored.pandas.core.arrays.arrow.accessors as vendoracessors import pandas as pd +from bigframes._tools import docs from bigframes.core import backports from bigframes.core.logging import log_adapter import bigframes.dataframe @@ -25,9 +26,8 @@ @log_adapter.class_logger -class StructAccessor(vendoracessors.StructAccessor): - __doc__ = vendoracessors.StructAccessor.__doc__ - +@docs.inherit_docs(vendoracessors.StructAccessor) +class StructAccessor: def __init__(self, data: bigframes.series.Series): self._data = data @@ -69,7 +69,8 @@ def dtypes(self) -> pd.Series: @log_adapter.class_logger -class StructFrameAccessor(vendoracessors.StructFrameAccessor): +@docs.inherit_docs(vendoracessors.StructFrameAccessor) +class StructFrameAccessor: __doc__ = vendoracessors.StructAccessor.__doc__ def __init__(self, data: bigframes.dataframe.DataFrame) -> None: diff --git a/bigframes/series.py b/bigframes/series.py index 0c74a0dd19c..aac3fb88e9f 100644 --- a/bigframes/series.py +++ b/bigframes/series.py @@ -18,7 +18,6 @@ import datetime import functools -import inspect import itertools import numbers import textwrap @@ -48,6 +47,7 @@ import pyarrow as pa import typing_extensions +from bigframes._tools import docs import bigframes.core from bigframes.core import agg_expressions, groupby import bigframes.core.block_transforms as block_ops @@ -97,7 +97,8 @@ @log_adapter.class_logger -class Series(vendored_pandas_series.Series): +@docs.inherit_docs(vendored_pandas_series.Series) +class Series: # Must be above 5000 for pandas to delegate to bigframes for binops __pandas_priority__ = 13000 @@ -358,8 +359,6 @@ def _set_internal_query_job(self, query_job: Optional[bigquery.QueryJob]): def __len__(self): return self.shape[0] - __len__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__len__) - def __iter__(self) -> typing.Iterator: return itertools.chain.from_iterable( map(lambda x: x.squeeze(axis=1), self._block.to_pandas_batches()) @@ -918,7 +917,6 @@ def ffill(self, *, limit: typing.Optional[int] = None) -> Series: return self._apply_window_op(agg_ops.LastNonNullOp(), window) pad = ffill - pad.__doc__ = inspect.getdoc(vendored_pandas_series.Series.ffill) @validations.requires_ordering() def bfill(self, *, limit: typing.Optional[int] = None) -> Series: @@ -1166,45 +1164,33 @@ def isna(self) -> "Series": return self._apply_unary_op(ops.isnull_op) isnull = isna - isnull.__doc__ = inspect.getdoc(vendored_pandas_series.Series.isna) def notna(self) -> "Series": return self._apply_unary_op(ops.notnull_op) notnull = notna - notnull.__doc__ = inspect.getdoc(vendored_pandas_series.Series.notna) def __and__(self, other: bool | int | Series) -> Series: return self._apply_binary_op(other, ops.and_op) - __and__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__and__) - __rand__ = __and__ def __or__(self, other: bool | int | Series) -> Series: return self._apply_binary_op(other, ops.or_op) - __or__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__or__) - __ror__ = __or__ def __xor__(self, other: bool | int | Series) -> Series: return self._apply_binary_op(other, ops.xor_op) - __or__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__xor__) - __rxor__ = __xor__ def __add__(self, other: float | int | pandas.Timedelta | Series) -> Series: return self.add(other) - __add__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__add__) - def __radd__(self, other: float | int | pandas.Timedelta | Series) -> Series: return self.radd(other) - __radd__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__radd__) - def add(self, other: float | int | pandas.Timedelta | Series) -> Series: return self._apply_binary_op(other, ops.add_op) @@ -1214,13 +1200,9 @@ def radd(self, other: float | int | pandas.Timedelta | Series) -> Series: def __sub__(self, other: float | int | Series) -> Series: return self.sub(other) - __sub__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__sub__) - def __rsub__(self, other: float | int | Series) -> Series: return self.rsub(other) - __rsub__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__rsub__) - def sub(self, other) -> Series: return self._apply_binary_op(other, ops.sub_op) @@ -1228,18 +1210,13 @@ def rsub(self, other) -> Series: return self._apply_binary_op(other, ops.sub_op, reverse=True) subtract = sub - subtract.__doc__ = inspect.getdoc(vendored_pandas_series.Series.sub) def __mul__(self, other: float | int | Series) -> Series: return self.mul(other) - __mul__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__mul__) - def __rmul__(self, other: float | int | Series) -> Series: return self.rmul(other) - __rmul__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__rmul__) - def mul(self, other: float | int | Series) -> Series: return self._apply_binary_op(other, ops.mul_op) @@ -1247,40 +1224,29 @@ def rmul(self, other: float | int | Series) -> Series: return self._apply_binary_op(other, ops.mul_op, reverse=True) multiply = mul - multiply.__doc__ = inspect.getdoc(vendored_pandas_series.Series.mul) def __truediv__(self, other: float | int | pandas.Timedelta | Series) -> Series: return self.truediv(other) - __truediv__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__truediv__) - def __rtruediv__(self, other: float | int | pandas.Timedelta | Series) -> Series: return self.rtruediv(other) - __rtruediv__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__rtruediv__) - def truediv(self, other: float | int | pandas.Timedelta | Series) -> Series: return self._apply_binary_op(other, ops.div_op) def rtruediv(self, other: float | int | pandas.Timedelta | Series) -> Series: return self._apply_binary_op(other, ops.div_op, reverse=True) - truediv.__doc__ = inspect.getdoc(vendored_pandas_series.Series.truediv) div = divide = truediv rdiv = rtruediv - rdiv.__doc__ = inspect.getdoc(vendored_pandas_series.Series.rtruediv) def __floordiv__(self, other: float | int | pandas.Timedelta | Series) -> Series: return self.floordiv(other) - __floordiv__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__floordiv__) - def __rfloordiv__(self, other: float | int | pandas.Timedelta | Series) -> Series: return self.rfloordiv(other) - __rfloordiv__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__rfloordiv__) - def floordiv(self, other: float | int | pandas.Timedelta | Series) -> Series: return self._apply_binary_op(other, ops.floordiv_op) @@ -1290,13 +1256,9 @@ def rfloordiv(self, other: float | int | pandas.Timedelta | Series) -> Series: def __pow__(self, other: float | int | Series) -> Series: return self.pow(other) - __pow__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__pow__) - def __rpow__(self, other: float | int | Series) -> Series: return self.rpow(other) - __rpow__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__rpow__) - def pow(self, other: float | int | Series) -> Series: return self._apply_binary_op(other, ops.pow_op) @@ -1330,13 +1292,9 @@ def ge(self, other) -> Series: def __mod__(self, other) -> Series: # type: ignore return self.mod(other) - __mod__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__mod__) - def __rmod__(self, other) -> Series: # type: ignore return self.rmod(other) - __rmod__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__rmod__) - def mod(self, other) -> Series: # type: ignore return self._apply_binary_op(other, ops.mod_op) @@ -1359,13 +1317,9 @@ def dot(self, other): def __matmul__(self, other): return self.dot(other) - __matmul__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__matmul__) - def __rmatmul__(self, other): return self.dot(other) - __rmatmul__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__rmatmul__) - def combine_first(self, other: Series) -> Series: result = self._apply_binary_op(other, ops.coalesce_op) result.name = self.name @@ -1380,8 +1334,6 @@ def update(self, other: Union[Series, Sequence, Mapping]) -> None: def __abs__(self) -> Series: return self.abs() - __abs__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.abs) - def abs(self) -> Series: return self._apply_unary_op(ops.abs_op) @@ -1456,7 +1408,6 @@ def agg(self, func: str | typing.Sequence[str]) -> scalars.Scalar | Series: return self._apply_aggregation(agg_ops.lookup_agg_func(func)[0]) aggregate = agg - aggregate.__doc__ = inspect.getdoc(vendored_pandas_series.Series.agg) def describe(self) -> Series: from bigframes.pandas.core.methods import describe @@ -1496,7 +1447,6 @@ def kurt(self): return (numerator / denominator) - adjustment kurtosis = kurt - kurtosis.__doc__ = inspect.getdoc(vendored_pandas_series.Series.kurt) def mode(self) -> Series: block = self._block @@ -1561,7 +1511,6 @@ def prod(self) -> float: return typing.cast(float, self._apply_aggregation(agg_ops.product_op)) product = prod - product.__doc__ = inspect.getdoc(vendored_pandas_series.Series.prod) def __eq__(self, other: object) -> Series: # type: ignore return self.eq(other) @@ -1572,8 +1521,6 @@ def __ne__(self, other: object) -> Series: # type: ignore def __invert__(self) -> Series: return self._apply_unary_op(ops.invert_op) - __invert__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__invert__) - def __pos__(self) -> Series: return self._apply_unary_op(ops.pos_op) @@ -1751,8 +1698,6 @@ def __getitem__(self, indexer): return Series(block) return self.loc[indexer] - __getitem__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__getitem__) - def __getattr__(self, key: str): # Protect against recursion errors with uninitialized Series objects. # We use "_block" attribute to check whether the instance is initialized. @@ -2374,7 +2319,6 @@ def tolist( return self.to_pandas(allow_large_results=allow_large_results).to_list() to_list = tolist - to_list.__doc__ = inspect.getdoc(vendored_pandas_series.Series.tolist) def to_markdown( self, @@ -2405,8 +2349,6 @@ def __array__(self, dtype=None, copy: Optional[bool] = None) -> numpy.ndarray: raise ValueError("Cannot convert to array without copy.") return self.to_numpy(dtype=dtype) - __array__.__doc__ = inspect.getdoc(vendored_pandas_series.Series.__array__) - def to_pickle(self, path, *, allow_large_results=None, **kwargs) -> None: return self.to_pandas(allow_large_results=allow_large_results).to_pickle( path, **kwargs @@ -2575,8 +2517,6 @@ def hist( ): return self.plot.hist(by=by, bins=bins, **kwargs) - hist.__doc__ = inspect.getdoc(plotting.PlotAccessor.hist) - def line( self, x: typing.Optional[typing.Hashable] = None, @@ -2585,8 +2525,6 @@ def line( ): return self.plot.line(x=x, y=y, **kwargs) - line.__doc__ = inspect.getdoc(plotting.PlotAccessor.line) - def area( self, x: typing.Optional[typing.Hashable] = None, @@ -2596,8 +2534,6 @@ def area( ): return self.plot.area(x=x, y=y, stacked=stacked, **kwargs) - area.__doc__ = inspect.getdoc(plotting.PlotAccessor.area) - def bar( self, x: typing.Optional[typing.Hashable] = None, @@ -2606,8 +2542,6 @@ def bar( ): return self.plot.bar(x=x, y=y, **kwargs) - bar.__doc__ = inspect.getdoc(plotting.PlotAccessor.bar) - def _slice( self, start: typing.Optional[int] = None, From 069abf4f5be98e5f0fdd7b4c81ae2e48ae37dee3 Mon Sep 17 00:00:00 2001 From: Trevor Bergeron Date: Wed, 18 Feb 2026 23:34:03 +0000 Subject: [PATCH 2/6] add series docstring --- third_party/bigframes_vendored/pandas/core/series.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/third_party/bigframes_vendored/pandas/core/series.py b/third_party/bigframes_vendored/pandas/core/series.py index 2c0f493d81e..775971ab358 100644 --- a/third_party/bigframes_vendored/pandas/core/series.py +++ b/third_party/bigframes_vendored/pandas/core/series.py @@ -32,6 +32,10 @@ class Series(NDFrame): # type: ignore[misc] + """ + One-dimensional ndarray with axis labels (including time series). + """ + @property def dt(self): """ From d2165d439c5cd4267bfe09b24909b97a2ab0e84c Mon Sep 17 00:00:00 2001 From: Trevor Bergeron Date: Thu, 19 Feb 2026 00:15:48 +0000 Subject: [PATCH 3/6] fix errors --- bigframes/core/indexes/base.py | 5 ++++ bigframes/dataframe.py | 5 ++++ bigframes/series.py | 5 ++++ .../pandas/core/computation/align.py | 29 +++++++++---------- .../pandas/core/computation/eval.py | 7 +++-- .../bigframes_vendored/pandas/core/generic.py | 9 ------ 6 files changed, 33 insertions(+), 27 deletions(-) diff --git a/bigframes/core/indexes/base.py b/bigframes/core/indexes/base.py index 9e72cd533c1..011639ed9ef 100644 --- a/bigframes/core/indexes/base.py +++ b/bigframes/core/indexes/base.py @@ -778,6 +778,11 @@ def to_list(self, *, allow_large_results: Optional[bool] = None) -> list: def __len__(self): return self.shape[0] + def __bool__(self): + raise ValueError( + "Cannot convert Index into bool. Consider using .empty(), .item(), .any(), or .all() methods." + ) + def item(self): # Docstring is in third_party/bigframes_vendored/pandas/core/indexes/base.py return self.to_series().peek(2).item() diff --git a/bigframes/dataframe.py b/bigframes/dataframe.py index b4777c6745c..7a0a8085943 100644 --- a/bigframes/dataframe.py +++ b/bigframes/dataframe.py @@ -388,6 +388,11 @@ def __len__(self): rows, _ = self.shape return rows + def __bool__(self): + raise ValueError( + "Cannot convert dataframe into bool. Consider using .empty(), .any(), or .all() methods." + ) + def __iter__(self): return iter(self.columns) diff --git a/bigframes/series.py b/bigframes/series.py index aac3fb88e9f..053ea8053b8 100644 --- a/bigframes/series.py +++ b/bigframes/series.py @@ -359,6 +359,11 @@ def _set_internal_query_job(self, query_job: Optional[bigquery.QueryJob]): def __len__(self): return self.shape[0] + def __bool__(self): + raise ValueError( + "Cannot convert Series into bool. Consider using .empty(), .item(), .any(), or .all() methods." + ) + def __iter__(self) -> typing.Iterator: return itertools.chain.from_iterable( map(lambda x: x.squeeze(axis=1), self._block.to_pandas_batches()) diff --git a/third_party/bigframes_vendored/pandas/core/computation/align.py b/third_party/bigframes_vendored/pandas/core/computation/align.py index 2608dabe7ac..4280c32f9b6 100644 --- a/third_party/bigframes_vendored/pandas/core/computation/align.py +++ b/third_party/bigframes_vendored/pandas/core/computation/align.py @@ -5,7 +5,7 @@ from __future__ import annotations from functools import partial, wraps -from typing import Callable, TYPE_CHECKING +from typing import Callable, TYPE_CHECKING, Union import warnings import bigframes_vendored.pandas.core.common as com @@ -17,15 +17,18 @@ if TYPE_CHECKING: from collections.abc import Sequence - from bigframes_vendored.pandas.core.generic import NDFrame from bigframes_vendored.pandas.core.indexes.base import Index from pandas._typing import F + from bigframes.pandas import DataFrame, Series + + FrameT = Union[Series, DataFrame] + def _align_core_single_unary_op( term, -) -> tuple[partial | type[NDFrame], dict[str, Index] | None]: - typ: partial | type[NDFrame] +) -> tuple[partial | FrameT, dict[str, Index] | None]: + typ: partial | FrameT axes: dict[str, Index] | None = None if isinstance(term.value, np.ndarray): @@ -38,9 +41,7 @@ def _align_core_single_unary_op( return typ, axes -def _zip_axes_from_type( - typ: type[NDFrame], new_axes: Sequence[Index] -) -> dict[str, Index]: +def _zip_axes_from_type(typ: FrameT, new_axes: Sequence[Index]) -> dict[str, Index]: return {name: new_axes[i] for i, name in enumerate(typ._AXIS_ORDERS)} @@ -207,20 +208,18 @@ def is_series(obj) -> bool: def is_series_or_dataframe(obj) -> bool: - from bigframes_vendored.pandas.core.frame import NDFrame + from bigframes.pandas import DataFrame, Series - return isinstance(obj, NDFrame) + return isinstance(obj, Series | DataFrame) def is_pandas_object(obj) -> bool: - from bigframes_vendored.pandas.core.frame import NDFrame - from bigframes_vendored.pandas.core.indexes.base import Index + from bigframes.pandas import DataFrame, Index, Series - return isinstance(obj, NDFrame) or isinstance(obj, Index) + return isinstance(obj, Series | DataFrame | Index) def is_pandas_type(type) -> bool: - from bigframes_vendored.pandas.core.frame import NDFrame - from bigframes_vendored.pandas.core.indexes.base import Index + from bigframes.pandas import DataFrame, Index, Series - return issubclass(type, NDFrame) or issubclass(type, Index) + return issubclass(type, Series | DataFrame | Index) diff --git a/third_party/bigframes_vendored/pandas/core/computation/eval.py b/third_party/bigframes_vendored/pandas/core/computation/eval.py index a1809f6cb3a..b16c62d9552 100644 --- a/third_party/bigframes_vendored/pandas/core/computation/eval.py +++ b/third_party/bigframes_vendored/pandas/core/computation/eval.py @@ -12,7 +12,6 @@ from bigframes_vendored.pandas.core.computation.expr import Expr, PARSERS from bigframes_vendored.pandas.core.computation.parsing import tokenize_string from bigframes_vendored.pandas.core.computation.scope import ensure_scope -from bigframes_vendored.pandas.core.generic import NDFrame from bigframes_vendored.pandas.util._validators import validate_bool_kwarg from pandas.io.formats.printing import pprint_thing @@ -317,6 +316,8 @@ def eval( # assign if needed assigner = parsed_expr.assigner + from bigframes.pandas import DataFrame, Series + if env.target is not None and assigner is not None: target_modified = True @@ -324,7 +325,7 @@ def eval( if not inplace and first_expr: try: target = env.target - if isinstance(target, NDFrame): + if isinstance(target, Series | DataFrame): target = target.copy() except AttributeError as err: raise ValueError("Cannot return a copy of the target") from err @@ -338,7 +339,7 @@ def eval( try: with warnings.catch_warnings(record=True): # TODO: Filter the warnings we actually care about here. - if inplace and isinstance(target, NDFrame): + if inplace and isinstance(target, Series | DataFrame): target.loc[:, assigner] = ret else: target[ # pyright: ignore[reportGeneralTypeIssues] diff --git a/third_party/bigframes_vendored/pandas/core/generic.py b/third_party/bigframes_vendored/pandas/core/generic.py index 63b9f8199b6..17ec864a6c3 100644 --- a/third_party/bigframes_vendored/pandas/core/generic.py +++ b/third_party/bigframes_vendored/pandas/core/generic.py @@ -1229,15 +1229,6 @@ def pipe( """ return common.pipe(self, func, *args, **kwargs) - def __nonzero__(self): - """Returns the truth value of the object.""" - raise ValueError( - f"The truth value of a {type(self).__name__} is ambiguous. " - "Use a.empty, a.bool(), a.item(), a.any() or a.all()." - ) - - __bool__ = __nonzero__ - def __getattr__(self, name: str): """ After regular attribute access, try looking up the name From 12e8cd7707fa63bf313f233f61f0d5ee4496a3b4 Mon Sep 17 00:00:00 2001 From: Trevor Bergeron Date: Thu, 19 Feb 2026 00:28:06 +0000 Subject: [PATCH 4/6] fill previously inherited members --- bigframes/dataframe.py | 18 ++++++++++++++++++ bigframes/series.py | 18 ++++++++++++++++++ .../bigframes_vendored/pandas/core/generic.py | 8 ++------ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/bigframes/dataframe.py b/bigframes/dataframe.py index 7a0a8085943..4cdf3e5c6c4 100644 --- a/bigframes/dataframe.py +++ b/bigframes/dataframe.py @@ -37,6 +37,7 @@ overload, Sequence, Tuple, + TypeVar, Union, ) import warnings @@ -108,6 +109,7 @@ "DataFrame", Sequence[int | float | str | pandas.Timedelta | Callable] ] +U = TypeVar("U") LevelType = typing.Hashable LevelsType = typing.Union[LevelType, typing.Sequence[LevelType]] @@ -3760,6 +3762,22 @@ def expanding(self, min_periods: int = 1) -> bigframes.core.window.Window: self._block, window, self._block.value_columns ) + def pipe( + self, + func: Union[Callable[..., U], tuple[Callable[..., U], str]], + *args, + **kwargs, + ) -> U: + import bigframes_vendored.pandas.core.common as common + + return common.pipe(self, func, *args, **kwargs) + + def get(self, key, default=None): + try: + return self[key] + except (KeyError, ValueError, IndexError): + return default + def groupby( self, by: typing.Union[ diff --git a/bigframes/series.py b/bigframes/series.py index 053ea8053b8..299c39637d0 100644 --- a/bigframes/series.py +++ b/bigframes/series.py @@ -34,6 +34,7 @@ overload, Sequence, Tuple, + TypeVar, Union, ) import warnings @@ -84,6 +85,7 @@ import bigframes.operations.strings as strings +U = TypeVar("U") LevelType = typing.Union[str, int] LevelsType = typing.Union[LevelType, typing.Sequence[LevelType]] @@ -1886,6 +1888,22 @@ def expanding(self, min_periods: int = 1) -> bigframes.core.window.Window: self._block, window_spec, self._block.value_columns, is_series=True ) + def pipe( + self, + func: Union[Callable[..., U], tuple[Callable[..., U], str]], + *args, + **kwargs, + ) -> U: + import bigframes_vendored.pandas.core.common as common + + return common.pipe(self, func, *args, **kwargs) + + def get(self, key, default=None): + try: + return self[key] + except (KeyError, ValueError, IndexError): + return default + def groupby( self, by: typing.Union[ diff --git a/third_party/bigframes_vendored/pandas/core/generic.py b/third_party/bigframes_vendored/pandas/core/generic.py index 17ec864a6c3..417ccd2d0e5 100644 --- a/third_party/bigframes_vendored/pandas/core/generic.py +++ b/third_party/bigframes_vendored/pandas/core/generic.py @@ -5,7 +5,6 @@ import bigframes_vendored.constants as constants from bigframes_vendored.pandas.core import indexing -import bigframes_vendored.pandas.core.common as common if TYPE_CHECKING: from bigframes_vendored.pandas.pandas._typing import T @@ -395,10 +394,7 @@ def get(self, key, default=None): Any: same type as items contained in object """ - try: - return self[key] - except (KeyError, ValueError, IndexError): - return default + raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE) def add_prefix(self, prefix: str, axis: int | str | None = None): """Prefix labels with string `prefix`. @@ -1227,7 +1223,7 @@ def pipe( bigframes.pandas.DataFrame or bigframes.pandas.Series: Object of same type as caller """ - return common.pipe(self, func, *args, **kwargs) + raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE) def __getattr__(self, name: str): """ From ab230ebd57bda4975f53dd8dfa4c6a9d84cc7868 Mon Sep 17 00:00:00 2001 From: Trevor Bergeron Date: Thu, 19 Feb 2026 18:06:59 +0000 Subject: [PATCH 5/6] fix axes method --- bigframes/dataframe.py | 4 ++++ third_party/bigframes_vendored/pandas/core/frame.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bigframes/dataframe.py b/bigframes/dataframe.py index 4cdf3e5c6c4..2a22fc4487d 100644 --- a/bigframes/dataframe.py +++ b/bigframes/dataframe.py @@ -346,6 +346,10 @@ def columns(self, labels: pandas.Index): def shape(self) -> Tuple[int, int]: return self._block.shape + @property + def axes(self) -> list: + return [self.index, self.columns] + @property def size(self) -> int: rows, cols = self.shape diff --git a/third_party/bigframes_vendored/pandas/core/frame.py b/third_party/bigframes_vendored/pandas/core/frame.py index dc1bcca213e..f04d9989dd4 100644 --- a/third_party/bigframes_vendored/pandas/core/frame.py +++ b/third_party/bigframes_vendored/pandas/core/frame.py @@ -67,7 +67,7 @@ def axes(self) -> list: >>> df.axes[1:] [Index(['col1', 'col2'], dtype='object')] """ - return [self.index, self.columns] + raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE) @property def values(self) -> np.ndarray: From d02a72f31d759661f8994ff06986c4db1f4a3d08 Mon Sep 17 00:00:00 2001 From: Trevor Bergeron Date: Thu, 19 Feb 2026 18:11:40 +0000 Subject: [PATCH 6/6] remove unneeded comments --- bigframes/_tools/docs.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/bigframes/_tools/docs.py b/bigframes/_tools/docs.py index 22b137f8f35..1b4b329a945 100644 --- a/bigframes/_tools/docs.py +++ b/bigframes/_tools/docs.py @@ -20,27 +20,18 @@ def inherit_docs(source_class): """ def decorator(target_class): - # 1. Steal the main class docstring if the target doesn't have one if not target_class.__doc__ and source_class.__doc__: target_class.__doc__ = source_class.__doc__ - # 2. Iterate over all attributes in the source class for name, source_item in vars(source_class).items(): - # Check if the target class has the same attribute if name in vars(target_class): target_item = getattr(target_class, name) - # Only copy if the target doesn't have a docstring - # and the source does if hasattr(target_item, "__doc__") and not target_item.__doc__: if hasattr(source_item, "__doc__") and source_item.__doc__: try: - # Use functools.update_wrapper or manual assignment - # for methods, properties, and static methods target_item.__doc__ = source_item.__doc__ except AttributeError: - # Read-only attributes or certain built-ins - # might skip docstring assignment pass return target_class