Skip to content
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.

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

ParamSpec + Generics doesn't work #14968

Closed
klen opened this issue Mar 28, 2023 · 6 comments · Fixed by #15903
Closed

ParamSpec + Generics doesn't work #14968

klen opened this issue Mar 28, 2023 · 6 comments · Fixed by #15903
Labels
bug mypy got something wrong topic-paramspec PEP 612, ParamSpec, Concatenate

Comments

@klen
Copy link

klen commented Mar 28, 2023

Bug Report

Mypy doesn't process ParamSpec correctly.

To Reproduce

from __future__ import annotations

from collections.abc import Callable
from typing import Any, Generic, ParamSpec, TypeVar

P = ParamSpec("P")
TVFn = TypeVar("TVFn", bound=Callable[..., Any])


class Example(Generic[TVFn]):
    def __init__(self, fn: TVFn):
        self.fn = fn

    def __call__(self: Example[Callable[P, Any]], *args: P.args, **kwargs: P.kwargs):
        ...


def test_fn(a: int, b: str) -> None:
    ...


example = Example(test_fn)
example()  # mypy doesn't show an error for this call
example(1, "b") # mypy show errors for this call

Expected Behavior

example()  # show missing arguments here
example(1, "b")  # pass here

Actual Behavior

Mypy output

paramspec.py:24: error: Argument 1 to "__call__" of "Example" has incompatible type "int"; expected <nothing>  [arg-type]
paramspec.py:24: error: Argument 2 to "__call__" of "Example" has incompatible type "str"; expected <nothing>  [arg-type]
Found 2 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.1.1
  • Mypy command-line flags:
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.11.2

BTW: in pyright this code working fine

@klen klen added the bug mypy got something wrong label Mar 28, 2023
@AlexWaygood AlexWaygood added the topic-paramspec PEP 612, ParamSpec, Concatenate label Mar 28, 2023
@erictraut
Copy link

Your sample doesn't define the symbol Wrapper. I presume that it's a class. Could you add that class definition?

@klen
Copy link
Author

klen commented Mar 28, 2023

@erictraut sorry, there was a small mistype. I've updated the example.

@erictraut
Copy link

erictraut commented Mar 28, 2023

I agree this is a bug in mypy, but you're using ParamSpec in an unusual way here. Mypy works fine when you use it in a more typical manner.

class Example(Generic[P]):
    def __init__(self, fn: Callable[P, Any]):
        self.fn = fn

    def __call__(self, *args: P.args, **kwargs: P.kwargs):
        ...

@klen
Copy link
Author

klen commented Mar 28, 2023

@erictraut Real examples are more complex and I need whole callable object as TypeVar for the Generic. But you gave me some ideas for further research. Thank you! 🙏

@erictraut
Copy link

You probably already figured this out, but if you need the whole callable, you can also capture the return type in the constructor.

P = ParamSpec("P")
R = TypeVar("R")

class Example(Generic[P, R]):
    def __init__(self, fn: Callable[P, R]):
        self.fn = fn

    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
        ...

One situation where this approach won't work (and your original example will) is if the captured function is overloaded.

ilevkivskyi added a commit that referenced this issue Aug 19, 2023
Fixes #14968
Fixes #13911

The fix is simple, as I predicted on Discord, we simply should use
`get_all_type_vars()` instead of `get_type_vars()` (that specifically
returns only `TypeVarType`). I also use this opportunity to tidy-up code
in `bind_self()`, it should be now more readable, and much faster
(especially when compiled with mypyc).

cc @A5rocks

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
@hauntsaninja
Copy link
Collaborator

@klen is the project you were running into this on open source?

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
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants