From 288450e0dfec4d1a7060e1761575ded1d763711b Mon Sep 17 00:00:00 2001 From: patrick Date: Thu, 7 Jul 2022 20:23:57 +0200 Subject: [PATCH 1/4] TYP: Make typing of inclusive consistent --- pandas/_libs/interval.pyi | 14 +++++++------- pandas/_typing.py | 2 +- pandas/core/arrays/arrow/_arrow_utils.py | 3 ++- pandas/core/arrays/interval.py | 18 +++++++++--------- pandas/core/dtypes/dtypes.py | 11 ++++------- pandas/core/generic.py | 6 +++--- pandas/core/indexes/datetimes.py | 6 +++--- pandas/core/indexes/interval.py | 14 +++++++------- pandas/io/formats/style.py | 7 ++++--- pandas/util/_validators.py | 3 ++- 10 files changed, 42 insertions(+), 42 deletions(-) diff --git a/pandas/_libs/interval.pyi b/pandas/_libs/interval.pyi index 3bd5dd2042e69..a89ca9d5f74b1 100644 --- a/pandas/_libs/interval.pyi +++ b/pandas/_libs/interval.pyi @@ -12,7 +12,7 @@ import numpy.typing as npt from pandas._libs import lib from pandas._typing import ( - IntervalClosedType, + IntervalInclusiveType, Timedelta, Timestamp, ) @@ -56,7 +56,7 @@ class IntervalMixin: def _warning_interval( inclusive, closed -) -> tuple[IntervalClosedType, lib.NoDefault]: ... +) -> tuple[IntervalInclusiveType, lib.NoDefault]: ... class Interval(IntervalMixin, Generic[_OrderableT]): @property @@ -64,17 +64,17 @@ class Interval(IntervalMixin, Generic[_OrderableT]): @property def right(self: Interval[_OrderableT]) -> _OrderableT: ... @property - def inclusive(self) -> IntervalClosedType: ... + def inclusive(self) -> IntervalInclusiveType: ... @property - def closed(self) -> IntervalClosedType: ... + def closed(self) -> IntervalInclusiveType: ... mid: _MidDescriptor length: _LengthDescriptor def __init__( self, left: _OrderableT, right: _OrderableT, - inclusive: IntervalClosedType = ..., - closed: IntervalClosedType = ..., + inclusive: IntervalInclusiveType = ..., + closed: IntervalInclusiveType = ..., ) -> None: ... def __hash__(self) -> int: ... @overload @@ -158,7 +158,7 @@ class IntervalTree(IntervalMixin): self, left: np.ndarray, right: np.ndarray, - inclusive: IntervalClosedType = ..., + inclusive: IntervalInclusiveType = ..., leaf_size: int = ..., ) -> None: ... @property diff --git a/pandas/_typing.py b/pandas/_typing.py index ac1237f8841be..4bc5f75400455 100644 --- a/pandas/_typing.py +++ b/pandas/_typing.py @@ -314,7 +314,7 @@ def closed(self) -> bool: # Interval closed type IntervalLeftRight = Literal["left", "right"] -IntervalClosedType = Union[IntervalLeftRight, Literal["both", "neither"]] +IntervalInclusiveType = Union[IntervalLeftRight, Literal["both", "neither"]] # datetime and NaTType DatetimeNaTType = Union[datetime, "NaTType"] diff --git a/pandas/core/arrays/arrow/_arrow_utils.py b/pandas/core/arrays/arrow/_arrow_utils.py index e4bb7dc94cb8d..3c7e9e0ee18e4 100644 --- a/pandas/core/arrays/arrow/_arrow_utils.py +++ b/pandas/core/arrays/arrow/_arrow_utils.py @@ -6,6 +6,7 @@ import numpy as np import pyarrow +from pandas._typing import IntervalInclusiveType from pandas.errors import PerformanceWarning from pandas.util._decorators import deprecate_kwarg from pandas.util._exceptions import find_stack_level @@ -105,7 +106,7 @@ def to_pandas_dtype(self): class ArrowIntervalType(pyarrow.ExtensionType): @deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive") - def __init__(self, subtype, inclusive: str) -> None: + def __init__(self, subtype, inclusive: IntervalInclusiveType) -> None: # attributes need to be set first before calling # super init (as that calls serialize) assert inclusive in VALID_CLOSED diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index 56aae3039f7d6..bdcee60b29483 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -32,7 +32,7 @@ from pandas._typing import ( ArrayLike, Dtype, - IntervalClosedType, + IntervalInclusiveType, NpDtype, PositionalIndexer, ScalarIndexer, @@ -226,7 +226,7 @@ def ndim(self) -> Literal[1]: def __new__( cls: type[IntervalArrayT], data, - inclusive: str | None = None, + inclusive: IntervalInclusiveType | None = None, dtype: Dtype | None = None, copy: bool = False, verify_integrity: bool = True, @@ -273,7 +273,7 @@ def _simple_new( cls: type[IntervalArrayT], left, right, - inclusive=None, + inclusive: IntervalInclusiveType | None = None, copy: bool = False, dtype: Dtype | None = None, verify_integrity: bool = True, @@ -427,7 +427,7 @@ def _from_factorized( def from_breaks( cls: type[IntervalArrayT], breaks, - inclusive: IntervalClosedType | None = None, + inclusive: IntervalInclusiveType | None = None, copy: bool = False, dtype: Dtype | None = None, ) -> IntervalArrayT: @@ -509,7 +509,7 @@ def from_arrays( cls: type[IntervalArrayT], left, right, - inclusive: IntervalClosedType | None = None, + inclusive: IntervalInclusiveType | None = None, copy: bool = False, dtype: Dtype | None = None, ) -> IntervalArrayT: @@ -582,7 +582,7 @@ def from_arrays( def from_tuples( cls: type[IntervalArrayT], data, - inclusive=None, + inclusive: IntervalInclusiveType | None = None, copy: bool = False, dtype: Dtype | None = None, ) -> IntervalArrayT: @@ -1360,7 +1360,7 @@ def overlaps(self, other): # --------------------------------------------------------------------- @property - def inclusive(self) -> IntervalClosedType: + def inclusive(self) -> IntervalInclusiveType: """ Whether the intervals are closed on the left-side, right-side, both or neither. @@ -1368,7 +1368,7 @@ def inclusive(self) -> IntervalClosedType: return self.dtype.inclusive @property - def closed(self) -> IntervalClosedType: + def closed(self) -> IntervalInclusiveType: """ Whether the intervals are closed on the left-side, right-side, both or neither. @@ -1422,7 +1422,7 @@ def closed(self) -> IntervalClosedType: ) @deprecate_kwarg(old_arg_name="closed", new_arg_name="inclusive") def set_closed( - self: IntervalArrayT, inclusive: IntervalClosedType + self: IntervalArrayT, inclusive: IntervalInclusiveType ) -> IntervalArrayT: if inclusive not in VALID_CLOSED: msg = f"invalid option for 'inclusive': {inclusive}" diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index 20fecbb0095c5..e6854d035d359 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -20,6 +20,7 @@ missing as libmissing, ) from pandas._libs.interval import ( + VALID_CLOSED, Interval, _warning_interval, ) @@ -37,6 +38,7 @@ from pandas._typing import ( Dtype, DtypeObj, + IntervalInclusiveType, Ordered, npt, type_t, @@ -1088,7 +1090,7 @@ class IntervalDtype(PandasExtensionDtype): def __new__( cls, subtype=None, - inclusive: str_type | None = None, + inclusive: IntervalInclusiveType | None = None, closed: None | lib.NoDefault = lib.no_default, ): from pandas.core.dtypes.common import ( @@ -1098,12 +1100,7 @@ def __new__( inclusive, closed = _warning_interval(inclusive, closed) - if inclusive is not None and inclusive not in { - "right", - "left", - "both", - "neither", - }: + if inclusive is not None and inclusive not in VALID_CLOSED: raise ValueError( "inclusive must be one of 'right', 'left', 'both', 'neither'" ) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index ba3474a2513fb..09c9a8a1da186 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -48,7 +48,7 @@ IgnoreRaise, IndexKeyFunc, IndexLabel, - IntervalClosedType, + IntervalInclusiveType, JSONSerializable, Level, Manager, @@ -8066,7 +8066,7 @@ def between_time( end_time, include_start: bool_t | lib.NoDefault = lib.no_default, include_end: bool_t | lib.NoDefault = lib.no_default, - inclusive: IntervalClosedType | None = None, + inclusive: IntervalInclusiveType | None = None, axis=None, ) -> NDFrameT: """ @@ -8172,7 +8172,7 @@ def between_time( left = True if include_start is lib.no_default else include_start right = True if include_end is lib.no_default else include_end - inc_dict: dict[tuple[bool_t, bool_t], IntervalClosedType] = { + inc_dict: dict[tuple[bool_t, bool_t], IntervalInclusiveType] = { (True, True): "both", (True, False): "left", (False, True): "right", diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 18c0d56abbeb4..6aa2ff91ba933 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -35,7 +35,7 @@ from pandas._typing import ( Dtype, DtypeObj, - IntervalClosedType, + IntervalInclusiveType, IntervalLeftRight, npt, ) @@ -920,7 +920,7 @@ def date_range( normalize: bool = False, name: Hashable = None, closed: Literal["left", "right"] | None | lib.NoDefault = lib.no_default, - inclusive: IntervalClosedType | None = None, + inclusive: IntervalInclusiveType | None = None, **kwargs, ) -> DatetimeIndex: """ @@ -1126,7 +1126,7 @@ def bdate_range( weekmask=None, holidays=None, closed: IntervalLeftRight | lib.NoDefault | None = lib.no_default, - inclusive: IntervalClosedType | None = None, + inclusive: IntervalInclusiveType | None = None, **kwargs, ) -> DatetimeIndex: """ diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 5f48be921f7c6..ea8c50df53426 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -30,7 +30,7 @@ from pandas._typing import ( Dtype, DtypeObj, - IntervalClosedType, + IntervalInclusiveType, npt, ) from pandas.errors import InvalidIndexError @@ -198,7 +198,7 @@ class IntervalIndex(ExtensionIndex): _typ = "intervalindex" # annotate properties pinned via inherit_names - inclusive: IntervalClosedType + inclusive: IntervalInclusiveType is_non_overlapping_monotonic: bool closed_left: bool closed_right: bool @@ -217,7 +217,7 @@ class IntervalIndex(ExtensionIndex): def __new__( cls, data, - inclusive=None, + inclusive: IntervalInclusiveType | None = None, dtype: Dtype | None = None, copy: bool = False, name: Hashable = None, @@ -266,7 +266,7 @@ def closed(self): def from_breaks( cls, breaks, - inclusive=None, + inclusive: IntervalInclusiveType | None = None, name: Hashable = None, copy: bool = False, dtype: Dtype | None = None, @@ -302,7 +302,7 @@ def from_arrays( cls, left, right, - inclusive=None, + inclusive: IntervalInclusiveType | None = None, name: Hashable = None, copy: bool = False, dtype: Dtype | None = None, @@ -337,7 +337,7 @@ def from_arrays( def from_tuples( cls, data, - inclusive=None, + inclusive: IntervalInclusiveType | None = None, name: Hashable = None, copy: bool = False, dtype: Dtype | None = None, @@ -989,7 +989,7 @@ def interval_range( periods=None, freq=None, name: Hashable = None, - inclusive: IntervalClosedType | None = None, + inclusive: IntervalInclusiveType | None = None, ) -> IntervalIndex: """ Return a fixed frequency IntervalIndex. diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 10b607da45ca8..0461fbfc6faa8 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -25,6 +25,7 @@ Axis, FilePath, IndexLabel, + IntervalInclusiveType, Level, QuantileInterpolation, Scalar, @@ -3479,7 +3480,7 @@ def highlight_between( axis: Axis | None = 0, left: Scalar | Sequence | None = None, right: Scalar | Sequence | None = None, - inclusive: str = "both", + inclusive: IntervalInclusiveType = "both", props: str | None = None, ) -> Styler: """ @@ -3584,7 +3585,7 @@ def highlight_quantile( q_left: float = 0.0, q_right: float = 1.0, interpolation: QuantileInterpolation = "linear", - inclusive: str = "both", + inclusive: IntervalInclusiveType = "both", props: str | None = None, ) -> Styler: """ @@ -3969,7 +3970,7 @@ def _highlight_between( props: str, left: Scalar | Sequence | np.ndarray | NDFrame | None = None, right: Scalar | Sequence | np.ndarray | NDFrame | None = None, - inclusive: bool | str = True, + inclusive: bool | IntervalInclusiveType = True, ) -> np.ndarray: """ Return an array of css props based on condition of data values within given range. diff --git a/pandas/util/_validators.py b/pandas/util/_validators.py index caa191dc78493..3676e6eb0091e 100644 --- a/pandas/util/_validators.py +++ b/pandas/util/_validators.py @@ -14,6 +14,7 @@ import numpy as np +from pandas._typing import IntervalInclusiveType from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.common import ( @@ -487,7 +488,7 @@ def validate_endpoints(closed: str | None) -> tuple[bool, bool]: return left_closed, right_closed -def validate_inclusive(inclusive: str | None) -> tuple[bool, bool]: +def validate_inclusive(inclusive: IntervalInclusiveType | None) -> tuple[bool, bool]: """ Check that the `inclusive` argument is among {"both", "neither", "left", "right"}. From af85fc21f4020d873674e10f0d78f6c6bbf825d3 Mon Sep 17 00:00:00 2001 From: patrick Date: Thu, 7 Jul 2022 21:12:57 +0200 Subject: [PATCH 2/4] Fix comparison --- pandas/core/dtypes/dtypes.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index e6854d035d359..4e687e564a0ca 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -20,7 +20,6 @@ missing as libmissing, ) from pandas._libs.interval import ( - VALID_CLOSED, Interval, _warning_interval, ) @@ -1100,7 +1099,12 @@ def __new__( inclusive, closed = _warning_interval(inclusive, closed) - if inclusive is not None and inclusive not in VALID_CLOSED: + if inclusive is not None and inclusive not in { + "right", + "left", + "both", + "neither", + }: raise ValueError( "inclusive must be one of 'right', 'left', 'both', 'neither'" ) From 4f175ed1007ef30750b411d551a60b8c0a12c078 Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Fri, 8 Jul 2022 22:37:40 +0200 Subject: [PATCH 3/4] Fix typing issues --- pandas/_libs/interval.pyi | 2 +- pandas/core/arrays/arrow/_arrow_utils.py | 4 ++-- pandas/core/dtypes/dtypes.py | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pandas/_libs/interval.pyi b/pandas/_libs/interval.pyi index a89ca9d5f74b1..ba0a339fa93dd 100644 --- a/pandas/_libs/interval.pyi +++ b/pandas/_libs/interval.pyi @@ -151,7 +151,7 @@ class Interval(IntervalMixin, Generic[_OrderableT]): def intervals_to_interval_bounds( intervals: np.ndarray, validate_closed: bool = ... -) -> tuple[np.ndarray, np.ndarray, str]: ... +) -> tuple[np.ndarray, np.ndarray, IntervalInclusiveType]: ... class IntervalTree(IntervalMixin): def __init__( diff --git a/pandas/core/arrays/arrow/_arrow_utils.py b/pandas/core/arrays/arrow/_arrow_utils.py index 969858bb4454f..da3003e343c01 100644 --- a/pandas/core/arrays/arrow/_arrow_utils.py +++ b/pandas/core/arrays/arrow/_arrow_utils.py @@ -125,11 +125,11 @@ def subtype(self): return self._subtype @property - def inclusive(self) -> str: + def inclusive(self) -> IntervalInclusiveType: return self._closed @property - def closed(self): + def closed(self) -> IntervalInclusiveType: warnings.warn( "Attribute `closed` is deprecated in favor of `inclusive`.", FutureWarning, diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index c5bc46a504848..78096d836f5b0 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -1141,7 +1141,11 @@ def __new__( "'inclusive' keyword does not match value " "specified in dtype string" ) - inclusive = gd["inclusive"] + # Incompatible types in assignment (expression has type + # "Union[str, Any]", variable has type + # "Optional[Union[Literal['left', 'right'], + # Literal['both', 'neither']]]") + inclusive = gd["inclusive"] # type: ignore[assignment] try: subtype = pandas_dtype(subtype) From 311e796dc6388a0a385a970837f844de3ed6f15d Mon Sep 17 00:00:00 2001 From: Patrick Hoefler Date: Fri, 8 Jul 2022 23:18:14 +0200 Subject: [PATCH 4/4] Try fixing pyright --- pandas/core/arrays/arrow/_arrow_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/arrays/arrow/_arrow_utils.py b/pandas/core/arrays/arrow/_arrow_utils.py index da3003e343c01..5ed10661e8983 100644 --- a/pandas/core/arrays/arrow/_arrow_utils.py +++ b/pandas/core/arrays/arrow/_arrow_utils.py @@ -112,7 +112,7 @@ def __init__(self, subtype, inclusive: IntervalInclusiveType) -> None: # attributes need to be set first before calling # super init (as that calls serialize) assert inclusive in VALID_CLOSED - self._closed = inclusive + self._closed: IntervalInclusiveType = inclusive if not isinstance(subtype, pyarrow.DataType): subtype = pyarrow.type_for_alias(str(subtype)) self._subtype = subtype