-
Notifications
You must be signed in to change notification settings - Fork 258
ParamSpec: Allow only P.args
#1000
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
Perhaps this could require having |
I believe PEP 646 would allow this: https://www.python.org/dev/peps/pep-0646/#type-variable-tuples-with-callable. |
@JelleZijlstra is correct. This is supported by PEP 646. Ts = TypeVarTuple("Ts")
def call_soon(self, callback: Callable[[*Ts], Any], *args: *Ts) -> Handle:
... |
Thanks for the responses! This certainly looks promising. I do wonder though why it wouldn't work with ParamSpec, too. The PEP says it's forbidden, but is there an actual limitation? I would have assumed that only specifying one would just set an additional constraint for the other to be empty. E.g. if only There is also the question about P = ParamSpec("P")
R = TypeVar("R")
def decorator(
func: Callable[P, R]
) -> Callable[P, R]:
def wrapper(**kwargs: P.kwargs) -> R:
return func(**kwargs)
return wrapper |
I have a similar use case with Ariadne middleware functions, but with Ideally, I would like to type a middleware function as follows: _P = ParamSpec("_P")
_R = TypeVar("_R")
_T = TypeVar("_T")
def foo_middleware(
resolver: Callable[Concatenate[_T, GraphQLResolveInfo, _P], _R],
obj: _T,
info: GraphQLResolveInfo,
/,
**kwargs: _P.kwargs,
) -> _R:
...
return resolver(obj, info, **kwargs) But currently this isn't possible, because |
Closing as PEP 646 provides support for @cdce8p's use case. |
Unfortunately, it only fully works for from typing import Callable, TypeVarTuple
Ts = TypeVarTuple("Ts")
def f(a: int) -> None: ...
def g(a: int, b: str = "") -> None: ...
def call_soon(func: Callable[[*Ts], None], *args: *Ts) -> None:
return func(*args)
call_soon(f, 1)
call_soon(g, 1) # pyright error
call_soon(g, 1, "Hello") Whereas mypy can decide if
@erictraut mentioned in microsoft/pyright#3775 that this was be design. If so, @AlexWaygood mentioned a few other examples for TypeVarTuple in the typeshed tracking issue which wouldn't work then: python/typeshed#8708 (comment) -- |
I think it would be unfortunate if this interpretation of the spec meant we had to stick with the (significantly more unsafe) signatures indefinitely for the many functions in If it is decided that pyright's current interpretation of the spec is correct, I think the outcome will be that there will be essentially nowhere in typeshed that we are able to make use of |
If mypy figured out a way to implement this, I'll take another look at adding support in pyright. I'm not sure how mypy is capturing the value of def g(a: int, b: str = "") -> None: ...
def func(func: Callable[[*Ts], None]) -> tuple[*Ts]: ...
reveal_type(func(g)) # mypy and pyright both reveal "tuple[int, str]" @JelleZijlstra, do you happen to know who implemented this in mypy? Was it Ivan? I could use some advice on how this works. |
Yes, Ivan did most of the PEP 646 work, though Jared Hance worked on it earlier. |
Good news — I figured out how to make this work in pyright without too much hackery. This support will be included in the next release of pyright. It's not clear from a reading of PEP 646 that this case is meant to be supported — and if so, what the intended behavior is. If typeshed is going to depend on this behavior, we should work to mandate this behavior in the typing spec and include it in the typing conformance test suite. I've added this to a long list that I've been compiling. |
Thanks Eric! Yes, agreed, let's formalise this in the spec. |
Now that both mypy and pyright support using TypeVarTuples for it, I guess the only thing left would be to eventually formalize this in the typing spec. Would be fine with closing this issue if nobody has objections. https://github.com/microsoft/pyright/releases/tag/1.1.339 -- |
I split the kwargs case into #1524. |
I would like to use
ParamSpec
to type a function that accepts aCallable[P, Any]
and*args
which will be passed to the callable. The function itself does not accept keyword arguments. As an example,AbstractEventLoop.call_soon
behaves similar. This is the current Typeshed definitiontypeshed -> stdlib/asyncio/events.pyi -> AbstractEventLoop
Intuitively adding a ParamSpec variable like this would make sense: "Only except
*args
. If any arguments are passed, they need to match those of thecallback
." I would also expect that ifcallback
has required arguments, they need to be passed with*args
tocall_soon
.The issue here is that PEP 612 explicitly forbids specifying
P.args
orP.kwargs
alone. They always need to be together.https://www.python.org/dev/peps/pep-0612/#id2
I do wonder if this strict limitation makes sense or if there are other ways to work around it so the case described above could be supported. After all it's similar to adding
**kwargs
to the function signature without ever passing keyword arguments.The text was updated successfully, but these errors were encountered: