Skip to content

Commit

Permalink
upgrade mypy and pyright. close 8 issues (#1087)
Browse files Browse the repository at this point in the history
* update mypy and allow timestamp slicing

* fix for getitem with pythong 3.12 and mypy

* allow apply to return an offset

* allow loc setitem to accept a slice

* allow indexing with .loc using Timestamp

* define __bool__ for Series and DataFrame to be NoReturn

* support Path div

* change TypeVar to use Union where possible

* support dict keys in constructor to Series

* remove path overloads. add assert False for unreachable code
  • Loading branch information
Dr-Irv authored Jan 4, 2025
1 parent 8f48f32 commit d410f2e
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 71 deletions.
5 changes: 4 additions & 1 deletion pandas-stubs/_libs/tslibs/timestamps.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ from time import struct_time
from typing import (
ClassVar,
Literal,
SupportsIndex,
overload,
)

Expand Down Expand Up @@ -48,7 +49,7 @@ _Nonexistent: TypeAlias = (
Literal["raise", "NaT", "shift_backward", "shift_forward"] | Timedelta | timedelta
)

class Timestamp(datetime):
class Timestamp(datetime, SupportsIndex):
min: ClassVar[Timestamp] # pyright: ignore[reportIncompatibleVariableOverride]
max: ClassVar[Timestamp] # pyright: ignore[reportIncompatibleVariableOverride]

Expand Down Expand Up @@ -309,3 +310,5 @@ class Timestamp(datetime):
@property
def unit(self) -> TimeUnit: ...
def as_unit(self, unit: TimeUnit, round_ok: bool = ...) -> Self: ...
# To support slicing
def __index__(self) -> int: ...
5 changes: 3 additions & 2 deletions pandas-stubs/_typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Incomplete: TypeAlias = Any
ArrayLike: TypeAlias = ExtensionArray | np.ndarray[Any, Any]
AnyArrayLike: TypeAlias = Index[Any] | Series[Any] | np.ndarray[Any, Any]
PythonScalar: TypeAlias = str | bool | complex
DatetimeLikeScalar = TypeVar("DatetimeLikeScalar", Period, Timestamp, Timedelta)
DatetimeLikeScalar = TypeVar("DatetimeLikeScalar", bound=Period | Timestamp | Timedelta)
PandasScalar: TypeAlias = bytes | datetime.date | datetime.datetime | datetime.timedelta
IntStrT = TypeVar("IntStrT", int, str)
# Scalar: TypeAlias = PythonScalar | PandasScalar
Expand Down Expand Up @@ -490,7 +490,8 @@ AxisColumn: TypeAlias = Literal["columns", 1]
Axis: TypeAlias = AxisIndex | AxisColumn
DtypeNp = TypeVar("DtypeNp", bound=np.dtype[np.generic])
KeysArgType: TypeAlias = Any
ListLike = TypeVar("ListLike", Sequence, np.ndarray, Series, Index)
ListLike: TypeAlias = Sequence | np.ndarray | Series | Index
ListLikeT = TypeVar("ListLikeT", bound=ListLike)
ListLikeExceptSeriesAndStr: TypeAlias = (
MutableSequence[Any] | np.ndarray[Any, Any] | tuple[Any, ...] | Index[Any]
)
Expand Down
2 changes: 1 addition & 1 deletion pandas-stubs/core/dtypes/concat.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ from pandas import (
Series,
)

_CatT = TypeVar("_CatT", Categorical, CategoricalIndex, Series)
_CatT = TypeVar("_CatT", bound=Categorical | CategoricalIndex | Series)

def union_categoricals(
to_union: list[_CatT], sort_categories: bool = ..., ignore_order: bool = ...
Expand Down
51 changes: 39 additions & 12 deletions pandas-stubs/core/frame.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ from collections.abc import (
)
import datetime as dt
from re import Pattern
import sys
from typing import (
Any,
ClassVar,
Literal,
NoReturn,
overload,
)

Expand Down Expand Up @@ -112,6 +114,7 @@ from pandas._typing import (
ReplaceMethod,
Scalar,
ScalarT,
SequenceNotStr,
SeriesByT,
SortKind,
StataDateFormat,
Expand Down Expand Up @@ -193,7 +196,11 @@ class _LocIndexerFrame(_LocIndexer):
def __getitem__( # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload]
self,
idx: tuple[
int | StrLike | tuple[Scalar, ...] | Callable[[DataFrame], ScalarT],
int
| StrLike
| Timestamp
| tuple[Scalar, ...]
| Callable[[DataFrame], ScalarT],
int | StrLike | tuple[Scalar, ...],
],
) -> Scalar: ...
Expand All @@ -206,6 +213,7 @@ class _LocIndexerFrame(_LocIndexer):
IndexType
| MaskType
| _IndexSliceTuple
| SequenceNotStr[float | str | Timestamp]
| Callable[
[DataFrame], ScalarT | list[HashableT] | IndexType | MaskType
],
Expand All @@ -219,7 +227,9 @@ class _LocIndexerFrame(_LocIndexer):
@overload
def __setitem__(
self,
idx: MaskType | StrLike | _IndexSliceTuple | list[ScalarT] | IndexingInt,
idx: (
MaskType | StrLike | _IndexSliceTuple | list[ScalarT] | IndexingInt | slice
),
value: Scalar | NAType | NaTType | ArrayLike | Series | DataFrame | list | None,
) -> None: ...
@overload
Expand All @@ -229,8 +239,32 @@ class _LocIndexerFrame(_LocIndexer):
value: Scalar | NAType | NaTType | ArrayLike | Series | list | None,
) -> None: ...

class DataFrame(NDFrame, OpsMixin):
__hash__: ClassVar[None] # type: ignore[assignment]
# With mypy 1.14.1 and python 3.12, the second overload needs a type-ignore statement
if sys.version_info >= (3, 12):
class _GetItemHack:
@overload
def __getitem__(self, key: Scalar | tuple[Hashable, ...]) -> Series: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload]
@overload
def __getitem__( # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload]
self, key: Iterable[Hashable] | slice
) -> DataFrame: ...
@overload
def __getitem__(self, key: Hashable) -> Series: ...

else:
class _GetItemHack:
@overload
def __getitem__(self, key: Scalar | tuple[Hashable, ...]) -> Series: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload]
@overload
def __getitem__( # pyright: ignore[reportOverlappingOverload]
self, key: Iterable[Hashable] | slice
) -> DataFrame: ...
@overload
def __getitem__(self, key: Hashable) -> Series: ...

class DataFrame(NDFrame, OpsMixin, _GetItemHack):

__hash__: ClassVar[None] # type: ignore[assignment] # pyright: ignore[reportIncompatibleMethodOverride]

@overload
def __new__(
Expand Down Expand Up @@ -607,14 +641,6 @@ class DataFrame(NDFrame, OpsMixin):
@property
def T(self) -> DataFrame: ...
def __getattr__(self, name: str) -> Series: ...
@overload
def __getitem__(self, key: Scalar | tuple[Hashable, ...]) -> Series: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload]
@overload
def __getitem__( # pyright: ignore[reportOverlappingOverload]
self, key: Iterable[Hashable] | slice
) -> DataFrame: ...
@overload
def __getitem__(self, key: Hashable) -> Series: ...
def isetitem(
self, loc: int | Sequence[int], value: Scalar | ArrayLike | list[Any]
) -> None: ...
Expand Down Expand Up @@ -2453,6 +2479,7 @@ class DataFrame(NDFrame, OpsMixin):
) -> Self: ...
def __truediv__(self, other: float | DataFrame | Series | Sequence) -> Self: ...
def __rtruediv__(self, other: float | DataFrame | Series | Sequence) -> Self: ...
def __bool__(self) -> NoReturn: ...

class _PandasNamedTuple(tuple[Any, ...]):
def __getattr__(self, field: str) -> Scalar: ...
44 changes: 25 additions & 19 deletions pandas-stubs/core/indexes/accessors.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ from pandas._typing import (

class Properties(PandasDelegate, NoNewAttributesMixin): ...

_DTFieldOpsReturnType = TypeVar("_DTFieldOpsReturnType", Series[int], Index[int])
_DTFieldOpsReturnType = TypeVar("_DTFieldOpsReturnType", bound=Series[int] | Index[int])

class _DayLikeFieldOps(Generic[_DTFieldOpsReturnType]):
@property
Expand Down Expand Up @@ -84,7 +84,9 @@ class _DatetimeFieldOps(
_DayLikeFieldOps[_DTFieldOpsReturnType], _MiniSeconds[_DTFieldOpsReturnType]
): ...

_DTBoolOpsReturnType = TypeVar("_DTBoolOpsReturnType", Series[bool], np_ndarray_bool)
_DTBoolOpsReturnType = TypeVar(
"_DTBoolOpsReturnType", bound=Series[bool] | np_ndarray_bool
)

class _IsLeapYearProperty(Generic[_DTBoolOpsReturnType]):
@property
Expand All @@ -106,7 +108,7 @@ class _DatetimeBoolOps(
@property
def is_year_end(self) -> _DTBoolOpsReturnType: ...

_DTFreqReturnType = TypeVar("_DTFreqReturnType", str, BaseOffset)
_DTFreqReturnType = TypeVar("_DTFreqReturnType", bound=str | BaseOffset)

class _FreqProperty(Generic[_DTFreqReturnType]):
@property
Expand All @@ -121,10 +123,10 @@ class _DatetimeObjectOps(
): ...

_DTOtherOpsDateReturnType = TypeVar(
"_DTOtherOpsDateReturnType", Series[dt.date], np.ndarray
"_DTOtherOpsDateReturnType", bound=Series[dt.date] | np.ndarray
)
_DTOtherOpsTimeReturnType = TypeVar(
"_DTOtherOpsTimeReturnType", Series[dt.time], np.ndarray
"_DTOtherOpsTimeReturnType", bound=Series[dt.time] | np.ndarray
)

class _DatetimeOtherOps(Generic[_DTOtherOpsDateReturnType, _DTOtherOpsTimeReturnType]):
Expand Down Expand Up @@ -157,11 +159,7 @@ class _DatetimeLikeOps(

_DTTimestampTimedeltaReturnType = TypeVar(
"_DTTimestampTimedeltaReturnType",
Series,
TimestampSeries,
TimedeltaSeries,
DatetimeIndex,
TimedeltaIndex,
bound=Series | TimestampSeries | TimedeltaSeries | DatetimeIndex | TimedeltaIndex,
)

class _DatetimeRoundingMethods(Generic[_DTTimestampTimedeltaReturnType]):
Expand Down Expand Up @@ -199,8 +197,10 @@ class _DatetimeRoundingMethods(Generic[_DTTimestampTimedeltaReturnType]):
_DTNormalizeReturnType = TypeVar(
"_DTNormalizeReturnType", TimestampSeries, DatetimeIndex
)
_DTStrKindReturnType = TypeVar("_DTStrKindReturnType", Series[str], Index)
_DTToPeriodReturnType = TypeVar("_DTToPeriodReturnType", PeriodSeries, PeriodIndex)
_DTStrKindReturnType = TypeVar("_DTStrKindReturnType", bound=Series[str] | Index)
_DTToPeriodReturnType = TypeVar(
"_DTToPeriodReturnType", bound=PeriodSeries | PeriodIndex
)

class _DatetimeLikeNoTZMethods(
_DatetimeRoundingMethods[_DTTimestampTimedeltaReturnType],
Expand Down Expand Up @@ -289,9 +289,11 @@ class DatetimeProperties(
def as_unit(self, unit: TimeUnit) -> _DTTimestampTimedeltaReturnType: ...

_TDNoRoundingMethodReturnType = TypeVar(
"_TDNoRoundingMethodReturnType", Series[int], Index
"_TDNoRoundingMethodReturnType", bound=Series[int] | Index
)
_TDTotalSecondsReturnType = TypeVar(
"_TDTotalSecondsReturnType", bound=Series[float] | Index
)
_TDTotalSecondsReturnType = TypeVar("_TDTotalSecondsReturnType", Series[float], Index)

class _TimedeltaPropertiesNoRounding(
Generic[_TDNoRoundingMethodReturnType, _TDTotalSecondsReturnType]
Expand All @@ -318,11 +320,15 @@ class TimedeltaProperties(
def unit(self) -> TimeUnit: ...
def as_unit(self, unit: TimeUnit) -> TimedeltaSeries: ...

_PeriodDTReturnTypes = TypeVar("_PeriodDTReturnTypes", TimestampSeries, DatetimeIndex)
_PeriodIntReturnTypes = TypeVar("_PeriodIntReturnTypes", Series[int], Index[int])
_PeriodStrReturnTypes = TypeVar("_PeriodStrReturnTypes", Series[str], Index)
_PeriodDTAReturnTypes = TypeVar("_PeriodDTAReturnTypes", DatetimeArray, DatetimeIndex)
_PeriodPAReturnTypes = TypeVar("_PeriodPAReturnTypes", PeriodArray, PeriodIndex)
_PeriodDTReturnTypes = TypeVar(
"_PeriodDTReturnTypes", bound=TimestampSeries | DatetimeIndex
)
_PeriodIntReturnTypes = TypeVar("_PeriodIntReturnTypes", bound=Series[int] | Index[int])
_PeriodStrReturnTypes = TypeVar("_PeriodStrReturnTypes", bound=Series[str] | Index)
_PeriodDTAReturnTypes = TypeVar(
"_PeriodDTAReturnTypes", bound=DatetimeArray | DatetimeIndex
)
_PeriodPAReturnTypes = TypeVar("_PeriodPAReturnTypes", bound=PeriodArray | PeriodIndex)

class _PeriodProperties(
Generic[
Expand Down
24 changes: 18 additions & 6 deletions pandas-stubs/core/series.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections import dict_keys # type: ignore[attr-defined]
from collections.abc import (
Callable,
Hashable,
Expand All @@ -13,11 +14,13 @@ from datetime import (
time,
timedelta,
)
from pathlib import Path
from typing import (
Any,
ClassVar,
Generic,
Literal,
NoReturn,
overload,
)

Expand Down Expand Up @@ -139,6 +142,7 @@ from pandas._typing import (
ReplaceMethod,
Scalar,
ScalarT,
SequenceNotStr,
SeriesByT,
SortKind,
StrDtypeArg,
Expand Down Expand Up @@ -195,8 +199,7 @@ class _LocIndexerSeries(_LocIndexer, Generic[S1]):
idx: (
MaskType
| Index
| Sequence[float]
| list[str]
| SequenceNotStr[float | str | Timestamp]
| slice
| _IndexSliceTuple
| Sequence[_IndexSliceTuple]
Expand All @@ -208,7 +211,7 @@ class _LocIndexerSeries(_LocIndexer, Generic[S1]):
@overload
def __setitem__(
self,
idx: Index | MaskType,
idx: Index | MaskType | slice,
value: S1 | ArrayLike | Series[S1] | None,
) -> None: ...
@overload
Expand Down Expand Up @@ -352,7 +355,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
@overload
def __new__(
cls,
data: S1 | _ListLike[S1] | dict[HashableT1, S1],
data: S1 | _ListLike[S1] | dict[HashableT1, S1] | dict_keys[S1, Any],
index: Axes | None = ...,
*,
dtype: Dtype = ...,
Expand Down Expand Up @@ -1030,6 +1033,14 @@ class Series(IndexOpsMixin[S1], NDFrame):
**kwds,
) -> Series: ...
@overload
def apply(
self,
func: Callable[..., BaseOffset],
convertDType: _bool = ...,
args: tuple = ...,
**kwds,
) -> OffsetSeries: ...
@overload
def apply(
self,
func: Callable[..., Series],
Expand Down Expand Up @@ -1640,7 +1651,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
self, other: int | np_ndarray_anyint | Series[int]
) -> Series[int]: ...
def __rsub__(self, other: num | _ListLike | Series[S1]) -> Series: ...
def __rtruediv__(self, other: num | _ListLike | Series[S1]) -> Series: ...
def __rtruediv__(self, other: num | _ListLike | Series[S1] | Path) -> Series: ...
# ignore needed for mypy as we want different results based on the arguments
@overload # type: ignore[override]
def __rxor__( # pyright: ignore[reportOverlappingOverload]
Expand All @@ -1666,7 +1677,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
) -> TimedeltaSeries: ...
@overload
def __sub__(self, other: num | _ListLike | Series) -> Series: ...
def __truediv__(self, other: num | _ListLike | Series[S1]) -> Series: ...
def __truediv__(self, other: num | _ListLike | Series[S1] | Path) -> Series: ...
# ignore needed for mypy as we want different results based on the arguments
@overload # type: ignore[override]
def __xor__( # pyright: ignore[reportOverlappingOverload]
Expand Down Expand Up @@ -2144,6 +2155,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
level: Level | None = ...,
drop_level: _bool = ...,
) -> Self: ...
def __bool__(self) -> NoReturn: ...

class TimestampSeries(Series[Timestamp]):
@property
Expand Down
6 changes: 3 additions & 3 deletions pandas-stubs/core/strings.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ from pandas._typing import (
)

# The _TS type is what is used for the result of str.split with expand=True
_TS = TypeVar("_TS", DataFrame, MultiIndex)
_TS = TypeVar("_TS", bound=DataFrame | MultiIndex)
# The _TS2 type is what is used for the result of str.split with expand=False
_TS2 = TypeVar("_TS2", Series[list[str]], Index[list[str]])
_TS2 = TypeVar("_TS2", bound=Series[list[str]] | Index[list[str]])
# The _TM type is what is used for the result of str.match
_TM = TypeVar("_TM", Series[bool], np_ndarray_bool)
_TM = TypeVar("_TM", bound=Series[bool] | np_ndarray_bool)

class StringMethods(NoNewAttributesMixin, Generic[T, _TS, _TM, _TS2]):
def __init__(self, data: T) -> None: ...
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ types-pytz = ">= 2022.1.1"
numpy = ">= 1.23.5"

[tool.poetry.group.dev.dependencies]
mypy = "1.13.0"
mypy = "1.14.1"
pandas = "2.2.3"
pyarrow = ">=10.0.1"
pytest = ">=7.1.2"
pyright = ">= 1.1.390"
pyright = ">= 1.1.391"
poethepoet = ">=0.16.5"
loguru = ">=0.6.0"
typing-extensions = ">=4.4.0"
Expand Down
Loading

0 comments on commit d410f2e

Please sign in to comment.