From 67b524dd2853857c3463a1d2ee87ea79399a3b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20W=C3=B6rtwein?= Date: Sun, 4 Feb 2024 19:01:25 -0500 Subject: [PATCH 1/2] TYP: misc Index return types --- pandas/_typing.py | 1 + pandas/core/generic.py | 2 +- pandas/core/indexes/accessors.py | 5 +++-- pandas/core/indexes/base.py | 7 ++++--- pandas/core/indexes/datetimelike.py | 4 ++-- pandas/core/indexes/datetimes.py | 6 ++++-- pandas/core/indexes/multi.py | 3 ++- pandas/core/indexes/range.py | 2 +- pandas/core/indexes/timedeltas.py | 6 +++++- 9 files changed, 23 insertions(+), 13 deletions(-) diff --git a/pandas/_typing.py b/pandas/_typing.py index 1fec41463904c..8646b7425894d 100644 --- a/pandas/_typing.py +++ b/pandas/_typing.py @@ -189,6 +189,7 @@ def __reversed__(self) -> Iterator[_T_co]: # passed in, a DataFrame is always returned. NDFrameT = TypeVar("NDFrameT", bound="NDFrame") +IndexT = TypeVar("IndexT", bound="Index") NumpyIndexT = TypeVar("NumpyIndexT", np.ndarray, "Index") AxisInt = int diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 7bb07694c34a5..3b8e3586346a0 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -10745,7 +10745,7 @@ def _shift_with_freq(self, periods: int, axis: int, freq) -> Self: f"does not match PeriodIndex freq " f"{freq_to_period_freqstr(orig_freq.n, orig_freq.name)}" ) - new_ax = index.shift(periods) + new_ax: Index = index.shift(periods) else: new_ax = index.shift(periods, freq) diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 1a24ae8530c12..a91fb0a8d718d 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -5,6 +5,7 @@ from typing import ( TYPE_CHECKING, + NoReturn, cast, ) import warnings @@ -108,7 +109,7 @@ def _delegate_property_get(self, name: str): # return the result as a Series return Series(result, index=index, name=self.name).__finalize__(self._parent) - def _delegate_property_set(self, name: str, value, *args, **kwargs): + def _delegate_property_set(self, name: str, value, *args, **kwargs) -> NoReturn: raise ValueError( "modifications to a property of a datetimelike object are not supported. " "Change values on the original." @@ -483,7 +484,7 @@ def to_pytimedelta(self) -> np.ndarray: return self._get_values().to_pytimedelta() @property - def components(self): + def components(self) -> DataFrame: """ Return a Dataframe of the components of the Timedeltas. diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 87135ce9e0dd0..42613ca4c6573 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -55,6 +55,7 @@ F, IgnoreRaise, IndexLabel, + IndexT, JoinHow, Level, NaPosition, @@ -2027,7 +2028,7 @@ def sortlevel( ascending: bool | list[bool] = True, sort_remaining=None, na_position: NaPosition = "first", - ): + ) -> tuple[Self, np.ndarray]: """ For internal compatibility with the Index API. @@ -4432,7 +4433,7 @@ def _wrap_reindex_result(self, target, indexer, preserve_names: bool): target = self._maybe_preserve_names(target, preserve_names) return target - def _maybe_preserve_names(self, target: Index, preserve_names: bool): + def _maybe_preserve_names(self, target: IndexT, preserve_names: bool) -> IndexT: if preserve_names and target.nlevels == 1 and target.name != self.name: target = target.copy(deep=False) target.name = self.name @@ -5987,7 +5988,7 @@ def sort(self, *args, **kwargs): """ raise TypeError("cannot sort an Index object in-place, use sort_values instead") - def shift(self, periods: int = 1, freq=None): + def shift(self, periods: int = 1, freq=None) -> Self: """ Shift index by desired number of time frequency increments. diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index ae13edab3a35a..a5670536c74f7 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -273,7 +273,7 @@ def _can_partial_date_slice(self, reso: Resolution) -> bool: def _parsed_string_to_bounds(self, reso: Resolution, parsed): raise NotImplementedError - def _parse_with_reso(self, label: str): + def _parse_with_reso(self, label: str) -> tuple[datetime, Resolution]: # overridden by TimedeltaIndex try: if self.freq is None or hasattr(self.freq, "rule_code"): @@ -295,7 +295,7 @@ def _parse_with_reso(self, label: str): reso = Resolution.from_attrname(reso_str) return parsed, reso - def _get_string_slice(self, key: str): + def _get_string_slice(self, key: str) -> slice | npt.NDArray[np.intp]: # overridden by TimedeltaIndex parsed, reso = self._parse_with_reso(key) try: diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index c978abd8c2427..3cf3352e64f27 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -518,7 +518,9 @@ def snap(self, freq: Frequency = "S") -> DatetimeIndex: # -------------------------------------------------------------------- # Indexing Methods - def _parsed_string_to_bounds(self, reso: Resolution, parsed: dt.datetime): + def _parsed_string_to_bounds( + self, reso: Resolution, parsed: dt.datetime + ) -> tuple[Timestamp, Timestamp]: """ Calculate datetime bounds for parsed time string and its resolution. @@ -555,7 +557,7 @@ def _parsed_string_to_bounds(self, reso: Resolution, parsed: dt.datetime): # which localizes parsed. return start, end - def _parse_with_reso(self, label: str): + def _parse_with_reso(self, label: str) -> tuple[Timestamp, Resolution]: parsed, reso = super()._parse_with_reso(label) parsed = Timestamp(parsed) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 0495f23508c09..f4bf4f3b2f275 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -37,6 +37,7 @@ F, IgnoreRaise, IndexLabel, + IndexT, Scalar, Self, Shape, @@ -2727,7 +2728,7 @@ def _wrap_reindex_result(self, target, indexer, preserve_names: bool): target = self._maybe_preserve_names(target, preserve_names) return target - def _maybe_preserve_names(self, target: Index, preserve_names: bool) -> Index: + def _maybe_preserve_names(self, target: IndexT, preserve_names: bool) -> IndexT: if ( preserve_names and target.nlevels == self.nlevels diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 16b203931c073..2edf6057442b6 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -876,7 +876,7 @@ def _difference(self, other, sort=None): def symmetric_difference( self, other, result_name: Hashable | None = None, sort=None - ): + ) -> Index: if not isinstance(other, RangeIndex) or sort is not None: return super().symmetric_difference(other, result_name, sort) diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 08a265ba47648..db813b047b2bb 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -32,6 +32,7 @@ from pandas.core.indexes.extension import inherit_names if TYPE_CHECKING: + from pandas._libs import NaTType from pandas._typing import DtypeObj @@ -245,7 +246,10 @@ def get_loc(self, key): return Index.get_loc(self, key) - def _parse_with_reso(self, label: str): + # error: Return type "tuple[Timedelta | NaTType, None]" of "_parse_with_reso" + # incompatible with return type "tuple[datetime, Resolution]" in supertype + # "DatetimeIndexOpsMixin" + def _parse_with_reso(self, label: str) -> tuple[Timedelta | NaTType, None]: # type: ignore[override] # the "with_reso" is a no-op for TimedeltaIndex parsed = Timedelta(label) return parsed, None From dbe4b91faa4208b549979f024d2de3e3707a8861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20W=C3=B6rtwein?= Date: Mon, 5 Feb 2024 16:34:55 -0500 Subject: [PATCH 2/2] add IndexT to ignore list --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 934f66136f601..7f8b13e74cdf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -758,5 +758,5 @@ exclude_lines = [ directory = "coverage_html_report" [tool.codespell] -ignore-words-list = "blocs, coo, hist, nd, sav, ser, recuse, nin, timere, expec, expecs" +ignore-words-list = "blocs, coo, hist, nd, sav, ser, recuse, nin, timere, expec, expecs, indext" ignore-regex = 'https://([\w/\.])+'