diff --git a/stdlib/@tests/test_cases/builtins/check_slice.py b/stdlib/@tests/test_cases/builtins/check_slice.py index 596d8c63c1c5..7f26d4eaeead 100644 --- a/stdlib/@tests/test_cases/builtins/check_slice.py +++ b/stdlib/@tests/test_cases/builtins/check_slice.py @@ -1,54 +1,22 @@ -""" -Assuming X, Y and Z are types other than None, the following rules apply to the slice type: - -- The type hint `slice` should be compatible with all slices, including: - - `slice(None)`, `slice(None, None)` and `slice(None, None, None)`. (⟿ `slice[?, ?, ?]`) -- The type hint `slice[T]` should be compatible with: - - `slice(None)`, `slice(None, None)` and `slice(None, None, None)` (⟿ `slice[?, ?, ?]`) - - `slice(t)`, `slice(None, t)` and `slice(None, t, None)`. (⟿ `slice[?, T, ?]`) - - `slice(t, None)` and `slice(t, None, None)`. (⟿ `slice[T, ?, ?]`) - - `slice(t, t)` and `slice(t, t, None)`. (⟿ `slice[T, T, ?]`) -- The type hint `slice[X, Y]` should be compatible with: - - `slice(None)`, `slice(None, None)` and `slice(None, None, None)` (⟿ `slice[?, ?, ?]`) - - `slice(y)`, `slice(None, y)` and `slice(None, y, None)`. (⟿ `slice[?, Y, ?]`) - - `slice(x, None)` and `slice(x, None, None)` (⟿ `slice[X, ?, ?]`) - - `slice(x, y)` and `slice(x, y, None)`. (⟿ `slice[X, Y, ?]`) -- The type hint `slice[X, Y, Z]` should be compatible with: - - `slice(None)`, `slice(None, None)` and `slice(None, None, None)`. (⟿ `slice[?, ?, ?]`) - - `slice(y)`, `slice(None, y)` and `slice(None, y, None)`. (⟿ `slice[?, Y, ?]`) - - `slice(x, None)` and `slice(x, None, None)` (⟿ `slice[X, ?, ?]`) - - `slice(x, y)` and `slice(x, y, None)`. (⟿ `slice[X, Y, ?]`) - - `slice(None, None, z)` (⟿ `slice[?, ?, Z]`) - - `slice(None, y, z)` (⟿ `slice[?, Y, Z]`) - - `slice(x, None, z)` (⟿ `slice[X, ?, Z]`) - - `slice(x, y, z)` (⟿ `slice[X, Y, Z]`) - -Consistency criterion: Assuming now X, Y, Z can potentially be None, the following rules apply: - -- `slice(x)` must be compatible with `slice[None, X, None]`, even if X is None. -- `slice(x, y)` must be compatible with `slice[X,Y,None]`, even if X is None or Y is None. -- `slice(x, y, z)` must be compatible with `slice[X, Y, Z]`, even if X, Y, or Z are `None`. -""" - from __future__ import annotations from datetime import date, datetime as DT, timedelta as TD from typing import Any, SupportsIndex, cast -from typing_extensions import assert_type +from typing_extensions import assert_never, assert_type # region Tests for slice constructor overloads ----------------------------------------- -assert_type(slice(None), "slice[Any, Any, Any]") -assert_type(slice(1234), "slice[Any, int, Any]") - -assert_type(slice(None, None), "slice[Any, Any, Any]") -assert_type(slice(None, 5678), "slice[Any, int, Any]") -assert_type(slice(1234, None), "slice[int, Any, Any]") -assert_type(slice(1234, 5678), "slice[int, int, Any]") - -assert_type(slice(None, None, None), "slice[Any, Any, Any]") -assert_type(slice(None, 5678, None), "slice[Any, int, Any]") -assert_type(slice(1234, None, None), "slice[int, Any, Any]") -assert_type(slice(1234, 5678, None), "slice[int, int, Any]") +assert_type(slice(None), "slice[None, None, None]") +assert_type(slice(1234), "slice[None, int, None]") + +assert_type(slice(None, None), "slice[None, None, None]") +assert_type(slice(None, 5678), "slice[None, int, None]") +assert_type(slice(1234, None), "slice[int, None, None]") +assert_type(slice(1234, 5678), "slice[int, int, None]") + +assert_type(slice(None, None, None), "slice[None, None, None]") +assert_type(slice(None, 5678, None), "slice[None, int, None]") +assert_type(slice(1234, None, None), "slice[int, None, None]") +assert_type(slice(1234, 5678, None), "slice[int, int, None]") assert_type(slice(1234, 5678, 9012), "slice[int, int, int]") # endregion Tests for slice constructor overloads -------------------------------------- @@ -126,39 +94,39 @@ # region Tests for slice[T] assignments ------------------------------------------------ -sXNX: "slice[int]" = slice(None) -sXIX: "slice[int]" = slice(1234) - -sNNX: "slice[int]" = slice(None, None) -sNIX: "slice[int]" = slice(None, 5678) -sINX: "slice[int]" = slice(1234, None) -sIIX: "slice[int]" = slice(1234, 5678) - -sNNN: "slice[int]" = slice(None, None, None) -sNIN: "slice[int]" = slice(None, 5678, None) -sNNS: "slice[int]" = slice(None, None, 9012) -sINN: "slice[int]" = slice(1234, None, None) -sINS: "slice[int]" = slice(1234, None, 9012) -sIIN: "slice[int]" = slice(1234, 5678, None) -sIIS: "slice[int]" = slice(1234, 5678, 9012) +sXNX: "slice[int | None]" = slice(None) +sXIX: "slice[int | None]" = slice(1234) + +sNNX: "slice[int | None]" = slice(None, None) +sNIX: "slice[int | None]" = slice(None, 5678) +sINX: "slice[int | None]" = slice(1234, None) +sIIX: "slice[int | None]" = slice(1234, 5678) + +sNNN: "slice[int | None]" = slice(None, None, None) +sNIN: "slice[int | None]" = slice(None, 5678, None) +sNNS: "slice[int | None]" = slice(None, None, 9012) +sINN: "slice[int | None]" = slice(1234, None, None) +sINS: "slice[int | None]" = slice(1234, None, 9012) +sIIN: "slice[int | None]" = slice(1234, 5678, None) +sIIS: "slice[int | None]" = slice(1234, 5678, 9012) # endregion Tests for slice[T] assignments --------------------------------------------- # region Tests for slice[X, Y] assignments --------------------------------------------- # Note: start=int is illegal and hence we add an explicit "type: ignore" comment. -tXNX: "slice[None, int]" = slice(None) # since slice(None) is slice[Any, Any, Any] +tXNX: "slice[None, int]" = slice(None) # type: ignore tXIX: "slice[None, int]" = slice(1234) -tNNX: "slice[None, int]" = slice(None, None) +tNNX: "slice[None, int]" = slice(None, None) # type: ignore tNIX: "slice[None, int]" = slice(None, 5678) tINX: "slice[None, int]" = slice(1234, None) # type: ignore tIIX: "slice[None, int]" = slice(1234, 5678) # type: ignore -tNNN: "slice[None, int]" = slice(None, None, None) +tNNN: "slice[None, int]" = slice(None, None, None) # type: ignore tNIN: "slice[None, int]" = slice(None, 5678, None) tINN: "slice[None, int]" = slice(1234, None, None) # type: ignore tIIN: "slice[None, int]" = slice(1234, 5678, None) # type: ignore -tNNS: "slice[None, int]" = slice(None, None, 9012) +tNNS: "slice[None, int]" = slice(None, None, 9012) # type: ignore tINS: "slice[None, int]" = slice(None, 5678, 9012) tNIS: "slice[None, int]" = slice(1234, None, 9012) # type: ignore tIIS: "slice[None, int]" = slice(1234, 5678, 9012) # type: ignore @@ -166,21 +134,21 @@ # region Tests for slice[X, Y, Z] assignments ------------------------------------------ -uXNX: "slice[int, int, int]" = slice(None) -uXIX: "slice[int, int, int]" = slice(1234) - -uNNX: "slice[int, int, int]" = slice(None, None) -uNIX: "slice[int, int, int]" = slice(None, 5678) -uINX: "slice[int, int, int]" = slice(1234, None) -uIIX: "slice[int, int, int]" = slice(1234, 5678) - -uNNN: "slice[int, int, int]" = slice(None, None, None) -uNNI: "slice[int, int, int]" = slice(None, None, 9012) -uNIN: "slice[int, int, int]" = slice(None, 5678, None) -uNII: "slice[int, int, int]" = slice(None, 5678, 9012) -uINN: "slice[int, int, int]" = slice(1234, None, None) -uINI: "slice[int, int, int]" = slice(1234, None, 9012) -uIIN: "slice[int, int, int]" = slice(1234, 5678, None) +uXNX: "slice[None, None, None]" = slice(None) +uXIX: "slice[None, int, None]" = slice(1234) + +uNNX: "slice[None, None, None]" = slice(None, None) +uNIX: "slice[None, int, None]" = slice(None, 5678) +uINX: "slice[int, None, None]" = slice(1234, None) +uIIX: "slice[int, int, None]" = slice(1234, 5678) + +uNNN: "slice[None, None, None]" = slice(None, None, None) +uNNI: "slice[None, None, int]" = slice(None, None, 9012) +uNIN: "slice[None, int, None]" = slice(None, 5678, None) +uNII: "slice[None, int, int]" = slice(None, 5678, 9012) +uINN: "slice[int, None, None]" = slice(1234, None, None) +uINI: "slice[int, None, int]" = slice(1234, None, 9012) +uIIN: "slice[int, int, None]" = slice(1234, 5678, None) uIII: "slice[int, int, int]" = slice(1234, 5678, 9012) # endregion Tests for slice[X, Y, Z] assignments --------------------------------------- @@ -219,33 +187,44 @@ def __getitem__(self, key: "slice[DT, DT, TD | None]") -> Any: ... -# tests slices as an argument -start = DT(1970, 1, 1) -stop = DT(1971, 1, 10) -step = TD(days=1) -# see: https://pandas.pydata.org/docs/user_guide/timeseries.html#partial-string-indexing -# FIXME: https://github.com/python/mypy/issues/2410 (use literal slices) -series = TimeSeries() -_ = series[slice(None, "1970-01-10")] -_ = series[slice("1970-01-01", None)] -_ = series[slice("1970-01-01", "1971-01-10")] -_ = series[slice(None, stop)] -_ = series[slice(start, None)] -_ = series[slice(start, stop)] -_ = series[slice(None)] +def test_datetime_slices() -> None: + # tests slices as an argument + start = DT(1970, 1, 1) + stop = DT(1971, 1, 10) + step = TD(days=1) + # see: https://pandas.pydata.org/docs/user_guide/timeseries.html#partial-string-indexing + # FIXME: https://github.com/python/mypy/issues/2410 (use literal slices) + series = TimeSeries() + _ = series[slice(None, "1970-01-10")] + _ = series[slice("1970-01-01", None)] + _ = series[slice("1970-01-01", "1971-01-10")] + _ = series[slice(None, stop)] + _ = series[slice(start, None)] + _ = series[slice(start, stop)] + _ = series[slice(None)] + + model = TimeSeriesInterpolator() + _ = model[slice(start, stop)] + _ = model[slice(start, stop, step)] + _ = model[slice(start, stop, None)] + + +def tests_return_union_datetime_slices() -> None: + def _(flag: bool, value: DT) -> "slice[DT, None] | slice[None, DT]": + if flag: + return slice(value, None) # slice[DT, DT|Any, Any] incompatible + else: + return slice(None, value) # slice[DT|Any, DT, Any] incompatible -model = TimeSeriesInterpolator() -_ = model[slice(start, stop)] -_ = model[slice(start, stop, step)] -_ = model[slice(start, stop, None)] +# endregion Integration tests for slices with datetimes -------------------------------- -# test slices as a return type -def foo(flag: bool, value: DT) -> "slice[DT, None] | slice[None, DT]": - if flag: - return slice(value, None) # slice[DT, DT|Any, Any] incompatible - else: - return slice(None, value) # slice[DT|Any, DT, Any] incompatible +def test_bad_slice_raises() -> None: + # gh-15526 + def test(s: "slice[int]") -> None: + assert_type(s.start, int) + if s.start is None: # pyright: ignore[reportUnnecessaryComparison] + assert_never(s.start) -# endregion Integration tests for slices with datetimes -------------------------------- + test(slice(42)) # type: ignore diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index 5657ac74a9ac..ac40fbcdecd7 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -1007,28 +1007,10 @@ class slice(Generic[_StartT_co, _StopT_co, _StepT_co]): def step(self) -> _StepT_co: ... @property def stop(self) -> _StopT_co: ... - # Note: __new__ overloads map `None` to `Any`, since users expect slice(x, None) - # to be compatible with slice(None, x). - # generic slice -------------------------------------------------------------------- @overload - def __new__(cls, start: None, stop: None = None, step: None = None, /) -> slice[Any, Any, Any]: ... - # unary overloads ------------------------------------------------------------------ + def __new__(cls, stop: _T2, /) -> slice[None, _T2, None]: ... @overload - def __new__(cls, stop: _T2, /) -> slice[Any, _T2, Any]: ... - # binary overloads ----------------------------------------------------------------- - @overload - def __new__(cls, start: _T1, stop: None, step: None = None, /) -> slice[_T1, Any, Any]: ... - @overload - def __new__(cls, start: None, stop: _T2, step: None = None, /) -> slice[Any, _T2, Any]: ... - @overload - def __new__(cls, start: _T1, stop: _T2, step: None = None, /) -> slice[_T1, _T2, Any]: ... - # ternary overloads ---------------------------------------------------------------- - @overload - def __new__(cls, start: None, stop: None, step: _T3, /) -> slice[Any, Any, _T3]: ... - @overload - def __new__(cls, start: _T1, stop: None, step: _T3, /) -> slice[_T1, Any, _T3]: ... - @overload - def __new__(cls, start: None, stop: _T2, step: _T3, /) -> slice[Any, _T2, _T3]: ... + def __new__(cls, start: _T1, stop: _T2, /) -> slice[_T1, _T2, None]: ... @overload def __new__(cls, start: _T1, stop: _T2, step: _T3, /) -> slice[_T1, _T2, _T3]: ... def __eq__(self, value: object, /) -> bool: ...