Repository: python/mypy

Issue:
ParamspecArgs cannot be used with enumerate
**Bug Report**

With https://github.com/python/mypy/issues/12386 an issue was closed, that ParamSpec cannot be used as tuples and dicts. However, enumerate is seen as invalid, even if it works.

**To Reproduce**

```py
from typing_extensions import ParamSpec
from typing import Callable, TypeVar

P = ParamSpec("P")
R = TypeVar("R")

def foo(func: Callable[P, R]) -> Callable[P, R]:
    def _inner(*args: P.args, **kwargs: P.kwargs) -> R:
        x = kwargs.pop("x")
        y = enumerate(args)
        return func(*args, **kwargs)
    return _inner
```

**Expected Behavior**

It works fine

**Actual Behavior**

```
foo.py:10:13: error: Need type annotation for "y"  [var-annotated]
            y = enumerate(args)
                ^
foo.py:10:23: error: Argument 1 to "enumerate" has incompatible type "P.args"; expected "Iterable[<nothing>]"  [arg-type]
            y = enumerate(args)
                          ^
Found 2 errors in 1 file (checked 1 source file)
```

**Your Environment**

- Mypy version used: 0.960
- Python version used: 3.7.2
- Operating system and version: SUSE Linux




Discussion:
This looks like a bug to me, but here's a workaround in the meantime, that [passes mypy](https://mypy-play.net/?mypy=latest&python=3.10&gist=c709f20c6da291fb3e3227d1f665820b):

```python
from typing_extensions import ParamSpec
from typing import Callable, TypeVar

P = ParamSpec("P")
R = TypeVar("R")

def foo(func: Callable[P, R]) -> Callable[P, R]:
    def _inner(*args: P.args, **kwargs: P.kwargs) -> R:
        x = kwargs.pop("x")
        y: "enumerate[object]" = enumerate(args)
        return func(*args, **kwargs)
    return _inner
```
@AlexWaygood thanks!
Here's a repro that doesn't depend on the typeshed stubs for `enumerate`:

```python
from typing_extensions import ParamSpec
from typing import Callable, TypeVar, Iterator, Iterable, Tuple

P = ParamSpec("P")
T = TypeVar("T")

def enumerate(x: Iterable[T]) -> Iterator[Tuple[int, T]]: ...

def foo(func: Callable[P, T]) -> Callable[P, T]:
    def _inner(*args: P.args, **kwargs: P.kwargs) -> T:
        x = kwargs.pop("x")
        y = enumerate(args)  # E: Need type annotation for "y"  # E: Argument 1 to "enumerate" has incompatible type "P.args"; expected "Iterable[<nothing>]"
        return func(*args, **kwargs)
    return _inner
```

Resolving patch:
diff --git a/mypy/constraints.py b/mypy/constraints.py
--- a/mypy/constraints.py
+++ b/mypy/constraints.py
@@ -571,6 +571,8 @@ def visit_instance(self, template: Instance) -> List[Constraint]:
             if not actual.values:
                 return infer_constraints(template, actual.upper_bound, self.direction)
             return []
+        elif isinstance(actual, ParamSpecType):
+            return infer_constraints(template, actual.upper_bound, self.direction)
         else:
             return []
 


Test patch:
diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test
--- a/test-data/unit/check-parameter-specification.test
+++ b/test-data/unit/check-parameter-specification.test
@@ -1066,16 +1066,21 @@ def run_job(job: Job[...]) -> T: ...
 [builtins fixtures/tuple.pyi]
 
 [case testTupleAndDictOperationsOnParamSpecArgsAndKwargs]
-from typing import Callable
+from typing import Callable, Iterator, Iterable, TypeVar, Tuple
 from typing_extensions import ParamSpec
 
 P = ParamSpec('P')
+T = TypeVar('T')
+def enumerate(x: Iterable[T]) -> Iterator[Tuple[int, T]]: ...
 
 def func(callback: Callable[P, str]) -> Callable[P, str]:
     def inner(*args: P.args, **kwargs: P.kwargs) -> str:
         reveal_type(args[5])  # N: Revealed type is "builtins.object"
         for a in args:
             reveal_type(a)  # N: Revealed type is "builtins.object"
+        for idx, a in enumerate(args):
+            reveal_type(idx)  # N: Revealed type is "builtins.int"
+            reveal_type(a)  # N: Revealed type is "builtins.object"
         b = 'foo' in args
         reveal_type(b)  # N: Revealed type is "builtins.bool"
         reveal_type(args.count(42))  # N: Revealed type is "builtins.int"