Repository: python/mypy

Issue:
Tuple slice by variable results in incorrect type
```python
from typing import Tuple

sz = 2
x: Tuple[int, ...] = (1, 2)[:sz]
```

```console
$ mypy --version
mypy 0.711
$ mypy t.py
mt.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "Tuple[int, ...]")
```


Discussion:
this is of course reduced from a larger program to a minimal reproduction
I checked and you can accomplish this with `Literal` and `Final`:
```
from typing import Tuple
from typing_extensions import Final

sz: Final = 2
x: Tuple[int, ...] = (1, 2)[:sz]
```
or if you prefer, a `Literal[2]` annotation should work as well for `sz`.

in reality, sz is not a literal -- it's used in the following way:

```python
ver = tuple(int(p) for p in x.split('.'))
sz = len(ver)
return min_ver[:sz] <= ver <= max_ver[:sz]
```

however this is lost when converting to a minimal testcase

Here's the [current code with the `# type: ignore`](https://github.com/asottile/setup-cfg-fmt/blob/a19c422ae3cb484a3164205709339d7ac0abe1d8/setup_cfg_fmt.py#L251-L257) -- this is definitely a bug and not a question, a slice of a tuple should never return a scalar
Ah, you are correct, sorry I misunderstood the issue. 

E: also it seems the same does not happen for lists, so this is a bug in tuple handling code.
It seems the changes in https://github.com/python/mypy/pull/3514 made the incorrect decision to return the union of elements when given a slice. I'm working on a fix.

Resolving patch:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -2520,7 +2520,11 @@ def nonliteral_tuple_index_helper(self, left_type: TupleType, index: Expression)
                                       'actual type', 'expected type'):
             return AnyType(TypeOfAny.from_error)
         else:
-            return UnionType.make_simplified_union(left_type.items)
+            union = UnionType.make_simplified_union(left_type.items)
+            if isinstance(index, SliceExpr):
+                return self.chk.named_generic_type('builtins.tuple', [union])
+            else:
+                return union
 
     def _get_value(self, index: Expression) -> Optional[int]:
         if isinstance(index, IntExpr):
diff --git a/mypy/types.py b/mypy/types.py
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -84,7 +84,7 @@ class TypeOfAny:
     """
     This class describes different types of Any. Each 'Any' can be of only one type at a time.
     """
-    # Was this Any type was inferred without a type annotation?
+    # Was this Any type inferred without a type annotation?
     unannotated = 1  # type: Final
     # Does this Any come from an explicit type annotation?
     explicit = 2  # type: Final


Test patch:
diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test
--- a/test-data/unit/check-tuples.test
+++ b/test-data/unit/check-tuples.test
@@ -193,7 +193,7 @@ t1[2]     # E: Tuple index out of range
 t1[3]     # E: Tuple index out of range
 t2[1]     # E: Tuple index out of range
 reveal_type(t1[n])     # N: Revealed type is 'Union[__main__.A, __main__.B]'
-reveal_type(t3[n:])    # N: Revealed type is 'Union[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E]'
+reveal_type(t3[n:])    # N: Revealed type is 'builtins.tuple[Union[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E]]'
 if int():
     b = t1[(0)] # E: Incompatible types in assignment (expression has type "A", variable has type "B")
 
@@ -1127,7 +1127,7 @@ t[y]  # E: Invalid tuple index type (actual type "str", expected type "Union[int
 t = (0, "")
 x = 0
 y = ""
-reveal_type(t[x:])  # N: Revealed type is 'Union[builtins.int, builtins.str]'
+reveal_type(t[x:])  # N: Revealed type is 'builtins.tuple[Union[builtins.int, builtins.str]]'
 t[y:]  # E: Slice index must be an integer or None
 [builtins fixtures/tuple.pyi]