-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
ParamSpec
does not consider substitution of @overload
s
#13540
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
Comments
PEP 612 unfortunately didn't contemplate how If this is a problem that's worth addressing (and I'm not convinced it is), then someone will probably need to work out all of the intended behaviors (including edge cases), document everything, and drive consensus among the various type checker maintainers. |
Could you elaborate on what the edge cases are, for the interaction of (I did find you mentioned this interaction in #12292 (comment), but not that there were unresolved issues and edge cases) It seems worthwhile to me to address this, since I hit the bug 😄. But perhaps I don't understand the tradeoffs since I don't understand the edge cases. |
To identify the edge cases, you will probably need to write a prototype implementation and work through a bunch of test cases that include I think the approach will necessarily involve solving for not just one set of type variables (as is done with a normal call expression) but n sets, one for each overload in the argument passed to the call. And then once you've solved all n sets of type variables, you'll need to figure out how to combine the results to get the final return type of the call. If the declared return type is a callable itself that uses the ParamSpec, it will presumably translate into a new synthesized overload. If the declared return type isn't a callable, then you will presumably need to apply a union or join from all of the solved sets of type variables to get a combined answer. It's even worse if the target of the call accepts two callable input parameters that are parameterized by separate ParamSpecs. In that case, if you want to support passing overloaded functions for both parameters, you will need to solve n x m sets of type variables where n is the number of overloads in the first argument and m is the number of overloads in the second. That's just one issue I can think of, but I'm sure there are many more that I haven't. |
I've met this issue while implementing something very similar as the issue description, using generic(lambda: f("test")) and this plays nice because mypy correctly infers the return type of a https://mypy-play.net/?mypy=latest&python=3.10&gist=1f323faa2788a034a665dd4052d1e51a from collections.abc import Callable
from typing import Any, ParamSpec, TypeVar, overload
from typing_extensions import assert_type
@overload
def f(x: int) -> list[int]:
...
@overload
def f(x: str) -> list[str]:
...
def f(x):
return [x]
_P = ParamSpec("_P")
_R = TypeVar("_R")
def generic(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R:
return func(*args, **kwargs)
list_int = generic(lambda: f(1))
assert_type(list_int, list[int])
list_str = generic(lambda: f("test"))
assert_type(list_str, list[str]) |
FWIW, I worked through all of the issues with
Note: Things get very complicated when more than one ParamSpec is involved in the signature because you need to create a constraint solver context for all combinations of ParamSpecs, but this is extremely rare, so it can probably be ignored or treated as an error. If someone is interested in adding this support to mypy, feel free to reuse the unit tests I wrote for pyright. |
Bug Report
It looks like
mypy
only considers the first@overload
of a function, when considering substitutions for aParamSpec
.In particular this affects use of
asyncio.to_thread(f, arg)
, iff
has@overload
s.To Reproduce
Expected Behavior
Actual Behavior
Your Environment
mypy 0.971 (compiled: yes)
mypy t.py
mypy.ini
(and other config files): noneThe text was updated successfully, but these errors were encountered: