From 7eb232ac31d95fd2593fd760438aa8f7e9300b05 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 2 May 2026 23:01:48 +0100 Subject: [PATCH 1/2] Rely on typeshed/stubs for slice typing --- mypy/checkexpr.py | 10 +--------- test-data/unit/check-class-namedtuple.test | 2 +- test-data/unit/check-expressions.test | 8 ++++---- test-data/unit/check-narrowing.test | 2 +- test-data/unit/check-tuples.test | 4 ++-- test-data/unit/fixtures/slice.pyi | 10 +++++++--- test-data/unit/fixtures/tuple.pyi | 7 ++++--- 7 files changed, 20 insertions(+), 23 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c9296bd5e875d..123c5f821ed29 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -141,7 +141,6 @@ fix_instance, has_any_from_unimported_type, instantiate_type_alias, - make_optional_type, set_any_tvars, validate_instance, ) @@ -5860,17 +5859,10 @@ def _super_arg_types(self, e: SuperExpr) -> Type | tuple[Type, Type]: return type_type, instance_type def visit_slice_expr(self, e: SliceExpr) -> Type: - try: - supports_index = self.chk.named_type("typing_extensions.SupportsIndex") - except KeyError: - supports_index = self.chk.named_type("builtins.int") # thanks, fixture life - expected = make_optional_type(supports_index) type_args = [] for index in [e.begin_index, e.end_index, e.stride]: if index: - t = self.accept(index) - self.chk.check_subtype(t, expected, index, message_registry.INVALID_SLICE_INDEX) - type_args.append(t) + type_args.append(self.accept(index)) else: type_args.append(NoneType()) return self.chk.named_generic_type("builtins.slice", type_args) diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index c9b5b0f4db1a2..8e7d85635f163 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -538,7 +538,7 @@ class Base(NamedTuple): # E: No overload variant of "__getitem__" of "tuple" matches argument type "TypeVar" \ # N: Possible overload variants: \ # N: def __getitem__(self, int, /) -> int \ - # N: def __getitem__(self, slice, /) -> tuple[int, ...] + # N: def __getitem__(self, slice[int | None], /) -> tuple[int, ...] return self.x def bad_override(self) -> int: return self.x diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index f3ad4bb42a4fb..7123285e5eca1 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1226,10 +1226,10 @@ o[:] # E: Value of type "object" is not indexable from typing import Any a: Any o: object -a[o:1] # E: Slice index must be an integer, SupportsIndex or None -a[1:o] # E: Slice index must be an integer, SupportsIndex or None -a[o:] # E: Slice index must be an integer, SupportsIndex or None -a[:o] # E: Slice index must be an integer, SupportsIndex or None +a[o:1] +a[1:o] +a[o:] +a[:o] [builtins fixtures/slice.pyi] [case testSliceSupportsIndex] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 6ae8a3ae11d8b..7eeaeade8fff0 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -438,7 +438,7 @@ weird_mixture: Union[KeyedTypedDict, KeyedNamedTuple] if weird_mixture["key"] is Key.B: # E: No overload variant of "__getitem__" of "tuple" matches argument type "str" \ # N: Possible overload variants: \ # N: def __getitem__(self, int, /) -> Literal[Key.C] \ - # N: def __getitem__(self, slice, /) -> tuple[Literal[Key.C], ...] + # N: def __getitem__(self, slice[int | None], /) -> tuple[Literal[Key.C], ...] reveal_type(weird_mixture) # N: Revealed type is "TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}) | tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" else: reveal_type(weird_mixture) # N: Revealed type is "TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}) | tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 9653d9d037ce6..bfbd2e631f5d8 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1435,7 +1435,7 @@ reveal_type(t[x]) # N: Revealed type is "builtins.int | builtins.str" t[y] # E: No overload variant of "__getitem__" of "tuple" matches argument type "str" \ # N: Possible overload variants: \ # N: def __getitem__(self, int, /) -> int | str \ - # N: def __getitem__(self, slice, /) -> tuple[int | str, ...] + # N: def __getitem__(self, slice[int | None], /) -> tuple[int | str, ...] [builtins fixtures/tuple.pyi] @@ -1444,7 +1444,7 @@ t = (0, "") x = 0 y = "" reveal_type(t[x:]) # N: Revealed type is "builtins.tuple[builtins.int | builtins.str, ...]" -t[y:] # E: Slice index must be an integer, SupportsIndex or None +t[y:] # E: Invalid index type "slice[str, None, None]" for "tuple[int, str]"; expected type "slice[int | None]" [builtins fixtures/tuple.pyi] [case testTupleSliceStepZeroNoCrash] diff --git a/test-data/unit/fixtures/slice.pyi b/test-data/unit/fixtures/slice.pyi index b22a12b5213f2..78cc24ab3d24b 100644 --- a/test-data/unit/fixtures/slice.pyi +++ b/test-data/unit/fixtures/slice.pyi @@ -1,6 +1,10 @@ # Builtins stub used in slicing test cases. -from typing import Generic, TypeVar +from typing import Generic, TypeVar, Protocol T = TypeVar('T') +_Tco = TypeVar('_Tco', covariant=True) + +class SupportsIndex(Protocol): + def __index__(self) -> int: ... class object: def __init__(self): pass @@ -12,8 +16,8 @@ class function: pass class int: pass class str: pass -class slice: pass +class slice(Generic[_Tco]): pass class ellipsis: pass class dict: pass class list(Generic[T]): - def __getitem__(self, x: slice) -> list[T]: pass + def __getitem__(self, x: slice[SupportsIndex | None]) -> list[T]: pass diff --git a/test-data/unit/fixtures/tuple.pyi b/test-data/unit/fixtures/tuple.pyi index 1e37ada08e521..35ce704ee820a 100644 --- a/test-data/unit/fixtures/tuple.pyi +++ b/test-data/unit/fixtures/tuple.pyi @@ -21,8 +21,9 @@ class tuple(Sequence[_Tco], Generic[_Tco]): def __contains__(self, item: object) -> bool: pass @overload def __getitem__(self, x: int) -> _Tco: pass + # Real stubs use SupportsIndex, but we use int to speed up tests, as this is a common fixture. @overload - def __getitem__(self, x: slice) -> Tuple[_Tco, ...]: ... + def __getitem__(self, x: slice[int | None]) -> Tuple[_Tco, ...]: ... def __mul__(self, n: int) -> Tuple[_Tco, ...]: pass def __rmul__(self, n: int) -> Tuple[_Tco, ...]: pass def __add__(self, x: Tuple[_Tco, ...]) -> Tuple[_Tco, ...]: pass @@ -37,7 +38,7 @@ class int: def __neg__(self) -> 'int': pass def __pos__(self) -> 'int': pass class float: pass -class slice: pass +class slice(Generic[_Tco]): pass class bool(int): pass class str: pass # For convenience class bytes: pass @@ -47,7 +48,7 @@ class list(Sequence[_T], Generic[_T]): @overload def __getitem__(self, i: int) -> _T: ... @overload - def __getitem__(self, s: slice) -> list[_T]: ... + def __getitem__(self, s: slice[int | None]) -> list[_T]: ... def __contains__(self, item: object) -> bool: ... def __iter__(self) -> Iterator[_T]: ... From d72e8f26bd0aeea43ff43d0cdb2d0674c2abffea Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 2 May 2026 23:15:22 +0100 Subject: [PATCH 2/2] Delete unused message --- mypy/message_registry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/message_registry.py b/mypy/message_registry.py index 2027913f952db..30ced27aef22f 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -92,7 +92,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage: TOO_MANY_TARGETS_FOR_VARIADIC_UNPACK: Final = ErrorMessage( "Too many assignment targets for variadic unpack" ) -INVALID_SLICE_INDEX: Final = ErrorMessage("Slice index must be an integer, SupportsIndex or None") CANNOT_INFER_LAMBDA_TYPE: Final = ErrorMessage("Cannot infer type of lambda") CANNOT_ACCESS_INIT: Final = ( 'Accessing "__init__" on an instance is unsound, since instance.__init__ could be from'