Skip to content

Commit 1cd724d

Browse files
committed
Allow slice syntax
This is useful for Annotated, and crucial for downstream libraries like torchtyping.
1 parent 9bd6517 commit 1cd724d

File tree

5 files changed

+79
-86
lines changed

5 files changed

+79
-86
lines changed

mypy/fastparse.py

+26-12
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,6 @@ def ast3_parse(source: Union[str, bytes], filename: str, mode: str,
130130

131131
INVALID_TYPE_IGNORE: Final = 'Invalid "type: ignore" comment'
132132

133-
INVALID_SLICE_ERROR: Final = 'Slice usage in type annotation is invalid'
134-
135133
TYPE_IGNORE_PATTERN: Final = re.compile(r'[^#]*#\s*type:\s*ignore\s*(.*)')
136134

137135

@@ -1554,22 +1552,38 @@ def visit_Bytes(self, n: Bytes) -> Type:
15541552
contents = bytes_to_human_readable_repr(n.s)
15551553
return RawExpressionType(contents, 'builtins.bytes', self.line, column=n.col_offset)
15561554

1555+
if sys.version_info < (3, 9):
1556+
# Supporting `Annotated[int, 1:3]` on Python < 3.9
1557+
def visit_Index(self, n: ast3.Index) -> Type:
1558+
return self.visit(n.value)
1559+
1560+
def visit_Slice(self, n: ast3.Slice) -> Type:
1561+
return self.invalid_type(
1562+
n, note="did you mean to use ',' instead of ':' ?"
1563+
)
1564+
15571565
# Subscript(expr value, slice slice, expr_context ctx) # Python 3.8 and before
15581566
# Subscript(expr value, expr slice, expr_context ctx) # Python 3.9 and later
15591567
def visit_Subscript(self, n: ast3.Subscript) -> Type:
15601568
if sys.version_info >= (3, 9): # Really 3.9a5 or later
15611569
sliceval: Any = n.slice
1562-
if (isinstance(sliceval, ast3.Slice) or
1563-
(isinstance(sliceval, ast3.Tuple) and
1564-
any(isinstance(x, ast3.Slice) for x in sliceval.elts))):
1565-
self.fail(INVALID_SLICE_ERROR, self.line, getattr(n, 'col_offset', -1))
1566-
return AnyType(TypeOfAny.from_error)
1570+
# Python 3.8 or earlier use a different AST structure for subscripts
1571+
elif isinstance(n.slice, ast3.Index):
1572+
sliceval: Any = n.slice.value
1573+
elif isinstance(n.slice, ast3.Slice):
1574+
sliceval = n.slice
1575+
if getattr(sliceval, "col_offset", None is None):
1576+
# Fix column information so that we get Python 3.9+ message order
1577+
sliceval.col_offset = sliceval.lower.col_offset
15671578
else:
1568-
# Python 3.8 or earlier use a different AST structure for subscripts
1569-
if not isinstance(n.slice, Index):
1570-
self.fail(INVALID_SLICE_ERROR, self.line, getattr(n, 'col_offset', -1))
1571-
return AnyType(TypeOfAny.from_error)
1572-
sliceval = n.slice.value
1579+
assert isinstance(n.slice, ast3.ExtSlice)
1580+
for s in n.slice.dims:
1581+
if getattr(s, "col_offset", None is None):
1582+
if isinstance(s, ast3.Index):
1583+
s.col_offset = s.value.col_offset # type: ignore
1584+
elif isinstance(s, ast3.Slice):
1585+
s.col_offset = s.lower.col_offset # type: ignore
1586+
sliceval = ast3.Tuple(n.slice.dims, n.ctx)
15731587

15741588
empty_tuple_index = False
15751589
if isinstance(sliceval, ast3.Tuple):

test-data/unit/check-annotated.test

+17
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,20 @@ class Meta:
126126
x = Annotated[int, Meta()]
127127
reveal_type(x) # N: Revealed type is "def () -> builtins.int"
128128
[builtins fixtures/tuple.pyi]
129+
130+
[case testSliceAnnotated39]
131+
# flags: --python-version 3.9
132+
from typing_extensions import Annotated
133+
134+
a: Annotated[int, 1:2]
135+
reveal_type(a) # N: Revealed type is "builtins.int"
136+
137+
[builtins fixtures/tuple.pyi]
138+
[case testSliceAnnotated38]
139+
# flags: --python-version 3.8
140+
from typing_extensions import Annotated
141+
142+
a: Annotated[int, 1:2]
143+
reveal_type(a) # N: Revealed type is "builtins.int"
144+
145+
[builtins fixtures/tuple.pyi]

test-data/unit/check-errorcodes.test

+36
Original file line numberDiff line numberDiff line change
@@ -897,3 +897,39 @@ lst: List[int] = []
897897
if lst:
898898
pass
899899
[builtins fixtures/list.pyi]
900+
901+
[case testSliceInDict39]
902+
# flags: --python-version 3.9
903+
from typing import Dict
904+
b: Dict[int, x:y]
905+
c: Dict[x:y]
906+
907+
[builtins fixtures/dict.pyi]
908+
[out]
909+
main:3: error: Invalid type comment or annotation [valid-type]
910+
main:3: note: did you mean to use ',' instead of ':' ?
911+
main:4: error: "dict" expects 2 type arguments, but 1 given [type-arg]
912+
main:4: error: Invalid type comment or annotation [valid-type]
913+
main:4: note: did you mean to use ',' instead of ':' ?
914+
915+
[case testSliceInDict38]
916+
# flags: --python-version 3.8
917+
from typing import Dict
918+
b: Dict[int, x:y]
919+
c: Dict[x:y]
920+
921+
[builtins fixtures/dict.pyi]
922+
[out]
923+
main:3: error: Invalid type comment or annotation [valid-type]
924+
main:3: note: did you mean to use ',' instead of ':' ?
925+
main:4: error: "dict" expects 2 type arguments, but 1 given [type-arg]
926+
main:4: error: Invalid type comment or annotation [valid-type]
927+
main:4: note: did you mean to use ',' instead of ':' ?
928+
929+
930+
[case testSliceInCustomTensorType]
931+
# mimics torchtyping.TensorType, at least syntactically
932+
class TensorType: ...
933+
t: TensorType["batch":..., float] # type: ignore
934+
reveal_type(t) # N: Revealed type is "__main__.TensorType"
935+
[builtins fixtures/tuple.pyi]

test-data/unit/check-fastparse.test

-7
Original file line numberDiff line numberDiff line change
@@ -317,13 +317,6 @@ x = None # type: Any
317317
x @ 1
318318
x @= 1
319319

320-
[case testIncorrectTypeCommentIndex]
321-
322-
from typing import Dict
323-
x = None # type: Dict[x: y]
324-
[out]
325-
main:3: error: Slice usage in type annotation is invalid
326-
327320
[case testPrintStatementTrailingCommaFastParser_python2]
328321

329322
print 0,

test-data/unit/parse.test

-67
Original file line numberDiff line numberDiff line change
@@ -949,73 +949,6 @@ main:1: error: invalid syntax
949949
[out version>=3.10]
950950
main:1: error: invalid syntax. Perhaps you forgot a comma?
951951

952-
[case testSliceInAnnotation39]
953-
# flags: --python-version 3.9
954-
a: Annotated[int, 1:2] # E: Slice usage in type annotation is invalid
955-
b: Dict[int, x:y] # E: Slice usage in type annotation is invalid
956-
c: Dict[x:y] # E: Slice usage in type annotation is invalid
957-
[out]
958-
959-
[case testSliceInAnnotation38]
960-
# flags: --python-version 3.8
961-
a: Annotated[int, 1:2] # E: Slice usage in type annotation is invalid
962-
b: Dict[int, x:y] # E: Slice usage in type annotation is invalid
963-
c: Dict[x:y] # E: Slice usage in type annotation is invalid
964-
[out]
965-
966-
[case testSliceInAnnotationTypeComment39]
967-
# flags: --python-version 3.9
968-
a = None # type: Annotated[int, 1:2] # E: Slice usage in type annotation is invalid
969-
b = None # type: Dict[int, x:y] # E: Slice usage in type annotation is invalid
970-
c = None # type: Dict[x:y] # E: Slice usage in type annotation is invalid
971-
[out]
972-
973-
[case testCorrectSlicesInAnnotations39]
974-
# flags: --python-version 3.9
975-
a: Annotated[int, slice(1, 2)]
976-
b: Dict[int, {x:y}]
977-
c: Dict[{x:y}]
978-
[out]
979-
MypyFile:1(
980-
AssignmentStmt:2(
981-
NameExpr(a)
982-
TempNode:2(
983-
Any)
984-
Annotated?[int?, None])
985-
AssignmentStmt:3(
986-
NameExpr(b)
987-
TempNode:3(
988-
Any)
989-
Dict?[int?, None])
990-
AssignmentStmt:4(
991-
NameExpr(c)
992-
TempNode:4(
993-
Any)
994-
Dict?[None]))
995-
996-
[case testCorrectSlicesInAnnotations38]
997-
# flags: --python-version 3.8
998-
a: Annotated[int, slice(1, 2)]
999-
b: Dict[int, {x:y}]
1000-
c: Dict[{x:y}]
1001-
[out]
1002-
MypyFile:1(
1003-
AssignmentStmt:2(
1004-
NameExpr(a)
1005-
TempNode:2(
1006-
Any)
1007-
Annotated?[int?, None])
1008-
AssignmentStmt:3(
1009-
NameExpr(b)
1010-
TempNode:3(
1011-
Any)
1012-
Dict?[int?, None])
1013-
AssignmentStmt:4(
1014-
NameExpr(c)
1015-
TempNode:4(
1016-
Any)
1017-
Dict?[None]))
1018-
1019952
[case testSliceInList39]
1020953
# flags: --python-version 3.9
1021954
x = [1, 2][1:2]

0 commit comments

Comments
 (0)