Skip to content

Commit fb6b8bc

Browse files
authored
Allow SupportsIndex in slice expressions (#14738)
Helps with #2410, as suggested by BvB93 in #2410 (comment) PEP 696 will be the real solution here, since it will allow us to make slice generic with few backward compatibility issues
1 parent 3098574 commit fb6b8bc

File tree

5 files changed

+31
-8
lines changed

5 files changed

+31
-8
lines changed

mypy/checkexpr.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4638,7 +4638,11 @@ def _super_arg_types(self, e: SuperExpr) -> Type | tuple[Type, Type]:
46384638
return type_type, instance_type
46394639

46404640
def visit_slice_expr(self, e: SliceExpr) -> Type:
4641-
expected = make_optional_type(self.named_type("builtins.int"))
4641+
try:
4642+
supports_index = self.chk.named_type("typing_extensions.SupportsIndex")
4643+
except KeyError:
4644+
supports_index = self.chk.named_type("builtins.int") # thanks, fixture life
4645+
expected = make_optional_type(supports_index)
46424646
for index in [e.begin_index, e.end_index, e.stride]:
46434647
if index:
46444648
t = self.accept(index)

mypy/message_registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
8282
INCOMPATIBLE_TYPES_IN_CAPTURE: Final = ErrorMessage("Incompatible types in capture pattern")
8383
MUST_HAVE_NONE_RETURN_TYPE: Final = ErrorMessage('The return type of "{}" must be None')
8484
TUPLE_INDEX_OUT_OF_RANGE: Final = ErrorMessage("Tuple index out of range")
85-
INVALID_SLICE_INDEX: Final = ErrorMessage("Slice index must be an integer or None")
85+
INVALID_SLICE_INDEX: Final = ErrorMessage("Slice index must be an integer, SupportsIndex or None")
8686
CANNOT_INFER_LAMBDA_TYPE: Final = ErrorMessage("Cannot infer type of lambda")
8787
CANNOT_ACCESS_INIT: Final = (
8888
'Accessing "__init__" on an instance is unsound, since instance.__init__ could be from'

test-data/unit/check-expressions.test

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,11 +1115,28 @@ o[:] # E: Value of type "object" is not indexable
11151115

11161116
[case testNonIntSliceBounds]
11171117
from typing import Any
1118-
a, o = None, None # type: (Any, object)
1119-
a[o:1] # E: Slice index must be an integer or None
1120-
a[1:o] # E: Slice index must be an integer or None
1121-
a[o:] # E: Slice index must be an integer or None
1122-
a[:o] # E: Slice index must be an integer or None
1118+
a: Any
1119+
o: object
1120+
a[o:1] # E: Slice index must be an integer, SupportsIndex or None
1121+
a[1:o] # E: Slice index must be an integer, SupportsIndex or None
1122+
a[o:] # E: Slice index must be an integer, SupportsIndex or None
1123+
a[:o] # E: Slice index must be an integer, SupportsIndex or None
1124+
[builtins fixtures/slice.pyi]
1125+
1126+
[case testSliceSupportsIndex]
1127+
import typing_extensions
1128+
class Index:
1129+
def __init__(self, value: int) -> None:
1130+
self.value = value
1131+
def __index__(self) -> int:
1132+
return self.value
1133+
1134+
c = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1135+
reveal_type(c[Index(0):Index(5)]) # N: Revealed type is "builtins.list[builtins.int]"
1136+
[file typing_extensions.pyi]
1137+
from typing import Protocol
1138+
class SupportsIndex(Protocol):
1139+
def __index__(self) -> int: ...
11231140
[builtins fixtures/slice.pyi]
11241141

11251142
[case testNoneSliceBounds]

test-data/unit/check-tuples.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,7 @@ t = (0, "")
12481248
x = 0
12491249
y = ""
12501250
reveal_type(t[x:]) # N: Revealed type is "builtins.tuple[Union[builtins.int, builtins.str], ...]"
1251-
t[y:] # E: Slice index must be an integer or None
1251+
t[y:] # E: Slice index must be an integer, SupportsIndex or None
12521252
[builtins fixtures/tuple.pyi]
12531253

12541254
[case testInferTupleTypeFallbackAgainstInstance]

test-data/unit/fixtures/slice.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ class str: pass
1515
class slice: pass
1616
class ellipsis: pass
1717
class dict: pass
18+
class list(Generic[T]):
19+
def __getitem__(self, x: slice) -> list[T]: pass

0 commit comments

Comments
 (0)