From 9c9a053ac53b0fcdb4c346464044c29f03979d6f Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 7 Nov 2020 15:51:33 -0800 Subject: [PATCH 1/8] TYP: @final for some NDFrame methods --- pandas/core/generic.py | 31 ++++++++++++++++++++++++++----- pandas/core/indexes/datetimes.py | 2 +- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 36ce2c4776bd0..80f01ca414baa 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -39,6 +39,7 @@ CompressionOptions, FilePathOrBuffer, FrameOrSeries, + FrameOrSeriesUnion, IndexKeyFunc, IndexLabel, JSONSerializable, @@ -49,6 +50,7 @@ TimedeltaConvertibleTypes, TimestampConvertibleTypes, ValueKeyFunc, + final, ) from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv @@ -9344,6 +9346,7 @@ def shift( result = self.set_axis(new_ax, axis) return result.__finalize__(self, method="shift") + @final def slice_shift(self: FrameOrSeries, periods: int = 1, axis=0) -> FrameOrSeries: """ Equivalent to `shift` without copying data. @@ -9392,6 +9395,7 @@ def slice_shift(self: FrameOrSeries, periods: int = 1, axis=0) -> FrameOrSeries: return new_obj.__finalize__(self, method="slice_shift") + @final def tshift( self: FrameOrSeries, periods: int = 1, freq=None, axis: Axis = 0 ) -> FrameOrSeries: @@ -9598,6 +9602,7 @@ def truncate( return result + @final def tz_convert( self: FrameOrSeries, tz, axis=0, level=None, copy: bool_t = True ) -> FrameOrSeries: @@ -9655,6 +9660,7 @@ def _tz_convert(ax, tz): result = result.set_axis(ax, axis=axis, inplace=False) return result.__finalize__(self, method="tz_convert") + @final def tz_localize( self: FrameOrSeries, tz, @@ -9828,6 +9834,7 @@ def _tz_localize(ax, tz, ambiguous, nonexistent): # ---------------------------------------------------------------------- # Numeric Methods + @final def abs(self: FrameOrSeries) -> FrameOrSeries: """ Return a Series/DataFrame with absolute numeric value of each element. @@ -9903,7 +9910,7 @@ def describe( include=None, exclude=None, datetime_is_numeric=False, - ) -> FrameOrSeries: + ) -> FrameOrSeriesUnion: """ Generate descriptive statistics. @@ -10170,7 +10177,7 @@ def describe( formatted_percentiles = format_percentiles(percentiles) - def describe_numeric_1d(series): + def describe_numeric_1d(series) -> "Series": stat_index = ( ["count", "mean", "std", "min"] + formatted_percentiles + ["max"] ) @@ -10181,7 +10188,7 @@ def describe_numeric_1d(series): ) return pd.Series(d, index=stat_index, name=series.name) - def describe_categorical_1d(data): + def describe_categorical_1d(data) -> "Series": names = ["count", "unique"] objcounts = data.value_counts() count_unique = len(objcounts[objcounts != 0]) @@ -10230,7 +10237,7 @@ def describe_categorical_1d(data): return pd.Series(result, index=names, name=data.name, dtype=dtype) - def describe_timestamp_1d(data): + def describe_timestamp_1d(data) -> "Series": # GH-30164 stat_index = ["count", "mean", "min"] + formatted_percentiles + ["max"] d = ( @@ -10240,7 +10247,7 @@ def describe_timestamp_1d(data): ) return pd.Series(d, index=stat_index, name=data.name) - def describe_1d(data): + def describe_1d(data) -> "Series": if is_bool_dtype(data.dtype): return describe_categorical_1d(data) elif is_numeric_dtype(data): @@ -10283,6 +10290,7 @@ def describe_1d(data): d.columns = data.columns.copy() return d + @final def pct_change( self: FrameOrSeries, periods=1, @@ -10421,6 +10429,7 @@ def pct_change( rs = rs.reindex_like(data) return rs + @final def _agg_by_level(self, name, axis=0, level=0, skipna=True, **kwargs): if axis is None: raise ValueError("Must specify 'axis' when aggregating by level.") @@ -10432,6 +10441,7 @@ def _agg_by_level(self, name, axis=0, level=0, skipna=True, **kwargs): applyf = lambda x: method(x, axis=axis, skipna=skipna, **kwargs) return grouped.aggregate(applyf) + @final def _logical_func( self, name: str, func, axis=0, bool_only=None, skipna=True, level=None, **kwargs ): @@ -10469,6 +10479,7 @@ def all(self, axis=0, bool_only=None, skipna=True, level=None, **kwargs): "all", nanops.nanall, axis, bool_only, skipna, level, **kwargs ) + @final def _accum_func(self, name: str, func, axis=None, skipna=True, *args, **kwargs): skipna = nv.validate_cum_func_with_skipna(skipna, args, kwargs, name) if axis is None: @@ -10509,6 +10520,7 @@ def cumsum(self, axis=None, skipna=True, *args, **kwargs): def cumprod(self, axis=None, skipna=True, *args, **kwargs): return self._accum_func("cumprod", np.cumprod, axis, skipna, *args, **kwargs) + @final def _stat_function_ddof( self, name: str, @@ -10554,6 +10566,7 @@ def std( "std", nanops.nanstd, axis, skipna, level, ddof, numeric_only, **kwargs ) + @final def _stat_function( self, name: str, @@ -10610,6 +10623,7 @@ def kurt(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs): kurtosis = kurt + @final def _min_count_stat_function( self, name: str, @@ -11038,6 +11052,7 @@ def min(self, axis=None, skipna=None, level=None, numeric_only=None, **kwargs): cls.min = min + @final @doc(Rolling) def rolling( self, @@ -11074,6 +11089,7 @@ def rolling( closed=closed, ) + @final @doc(Expanding) def expanding( self, min_periods: int = 1, center: Optional[bool_t] = None, axis: Axis = 0 @@ -11090,6 +11106,7 @@ def expanding( return Expanding(self, min_periods=min_periods, center=center, axis=axis) + @final @doc(ExponentialMovingWindow) def ewm( self, @@ -11120,6 +11137,7 @@ def ewm( # ---------------------------------------------------------------------- # Arithmetic Methods + @final def _inplace_method(self, other, op): """ Wrap arithmetic method to operate inplace. @@ -11174,6 +11192,7 @@ def __ixor__(self, other): # ---------------------------------------------------------------------- # Misc methods + @final def _find_valid_index(self, how: str): """ Retrieves the index of the first valid value. @@ -11192,6 +11211,7 @@ def _find_valid_index(self, how: str): return None return self.index[idxpos] + @final @doc(position="first", klass=_shared_doc_kwargs["klass"]) def first_valid_index(self): """ @@ -11208,6 +11228,7 @@ def first_valid_index(self): """ return self._find_valid_index("first") + @final @doc(first_valid_index, position="last", klass=_shared_doc_kwargs["klass"]) def last_valid_index(self): return self._find_valid_index("last") diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index aa16dc9752565..9744eb0ecbb88 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -802,7 +802,7 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): if (start is None or isinstance(start, str)) and ( end is None or isinstance(end, str) ): - mask = True + mask = np.array(True) if start is not None: start_casted = self._maybe_cast_slice_bound(start, "left", kind) mask = start_casted <= self From 2103ef0ff68148ecb6a868ad90c7c3ec4a23a7d5 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 7 Nov 2020 15:57:46 -0800 Subject: [PATCH 2/8] final more methods --- pandas/core/generic.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 80f01ca414baa..137a2f521f3aa 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7436,6 +7436,7 @@ def notna(self: FrameOrSeries) -> FrameOrSeries: def notnull(self: FrameOrSeries) -> FrameOrSeries: return notna(self).__finalize__(self, method="notnull") + @final def _clip_with_scalar(self, lower, upper, inplace: bool_t = False): if (lower is not None and np.any(isna(lower))) or ( upper is not None and np.any(isna(upper)) @@ -7461,6 +7462,7 @@ def _clip_with_scalar(self, lower, upper, inplace: bool_t = False): else: return result + @final def _clip_with_one_bound(self, threshold, method, axis, inplace): if axis is not None: @@ -7484,6 +7486,7 @@ def _clip_with_one_bound(self, threshold, method, axis, inplace): threshold = align_method_FRAME(self, threshold, axis, flex=None)[1] return self.where(subset, threshold, axis=axis, inplace=inplace) + @final def clip( self: FrameOrSeries, lower=None, @@ -7610,6 +7613,7 @@ def clip( return result + @final def asfreq( self: FrameOrSeries, freq, @@ -7721,6 +7725,7 @@ def asfreq( fill_value=fill_value, ) + @final def at_time( self: FrameOrSeries, time, asof: bool_t = False, axis=None ) -> FrameOrSeries: @@ -7779,6 +7784,7 @@ def at_time( indexer = index.indexer_at_time(time, asof=asof) return self._take_with_is_copy(indexer, axis=axis) + @final def between_time( self: FrameOrSeries, start_time, @@ -8264,6 +8270,7 @@ def resample( offset=offset, ) + @final def first(self: FrameOrSeries, offset) -> FrameOrSeries: """ Select initial periods of time series data based on a date offset. @@ -8332,6 +8339,7 @@ def first(self: FrameOrSeries, offset) -> FrameOrSeries: return self.loc[:end] + @final def last(self: FrameOrSeries, offset) -> FrameOrSeries: """ Select final periods of time series data based on a date offset. @@ -8395,6 +8403,7 @@ def last(self: FrameOrSeries, offset) -> FrameOrSeries: start = self.index.searchsorted(start_date, side="right") return self.iloc[start:] + @final def rank( self: FrameOrSeries, axis=0, @@ -8715,6 +8724,7 @@ def align( else: # pragma: no cover raise TypeError(f"unsupported type: {type(other)}") + @final def _align_frame( self, other, @@ -8784,6 +8794,7 @@ def _align_frame( right.__finalize__(other), ) + @final def _align_series( self, other, @@ -8875,6 +8886,7 @@ def _align_series( right.__finalize__(other), ) + @final def _where( self, cond, @@ -9022,6 +9034,7 @@ def _where( result = self._constructor(new_data) return result.__finalize__(self) + @final @doc( klass=_shared_doc_kwargs["klass"], cond="True", @@ -9164,6 +9177,7 @@ def where( cond, other, inplace, axis, level, errors=errors, try_cast=try_cast ) + @final @doc( where, klass=_shared_doc_kwargs["klass"], @@ -9904,6 +9918,7 @@ def abs(self: FrameOrSeries) -> FrameOrSeries: """ return np.abs(self) + @final def describe( self: FrameOrSeries, percentiles=None, From efff55969fe9f9664e174b0c50579c8c2198cde8 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 7 Nov 2020 16:14:02 -0800 Subject: [PATCH 3/8] TYP: @final more --- pandas/core/generic.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 137a2f521f3aa..f29599c6fe637 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -4929,6 +4929,7 @@ def f(x) -> bool: else: raise TypeError("Must pass either `items`, `like`, or `regex`") + @final def head(self: FrameOrSeries, n: int = 5) -> FrameOrSeries: """ Return the first `n` rows. @@ -5001,6 +5002,7 @@ def head(self: FrameOrSeries, n: int = 5) -> FrameOrSeries: """ return self.iloc[:n] + @final def tail(self: FrameOrSeries, n: int = 5) -> FrameOrSeries: """ Return the last `n` rows. @@ -5075,6 +5077,7 @@ def tail(self: FrameOrSeries, n: int = 5) -> FrameOrSeries: return self.iloc[0:0] return self.iloc[-n:] + @final def sample( self: FrameOrSeries, n=None, @@ -5283,6 +5286,7 @@ def sample( locs = rs.choice(axis_length, size=n, replace=replace, p=weights) return self.take(locs, axis=axis) + @final @doc(klass=_shared_doc_kwargs["klass"]) def pipe(self, func, *args, **kwargs): r""" @@ -5340,6 +5344,7 @@ def pipe(self, func, *args, **kwargs): # ---------------------------------------------------------------------- # Attribute access + @final def __finalize__( self: FrameOrSeries, other, method: Optional[str] = None, **kwargs ) -> FrameOrSeries: @@ -5436,6 +5441,7 @@ def __setattr__(self, name: str, value) -> None: ) object.__setattr__(self, name, value) + @final def _dir_additions(self) -> Set[str]: """ add the string-like attributes from the info_axis. @@ -5449,6 +5455,7 @@ def _dir_additions(self) -> Set[str]: # ---------------------------------------------------------------------- # Consolidation of internals + @final def _protect_consolidate(self, f): """ Consolidate _mgr -- if the blocks have changed, then clear the @@ -5460,6 +5467,7 @@ def _protect_consolidate(self, f): self._clear_item_cache() return result + @final def _consolidate_inplace(self) -> None: """Consolidate data in place and return None""" @@ -5468,6 +5476,7 @@ def f(): self._protect_consolidate(f) + @final def _consolidate(self): """ Compute NDFrame with "consolidated" internals (data of each dtype @@ -5481,6 +5490,7 @@ def _consolidate(self): cons_data = self._protect_consolidate(f) return self._constructor(cons_data).__finalize__(self) + @final @property def _is_mixed_type(self) -> bool_t: if self._mgr.is_single_block: @@ -5493,6 +5503,7 @@ def _is_mixed_type(self) -> bool_t: return self.dtypes.nunique() > 1 + @final def _check_inplace_setting(self, value) -> bool_t: """ check whether we allow in-place setting with this type of value """ if self._is_mixed_type: @@ -5509,9 +5520,11 @@ def _check_inplace_setting(self, value) -> bool_t: return True + @final def _get_numeric_data(self): return self._constructor(self._mgr.get_numeric_data()).__finalize__(self) + @final def _get_bool_data(self): return self._constructor(self._mgr.get_bool_data()).__finalize__(self) @@ -5631,6 +5644,7 @@ def dtypes(self): data = self._mgr.get_dtypes() return self._constructor_sliced(data, index=self._info_axis, dtype=np.object_) + @final def _to_dict_of_blocks(self, copy: bool_t = True): """ Return a dict of dtype -> Constructor Types that @@ -5808,6 +5822,7 @@ def astype( result.columns = self.columns return result + @final def copy(self: FrameOrSeries, deep: bool_t = True) -> FrameOrSeries: """ Make a copy of this object's indices and data. @@ -5917,9 +5932,11 @@ def copy(self: FrameOrSeries, deep: bool_t = True) -> FrameOrSeries: self._clear_item_cache() return self._constructor(data).__finalize__(self, method="copy") + @final def __copy__(self: FrameOrSeries, deep: bool_t = True) -> FrameOrSeries: return self.copy(deep=deep) + @final def __deepcopy__(self: FrameOrSeries, memo=None) -> FrameOrSeries: """ Parameters @@ -5929,6 +5946,7 @@ def __deepcopy__(self: FrameOrSeries, memo=None) -> FrameOrSeries: """ return self.copy(deep=True) + @final def _convert( self: FrameOrSeries, datetime: bool_t = False, @@ -5970,6 +5988,7 @@ def _convert( ) ).__finalize__(self) + @final def infer_objects(self: FrameOrSeries) -> FrameOrSeries: """ Attempt to infer better dtypes for object columns. @@ -6017,6 +6036,7 @@ def infer_objects(self: FrameOrSeries) -> FrameOrSeries: ) ).__finalize__(self, method="infer_objects") + @final def convert_dtypes( self: FrameOrSeries, infer_objects: bool_t = True, @@ -6341,6 +6361,7 @@ def fillna( else: return result.__finalize__(self, method="fillna") + @final def ffill( self: FrameOrSeries, axis=None, @@ -6362,6 +6383,7 @@ def ffill( pad = ffill + @final def bfill( self: FrameOrSeries, axis=None, @@ -6834,6 +6856,7 @@ def replace( else: return result.__finalize__(self, method="replace") + @final def interpolate( self: FrameOrSeries, method: str = "linear", @@ -7132,6 +7155,7 @@ def interpolate( # ---------------------------------------------------------------------- # Timeseries methods Methods + @final def asof(self, where, subset=None): """ Return the last row(s) without any NaNs before `where`. From 24968fe134259a5e1cd883647dac3fc171acb217 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 7 Nov 2020 17:21:43 -0800 Subject: [PATCH 4/8] final more --- pandas/core/generic.py | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index f29599c6fe637..ce15bca1e8325 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -272,6 +272,7 @@ def attrs(self) -> Dict[Optional[Hashable], Any]: def attrs(self, value: Mapping[Optional[Hashable], Any]) -> None: self._attrs = dict(value) + @final @property def flags(self) -> Flags: """ @@ -312,6 +313,7 @@ def flags(self) -> Flags: """ return self._flags + @final def set_flags( self: FrameOrSeries, *, @@ -362,6 +364,7 @@ def set_flags( df.flags["allows_duplicate_labels"] = allows_duplicate_labels return df + @final @classmethod def _validate_dtype(cls, dtype): """ validate the passed dtype """ @@ -407,6 +410,7 @@ def _constructor_expanddim(self): # ---------------------------------------------------------------------- # Internals + @final @property def _data(self): # GH#33054 retained because some downstream packages uses this, @@ -437,12 +441,14 @@ def _AXIS_NAMES(self) -> Dict[int, str]: warnings.warn("_AXIS_NAMES has been deprecated.", FutureWarning, stacklevel=3) return {0: "index"} + @final def _construct_axes_dict(self, axes=None, **kwargs): """Return an axes dictionary for myself.""" d = {a: self._get_axis(a) for a in (axes or self._AXIS_ORDERS)} d.update(kwargs) return d + @final @classmethod def _construct_axes_from_arguments( cls, args, kwargs, require_all: bool = False, sentinel=None @@ -474,6 +480,7 @@ def _construct_axes_from_arguments( axes = {a: kwargs.pop(a, sentinel) for a in cls._AXIS_ORDERS} return axes, kwargs + @final @classmethod def _get_axis_number(cls, axis: Axis) -> int: try: @@ -481,16 +488,19 @@ def _get_axis_number(cls, axis: Axis) -> int: except KeyError: raise ValueError(f"No axis named {axis} for object type {cls.__name__}") + @final @classmethod def _get_axis_name(cls, axis: Axis) -> str: axis_number = cls._get_axis_number(axis) return cls._AXIS_ORDERS[axis_number] + @final def _get_axis(self, axis: Axis) -> Index: axis_number = self._get_axis_number(axis) assert axis_number in {0, 1} return self.index if axis_number == 0 else self.columns + @final @classmethod def _get_block_manager_axis(cls, axis: Axis) -> int: """Map the axis to the block_manager axis.""" @@ -500,6 +510,7 @@ def _get_block_manager_axis(cls, axis: Axis) -> int: return m - axis return axis + @final def _get_axis_resolvers(self, axis: str) -> Dict[str, Union[Series, MultiIndex]]: # index or columns axis_index = getattr(self, axis) @@ -530,6 +541,7 @@ def _get_axis_resolvers(self, axis: str) -> Dict[str, Union[Series, MultiIndex]] d[axis] = dindex return d + @final def _get_index_resolvers(self) -> Dict[str, Union[Series, MultiIndex]]: from pandas.core.computation.parsing import clean_column_name @@ -539,6 +551,7 @@ def _get_index_resolvers(self) -> Dict[str, Union[Series, MultiIndex]]: return {clean_column_name(k): v for k, v in d.items() if not isinstance(k, int)} + @final def _get_cleaned_column_resolvers(self) -> Dict[str, ABCSeries]: """ Return the special character free column resolvers of a dataframe. @@ -628,11 +641,13 @@ def size(self) -> int: """ return np.prod(self.shape) + @final @property def _selected_obj(self: FrameOrSeries) -> FrameOrSeries: """ internal compat with SelectionMixin """ return self + @final @property def _obj_with_exclusions(self: FrameOrSeries) -> FrameOrSeries: """ internal compat with SelectionMixin """ @@ -668,6 +683,7 @@ def set_axis(self, labels, axis: Axis = 0, inplace: bool = False): self._check_inplace_and_allows_duplicate_labels(inplace) return self._set_axis_nocheck(labels, axis, inplace) + @final def _set_axis_nocheck(self, labels, axis: Axis, inplace: bool): # NDFrame.rename with inplace=False calls set_axis(inplace=True) on a copy. if inplace: @@ -682,6 +698,7 @@ def _set_axis(self, axis: int, labels: Index) -> None: self._mgr.set_axis(axis, labels) self._clear_item_cache() + @final def swapaxes(self: FrameOrSeries, axis1, axis2, copy=True) -> FrameOrSeries: """ Interchange axes and swap values axes appropriately. @@ -711,6 +728,7 @@ def swapaxes(self: FrameOrSeries, axis1, axis2, copy=True) -> FrameOrSeries: new_values, *new_axes # type: ignore[arg-type] ).__finalize__(self, method="swapaxes") + @final def droplevel(self: FrameOrSeries, level, axis=0) -> FrameOrSeries: """ Return DataFrame with requested index / column level(s) removed. @@ -783,6 +801,7 @@ def pop(self, item: Label) -> Union[Series, Any]: return result + @final def squeeze(self, axis=None): """ Squeeze 1 dimensional axis objects into scalars. @@ -1247,6 +1266,7 @@ class name if not inplace: return result + @final def _set_axis_name(self, name, axis=0, inplace=False): """ Set the name(s) of the axis. @@ -1309,11 +1329,13 @@ def _set_axis_name(self, name, axis=0, inplace=False): # ---------------------------------------------------------------------- # Comparison Methods + @final def _indexed_same(self, other) -> bool: return all( self._get_axis(a).equals(other._get_axis(a)) for a in self._AXIS_ORDERS ) + @final def equals(self, other: object) -> bool: """ Test whether two objects contain the same elements. @@ -1400,6 +1422,7 @@ def equals(self, other: object) -> bool: # ------------------------------------------------------------------------- # Unary Methods + @final def __neg__(self): values = self._values if is_bool_dtype(values): @@ -1414,6 +1437,7 @@ def __neg__(self): raise TypeError(f"Unary negative expects numeric dtype, not {values.dtype}") return self.__array_wrap__(arr) + @final def __pos__(self): values = self._values if is_bool_dtype(values): @@ -1431,6 +1455,7 @@ def __pos__(self): ) return self.__array_wrap__(arr) + @final def __invert__(self): if not self.size: # inv fails with 0 len @@ -1440,6 +1465,7 @@ def __invert__(self): result = self._constructor(new_data).__finalize__(self, method="__invert__") return result + @final def __nonzero__(self): raise ValueError( f"The truth value of a {type(self).__name__} is ambiguous. " @@ -1448,6 +1474,7 @@ def __nonzero__(self): __bool__ = __nonzero__ + @final def bool(self): """ Return the bool of a single element Series or DataFrame. @@ -1492,9 +1519,11 @@ def bool(self): self.__nonzero__() + @final def __abs__(self: FrameOrSeries) -> FrameOrSeries: return self.abs() + @final def __round__(self: FrameOrSeries, decimals: int = 0) -> FrameOrSeries: return self.round(decimals) @@ -1506,6 +1535,7 @@ def __round__(self: FrameOrSeries, decimals: int = 0) -> FrameOrSeries: # operations should utilize/extend these methods when possible so that we # have consistent precedence and validation logic throughout the library. + @final def _is_level_reference(self, key, axis=0): """ Test whether a key is a level reference for a given axis. @@ -1536,6 +1566,7 @@ def _is_level_reference(self, key, axis=0): and not self._is_label_reference(key, axis=axis) ) + @final def _is_label_reference(self, key, axis=0) -> bool_t: """ Test whether a key is a label reference for a given axis. @@ -1565,6 +1596,7 @@ def _is_label_reference(self, key, axis=0) -> bool_t: and any(key in self.axes[ax] for ax in other_axes) ) + @final def _is_label_or_level_reference(self, key: str, axis: int = 0) -> bool_t: """ Test whether a key is a label or level reference for a given axis. @@ -1589,6 +1621,7 @@ def _is_label_or_level_reference(self, key: str, axis: int = 0) -> bool_t: key, axis=axis ) + @final def _check_label_or_level_ambiguity(self, key, axis: int = 0) -> None: """ Check whether `key` is ambiguous. @@ -1632,6 +1665,7 @@ def _check_label_or_level_ambiguity(self, key, axis: int = 0) -> None: ) raise ValueError(msg) + @final def _get_label_or_level_values(self, key: str, axis: int = 0) -> np.ndarray: """ Return a 1-D array of values associated with `key`, a label or level @@ -1696,6 +1730,7 @@ def _get_label_or_level_values(self, key: str, axis: int = 0) -> np.ndarray: return values + @final def _drop_labels_or_levels(self, keys, axis: int = 0): """ Drop labels and/or levels for the given `axis`. @@ -1933,6 +1968,7 @@ def __array_wrap__( # ---------------------------------------------------------------------- # Picklability + @final def __getstate__(self) -> Dict[str, Any]: meta = {k: getattr(self, k, None) for k in self._metadata} return dict( @@ -1944,6 +1980,7 @@ def __getstate__(self) -> Dict[str, Any]: **meta, ) + @final def __setstate__(self, state): if isinstance(state, BlockManager): self._mgr = state @@ -1988,6 +2025,7 @@ def __repr__(self) -> str: prepr = f"[{','.join(map(pprint_thing, self))}]" return f"{type(self).__name__}({prepr})" + @final def _repr_latex_(self): """ Returns a LaTeX representation for a particular object. @@ -1998,6 +2036,7 @@ def _repr_latex_(self): else: return None + @final def _repr_data_resource_(self): """ Not a real Jupyter special repr method, but we use the same @@ -2014,6 +2053,7 @@ def _repr_data_resource_(self): # ---------------------------------------------------------------------- # I/O Methods + @final @doc(klass="object") def to_excel( self, @@ -2166,6 +2206,7 @@ def to_excel( engine=engine, ) + @final def to_json( self, path_or_buf: Optional[FilePathOrBuffer] = None, @@ -2453,6 +2494,7 @@ def to_json( storage_options=storage_options, ) + @final def to_hdf( self, path_or_buf, @@ -2594,6 +2636,7 @@ def to_hdf( encoding=encoding, ) + @final def to_sql( self, name: str, @@ -2761,6 +2804,7 @@ def to_sql( method=method, ) + @final def to_pickle( self, path, @@ -2838,6 +2882,7 @@ def to_pickle( storage_options=storage_options, ) + @final def to_clipboard( self, excel: bool_t = True, sep: Optional[str] = None, **kwargs ) -> None: @@ -2899,6 +2944,7 @@ def to_clipboard( clipboards.to_clipboard(self, excel=excel, sep=sep, **kwargs) + @final def to_xarray(self): """ Return an xarray object from the pandas object. @@ -2982,6 +3028,7 @@ class (index) object 'bird' 'bird' 'mammal' 'mammal' else: return xarray.Dataset.from_dataframe(self) + @final @doc(returns=fmt.return_docstring) def to_latex( self, @@ -3167,6 +3214,7 @@ def to_latex( position=position, ) + @final def to_csv( self, path_or_buf: Optional[FilePathOrBuffer] = None, @@ -3376,6 +3424,7 @@ def to_csv( # ---------------------------------------------------------------------- # Lookup Caching + @final def _set_as_cached(self, item, cacher) -> None: """ Set the _cacher attribute on the calling object with a weakref to @@ -3383,6 +3432,7 @@ def _set_as_cached(self, item, cacher) -> None: """ self._cacher = (item, weakref.ref(cacher)) + @final def _reset_cacher(self) -> None: """ Reset the cacher. @@ -3390,6 +3440,7 @@ def _reset_cacher(self) -> None: if hasattr(self, "_cacher"): del self._cacher + @final def _maybe_cache_changed(self, item, value) -> None: """ The object has called back to us saying maybe it has changed. @@ -3397,11 +3448,13 @@ def _maybe_cache_changed(self, item, value) -> None: loc = self._info_axis.get_loc(item) self._mgr.iset(loc, value) + @final @property def _is_cached(self) -> bool_t: """Return boolean indicating if self is cached or not.""" return getattr(self, "_cacher", None) is not None + @final def _get_cacher(self): """return my cacher or None""" cacher = getattr(self, "_cacher", None) @@ -3409,6 +3462,7 @@ def _get_cacher(self): cacher = cacher[1]() return cacher + @final def _maybe_update_cacher( self, clear: bool_t = False, verify_is_copy: bool_t = True ) -> None: @@ -3446,6 +3500,7 @@ def _maybe_update_cacher( if clear: self._clear_item_cache() + @final def _clear_item_cache(self) -> None: self._item_cache.clear() @@ -3551,6 +3606,7 @@ class max_speed ) return self._constructor(new_data).__finalize__(self, method="take") + @final def _take_with_is_copy(self: FrameOrSeries, indices, axis=0) -> FrameOrSeries: """ Internal version of the `take` method that sets the `_is_copy` @@ -3565,6 +3621,7 @@ def _take_with_is_copy(self: FrameOrSeries, indices, axis=0) -> FrameOrSeries: result._set_is_copy(self) return result + @final def xs(self, key, axis=0, level=None, drop_level: bool_t = True): """ Return cross-section from the Series/DataFrame. @@ -3733,6 +3790,7 @@ class animal locomotion def __getitem__(self, item): raise AbstractMethodError(self) + @final def _get_item_cache(self, item): """Return the cached item, item represents a label indexer.""" cache = self._item_cache @@ -3783,6 +3841,7 @@ def _set_item(self, key, value) -> None: NDFrame._iset_item(self, loc, value) + @final def _set_is_copy(self, ref, copy: bool_t = True) -> None: if not copy: self._is_copy = None @@ -3790,6 +3849,7 @@ def _set_is_copy(self, ref, copy: bool_t = True) -> None: assert ref is not None self._is_copy = weakref.ref(ref) + @final def _check_is_chained_assignment_possible(self) -> bool_t: """ Check if we are a view, have a cacher, and are of mixed type. @@ -3810,6 +3870,7 @@ def _check_is_chained_assignment_possible(self) -> bool_t: self._check_setitem_copy(stacklevel=4, t="referent") return False + @final def _check_setitem_copy(self, stacklevel=4, t="setting", force=False): """ @@ -3924,6 +3985,7 @@ def __delitem__(self, key) -> None: # ---------------------------------------------------------------------- # Unsorted + @final def _check_inplace_and_allows_duplicate_labels(self, inplace): if inplace and not self.flags.allows_duplicate_labels: raise ValueError( @@ -3931,6 +3993,7 @@ def _check_inplace_and_allows_duplicate_labels(self, inplace): "'self.flags.allows_duplicate_labels' is False." ) + @final def get(self, key, default=None): """ Get item from object for given key (ex: DataFrame column). @@ -3950,11 +4013,13 @@ def get(self, key, default=None): except (KeyError, ValueError, IndexError): return default + @final @property def _is_view(self) -> bool_t: """Return boolean indicating if self is view of another array """ return self._mgr.is_view + @final def reindex_like( self: FrameOrSeries, other, @@ -4102,6 +4167,7 @@ def drop( else: return obj + @final def _drop_axis( self: FrameOrSeries, labels, axis, level=None, errors: str = "raise" ) -> FrameOrSeries: @@ -4157,6 +4223,7 @@ def _drop_axis( return result + @final def _update_inplace(self, result, verify_is_copy: bool_t = True) -> None: """ Replace self internals with result. @@ -4174,6 +4241,7 @@ def _update_inplace(self, result, verify_is_copy: bool_t = True) -> None: self._mgr = result._mgr self._maybe_update_cacher(verify_is_copy=verify_is_copy) + @final def add_prefix(self: FrameOrSeries, prefix: str) -> FrameOrSeries: """ Prefix labels with string `prefix`. @@ -4237,6 +4305,7 @@ def add_prefix(self: FrameOrSeries, prefix: str) -> FrameOrSeries: # "**Dict[str, partial[str]]"; expected "Union[str, int, None]" return self.rename(**mapper) # type: ignore[return-value, arg-type] + @final def add_suffix(self: FrameOrSeries, suffix: str) -> FrameOrSeries: """ Suffix labels with string `suffix`. @@ -4751,6 +4820,7 @@ def reindex(self: FrameOrSeries, *args, **kwargs) -> FrameOrSeries: axes, level, limit, tolerance, method, fill_value, copy ).__finalize__(self, method="reindex") + @final def _reindex_axes( self: FrameOrSeries, axes, level, limit, tolerance, method, fill_value, copy ) -> FrameOrSeries: @@ -4776,6 +4846,7 @@ def _reindex_axes( return obj + @final def _needs_reindex_multi(self, axes, method, level) -> bool_t: """Check if we do need a multi reindex.""" return ( @@ -4788,6 +4859,7 @@ def _needs_reindex_multi(self, axes, method, level) -> bool_t: def _reindex_multi(self, axes, copy, fill_value): raise AbstractMethodError(self) + @final def _reindex_with_indexers( self: FrameOrSeries, reindexers, @@ -7893,6 +7965,7 @@ def between_time( ) return self._take_with_is_copy(indexer, axis=axis) + @final def resample( self, rule, From 945c29c394fb244d9bc5d4bae9c9830112b6e2ae Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 7 Nov 2020 18:44:59 -0800 Subject: [PATCH 5/8] final --- pandas/core/generic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index ce15bca1e8325..4ed9106ab5955 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1861,6 +1861,7 @@ def __len__(self) -> int: """Returns length of info axis""" return len(self._info_axis) + @final def __contains__(self, key) -> bool_t: """True if the key is in the info axis""" return key in self._info_axis From 5608229e71e0fad219107f5c0e865409be3f47bf Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 9 Nov 2020 08:29:54 -0800 Subject: [PATCH 6/8] post-merge mypy fixup --- pandas/core/indexes/datetimes.py | 4 +--- pandas/io/formats/excel.py | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 042446dbf5553..9744eb0ecbb88 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -811,9 +811,7 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): end_casted = self._maybe_cast_slice_bound(end, "right", kind) mask = (self <= end_casted) & mask - # pandas\core\indexes\datetimes.py:764: error: "bool" has no - # attribute "nonzero" [attr-defined] - indexer = mask.nonzero()[0][::step] # type: ignore[attr-defined] + indexer = mask.nonzero()[0][::step] if len(indexer) == len(self): return slice(None) else: diff --git a/pandas/io/formats/excel.py b/pandas/io/formats/excel.py index 9793f7a1e4613..12e0da36f7c3e 100644 --- a/pandas/io/formats/excel.py +++ b/pandas/io/formats/excel.py @@ -594,13 +594,13 @@ def _format_header_regular(self): # has incompatible type "Union[Sequence[Optional[Hashable]], # bool]"; expected "Sized" [arg-type] if len(self.header) != len(self.columns): # type: ignore[arg-type] - # pandas\io\formats\excel.py:595: error: Argument 1 to + # pandas\io\formats\excel.py:603: error: Argument 1 to # "len" has incompatible type # "Union[Sequence[Optional[Hashable]], bool]"; expected # "Sized" [arg-type] raise ValueError( - f"Writing {len(self.columns)} " # type: ignore[arg-type] - f"cols but got {len(self.header)} aliases" + f"Writing {len(self.columns)} cols but " + f"got {len(self.header)} aliases" # type: ignore[arg-type] ) else: colnames = self.header From be679eb09b8d669a185d2f9ad51a4bd2c1fd23bf Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 9 Nov 2020 09:08:08 -0800 Subject: [PATCH 7/8] mypy troubleshoot --- pandas/io/formats/excel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/io/formats/excel.py b/pandas/io/formats/excel.py index 12e0da36f7c3e..0916494d8ab60 100644 --- a/pandas/io/formats/excel.py +++ b/pandas/io/formats/excel.py @@ -594,13 +594,13 @@ def _format_header_regular(self): # has incompatible type "Union[Sequence[Optional[Hashable]], # bool]"; expected "Sized" [arg-type] if len(self.header) != len(self.columns): # type: ignore[arg-type] - # pandas\io\formats\excel.py:603: error: Argument 1 to + # pandas\io\formats\excel.py:602: error: Argument 1 to # "len" has incompatible type # "Union[Sequence[Optional[Hashable]], bool]"; expected # "Sized" [arg-type] raise ValueError( - f"Writing {len(self.columns)} cols but " - f"got {len(self.header)} aliases" # type: ignore[arg-type] + f"Writing {len(self.columns)} cols " # type: ignore[arg-type] + f"but got {len(self.header)} aliases" ) else: colnames = self.header From 780e3ea66f7bac308a15cbec44af95849dcf5ab5 Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 10 Nov 2020 10:01:45 -0800 Subject: [PATCH 8/8] type ignore for describe --- pandas/core/generic.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 31283afd78fd4..255c45d5a45aa 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -39,7 +39,6 @@ CompressionOptions, FilePathOrBuffer, FrameOrSeries, - FrameOrSeriesUnion, IndexKeyFunc, IndexLabel, JSONSerializable, @@ -9996,7 +9995,7 @@ def describe( include=None, exclude=None, datetime_is_numeric=False, - ) -> FrameOrSeriesUnion: + ) -> FrameOrSeries: """ Generate descriptive statistics. @@ -10346,7 +10345,9 @@ def describe_1d(data) -> "Series": return describe_categorical_1d(data) if self.ndim == 1: - return describe_1d(self) + # Incompatible return value type + # (got "Series", expected "FrameOrSeries") [return-value] + return describe_1d(self) # type:ignore[return-value] elif (include is None) and (exclude is None): # when some numerics are found, keep only numerics default_include = [np.number]