Description
I would like to use ParamSpec
to type a function that accepts a Callable[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 definition
def call_soon(self, callback: Callable[..., Any], *args: Any) -> Handle: ...
typeshed -> stdlib/asyncio/events.pyi -> AbstractEventLoop
P = ParamSpec("P")
def call_soon(self, callback: Callable[P, Any], *args: P.args) -> Handle: ...
Intuitively adding a ParamSpec variable like this would make sense: "Only except *args
. If any arguments are passed, they need to match those of the callback
." I would also expect that if callback
has required arguments, they need to be passed with *args
to call_soon
.
The issue here is that PEP 612 explicitly forbids specifying P.args
or P.kwargs
alone. They always need to be together.
https://www.python.org/dev/peps/pep-0612/#id2
Furthermore, because the default kind of parameter in Python ((x: int)) may be addressed
both positionally and through its name, two valid invocations of a (*args: P.args, **kwargs: P.kwargs)
function may give different partitions of the same set of parameters. Therefore, we need to make
sure that these special types are only brought into the world together, and are used together,
so that our usage is valid for all possible partitions.
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.
def call_soon(self, callback: Callable[P, Any], *args: P.args, **kwargs: P.kwargs) -> Handle: ...