diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 172d44555b94..a8542b58615a 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4774,7 +4774,12 @@ def nonliteral_tuple_index_helper(self, left_type: TupleType, index: Expression) self.check_method_call_by_name("__getitem__", left_type, [index], [ARG_POS], context=index) # We could return the return type from above, but unions are often better than the join union = self.union_tuple_fallback_item(left_type) - if isinstance(index, SliceExpr): + # A slice always yields a tuple, whether written as slice syntax (a SliceExpr) or + # passed as a value of type slice (e.g. a variable), which isn't a SliceExpr. + index_type = get_proper_type(self.chk.lookup_type_or_none(index)) + if isinstance(index, SliceExpr) or ( + isinstance(index_type, Instance) and index_type.type.fullname == "builtins.slice" + ): return self.chk.named_generic_type("builtins.tuple", [union]) return union diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index bfbd2e631f5d..6b366e10daa7 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1447,6 +1447,15 @@ reveal_type(t[x:]) # N: Revealed type is "builtins.tuple[builtins.int | builtin t[y:] # E: Invalid index type "slice[str, None, None]" for "tuple[int, str]"; expected type "slice[int | None]" [builtins fixtures/tuple.pyi] +[case testTupleIndexBySliceVariable] +# https://github.com/python/mypy/issues/21708 +s: slice +t = (0, "") +reveal_type(t[s]) # N: Revealed type is "builtins.tuple[builtins.int | builtins.str, ...]" +u = (0, 0) +reveal_type(u[s]) # N: Revealed type is "builtins.tuple[builtins.int, ...]" +[builtins fixtures/tuple.pyi] + [case testTupleSliceStepZeroNoCrash] # This was crashing: https://github.com/python/mypy/issues/18062 # TODO: emit better error when 0 is used for step