From 2c04b8e83a123eeb23204a6af0b8718abf20c9f5 Mon Sep 17 00:00:00 2001 From: Jared Hance Date: Thu, 8 Oct 2015 13:53:58 -0400 Subject: [PATCH 1/2] Add support for tuple slices. Apparently python happily accepts any slice, even if its out of range (and just gives an empty tuple). Personally, I find that behavior weird, but I opted to not check any ranges because python itself doesn't care. --- mypy/checkexpr.py | 17 +++++++++++++++++ mypy/test/data/check-tuples.test | 8 ++++++++ 2 files changed, 25 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index cc563cf562af..ccef1ebe3f4c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -986,6 +986,9 @@ def visit_index_expr_helper(self, e: IndexExpr) -> Type: # Special case for tuples. They support indexing only by integer # literals. (Except in weak type checking mode.) index = e.index + if isinstance(index, SliceExpr): + return self.visit_tuple_slice_helper(left_type, index) + ok = False if isinstance(index, IntExpr): n = index.value @@ -1010,6 +1013,20 @@ def visit_index_expr_helper(self, e: IndexExpr) -> Type: e.method_type = method_type return result + def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr): + begin = 0 + end = len(left_type.items) + stride = 1 + if slic.begin_index: + begin = slic.begin_index.value + if slic.end_index: + end = slic.end_index.value + if slic.stride: + stride = slic.stride.value + + return TupleType(left_type.items[begin:end:stride], left_type.fallback, + left_type.line, left_type.implicit) + def visit_cast_expr(self, expr: CastExpr) -> Type: """Type check a cast expression.""" source_type = self.accept(expr.expr, context=AnyType()) diff --git a/mypy/test/data/check-tuples.test b/mypy/test/data/check-tuples.test index 07586cfb4916..ba2426fc9e0e 100644 --- a/mypy/test/data/check-tuples.test +++ b/mypy/test/data/check-tuples.test @@ -152,7 +152,10 @@ def f() -> None: pass from typing import Tuple t1 = None # type: Tuple[A, B] t2 = None # type: Tuple[A] +t3 = None # type: Tuple[A, B, C, D, E] a, b = None, None # type: (A, B) +x = None # type: Tuple[A, B, C] +y = None # type: Tuple[A, C, E] n = 0 a = t1[1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -166,9 +169,14 @@ b = t1[(0)] # E: Incompatible types in assignment (expression has type "A", vari a = t1[0] b = t1[1] a = t1[(0)] +x = t3[0:3] # type (A, B, C) +y = t3[0:5:2] # type (A, C, E) class A: pass class B: pass +class C: pass +class D: pass +class E: pass [builtins fixtures/tuple.py] [case testIndexingTuplesWithNegativeIntegers] From 0ed28ea1885a72469d40dc8c98374a4c750909bc Mon Sep 17 00:00:00 2001 From: Jared Hance Date: Thu, 8 Oct 2015 14:07:40 -0400 Subject: [PATCH 2/2] Improve error messages for tuple indices/slices --- mypy/checkexpr.py | 18 +++++++++++++++--- mypy/messages.py | 3 ++- mypy/test/data/check-tuples.test | 3 ++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index ccef1ebe3f4c..235a9f44c2c5 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1018,11 +1018,23 @@ def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr): end = len(left_type.items) stride = 1 if slic.begin_index: - begin = slic.begin_index.value + if isinstance(slic.begin_index, IntExpr): + begin = slic.begin_index.value + else: + self.chk.fail(messages.TUPLE_SLICE_MUST_BE_AN_INT_LITERAL, slic.begin_index) + return AnyType() if slic.end_index: - end = slic.end_index.value + if isinstance(slic.end_index, IntExpr): + end = slic.end_index.value + else: + self.chk.fail(messages.TUPLE_SLICE_MUST_BE_AN_INT_LITERAL, slic.end_index) + return AnyType() if slic.stride: - stride = slic.stride.value + if isinstance(slic.stride, IntExpr): + stride = slic.stride.value + else: + self.chk.fail(messages.TUPLE_SLICE_MUST_BE_AN_INT_LITERAL, slic.stride) + return AnyType() return TupleType(left_type.items[begin:end:stride], left_type.fallback, left_type.line, left_type.implicit) diff --git a/mypy/messages.py b/mypy/messages.py index 40e040393cf8..93b537bca761 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -41,7 +41,8 @@ INIT_MUST_HAVE_NONE_RETURN_TYPE = 'The return type of "__init__" must be None' GETTER_TYPE_INCOMPATIBLE_WITH_SETTER = \ 'Type of getter incompatible with setter' -TUPLE_INDEX_MUST_BE_AN_INT_LITERAL = 'Tuple index must an integer literal' +TUPLE_INDEX_MUST_BE_AN_INT_LITERAL = 'Tuple index must be an integer literal' +TUPLE_SLICE_MUST_BE_AN_INT_LITERAL = 'Tuple slice must be an integer literal' TUPLE_INDEX_OUT_OF_RANGE = 'Tuple index out of range' TYPE_CONSTANT_EXPECTED = 'Type "Constant" or initializer expected' INCOMPATIBLE_PAIR_ITEM_TYPE = 'Incompatible Pair item type' diff --git a/mypy/test/data/check-tuples.test b/mypy/test/data/check-tuples.test index ba2426fc9e0e..d8f580e330da 100644 --- a/mypy/test/data/check-tuples.test +++ b/mypy/test/data/check-tuples.test @@ -163,7 +163,8 @@ b = t1[0] # E: Incompatible types in assignment (expression has type "A", variab t1[2] # E: Tuple index out of range t1[3] # E: Tuple index out of range t2[1] # E: Tuple index out of range -t1[n] # E: Tuple index must an integer literal +t1[n] # E: Tuple index must be an integer literal +t3[n:] # E: Tuple slice must be an integer literal b = t1[(0)] # E: Incompatible types in assignment (expression has type "A", variable has type "B") a = t1[0]