Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 83 additions & 104 deletions stdlib/@tests/test_cases/builtins/check_slice.py
Original file line number Diff line number Diff line change
@@ -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 --------------------------------------

Expand Down Expand Up @@ -126,61 +94,61 @@


# 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
# endregion Tests for slice[X, Y] assignments ------------------------------------------


# 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 ---------------------------------------

Expand Down Expand Up @@ -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
22 changes: 2 additions & 20 deletions stdlib/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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: ...
Expand Down
Loading