10000 When specifying a TypeVar to be a Callable, or union of Callables, ParamSpec cannot be used to indicate parameter types · Issue #14777 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

When specifying a TypeVar to be a Callable, or union of Callables, ParamSpec cannot be used to indicate parameter types #14777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

8000

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
AndrewGibb opened this issue Feb 24, 2023 · 3 comments
Labels
bug mypy got something wrong topic-paramspec PEP 612, ParamSpec, Concatenate topic-usability

Comments

@AndrewGibb
Copy link

Bug Report

The declarations of F, G and H all result in the following error:
The first argument to Callable must be a list of types, parameter specification, or "..." [valid-type]
Despite the first argument to the callable being a parameter specification.

from typing import Callable, ParamSpec, TypeVar, Coroutine, Any, Union

P = ParamSpec('P')
F = TypeVar('F', bound=Callable[P, Any])
G = TypeVar('G', bound=Callable[P, Coroutine[Any, Any, Any]])
H = TypeVar('H', bound=Union[Callable[P, Any], Callable[P, Coroutine[Any, Any, Any]]])

To Reproduce
https://mypy-play.net/?mypy=latest&python=3.11&gist=191e47b4afd74e307a3042dfedac77d4

Expected Behavior

Either these should pass type checking, or the error message should be updated to indicate what the problem actually is.

Your Environment

  • Mypy version used: 1.0.0
  • Python version used: 3.11
@AndrewGibb AndrewGibb added the bug mypy got something wrong label Feb 24, 2023
@erictraut
Copy link

The bounds of a TypeVar are not allowed to be generic, so they cannot include TypeVars, TypeVarTuples, or ParamSpecs. Mypy's error message could perhaps be clearer in this case.

@AlexWaygood AlexWaygood added topic-usability topic-paramspec PEP 612, ParamSpec, Concatenate labels Feb 24, 2023
@AndrewGibb
Copy link
Author
AndrewGibb 8000 commented Feb 27, 2023

The motivating problem behind the bug report looks like this:

from typing import Callable, TypeVar, Coroutine, Any, Union, cast, ParamSpec
import time
import asyncio
import functools

P = ParamSpec('P')
R = TypeVar('R')
F = TypeVar('F', bound=Union[Callable[P, R], Callable[P, Coroutine[Any, Any, R]]])

def retry_forever() -> Callable[[F], F]:
  def __decorator(f: F) -> F:
    if asyncio.iscoroutinefunction(f):
      @functools.wraps(f)
      async def __inner(*args: P.args, **kwargs: P.kwargs) -> R:
        while True:
          try:
            return await f(*args, **kwargs)
          except Exception:
            await asyncio.sleep(1)
      return __inner
    else:
      @functools.wraps(f)
      def __inner(*args: P.args, **kwargs: P.kwargs) -> R:
        while True:
          try:
            return f(*args, **kwargs)
          except Exception:
            time.sleep(1)
      return __inner
  return __decorator

Obviously this is a trivial example, but the structure is real and is taken from our codebase. This Bug report is notthe only problem MyPy has with this code at the moment. But I hope you'd agree this is more specific (and useful, to a sufficiently powerful static analyser) than omitting P (and R) as one must do at the moment.

hauntsaninja pushed a commit that referenced this issue Jan 2, 2025
Fixes #14832, fixes #13966, fixes #14622.

Still does not report error in #14777, I'll work separately on that.

Move all `ParamSpec` validity checking to `typeanal.py`. Stop treating
`P.args` and `P.kwargs` as binding - only bare typevar makes it
available in scope. Reject keyword arguments following `P.args`.

This also makes one more conformance test pass.
@sterliakov
Copy link
Collaborator

This is still wrong - there is no way to "carry on" some type variables bound to another TypeVar. HKTs do not exist in python (yet?). This F definition is explicitly prohibited by typing spec. Furthermore, F -> F signature is not strictly correct: I could retry_forever an instance of some callable class but receive a function instead, so it does not preserve input type.

Yes, typing a decorator that supports both sync and async interfaces is challenging (impossible to achieve without ignore/cast AFAIC). You can't unwrap Awaitable[R] when in union, and asyncio.iscoroutinefunction is not even a TypeIs. The most readable (IMO) is a simple solution with two casts both baked by manual verification that you didn't swap sync and async branches:

import time
import asyncio
import functools
from typing import Any, Awaitable, Callable, ParamSpec, Protocol, TypeVar, cast

P = ParamSpec('P')
R = TypeVar('R')

class Retrier(Protocol):
    def __call__(self, fn: Callable[P, R], /) -> Callable[P, R]: ...
    
def retry_forever() -> Retrier:
    
    def __decorator(f: Callable[P, R]) -> Callable[P, R]:
        if asyncio.iscoroutinefunction(f):
            
            @functools.wraps(f)
            async def __ainner(*args: P.args, **kwargs: P.kwargs) -> R:
                while True:
                    try:
                        return await cast("Callable[P, Awaitable[R]]", f)(*args, **kwargs)
                    except Exception:
                        await asyncio.sleep(1)
                        
            return cast("Callable[P, R]", __ainner)
        else:
            
            @functools.wraps(f)
            def __inner(*args: P.args, **kwargs: P.kwargs) -> R:
                while True:
                    try:
                        return f(*args, **kwargs)
                    except Exception:
                        time.sleep(1)
                        
            return __inner
            
    return __decorator

But this isn't a mypy bug by any means.

@sterliakov sterliakov closed this as not planned Won't fix, can't repro, duplicate, stale Mar 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-paramspec PEP 612, ParamSpec, Concatenate topic-usability
Projects
None yet
Development

No branches or pull requests

4 participants
0