-
Notifications
You must be signed in to change notification settings - Fork 258
Add PartialApplication #1372
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
Pyright already has very complete support for If we were to replace pyright's custom logic for I agree that there's a general problem with |
Right, it's incomplete. MyPy doesn't keep track of the parameters after application.
Why can't this be done with
I guess I don't see why
Given that Pyright already supports Also, there are custom versions of partial. For example, |
In Python, there are three major flavours of callables in Python. Decorators can return any of these, and there's no way to specify them currently. Instead, type checkers essentially guess that the decorator returns the same flavour that was passed in. Were this proposal accepted, we could annotate these explicitly, and declare what the decorator is doing: from collections.abc import Callable
from typing import Generic
from __future__ import annotations
from collections.abc import Callable
from typing import Any, Generic, ParamSpec, Protocol, TypeVar, overload
P = ParamSpec('P')
U = ParamSpec('U')
R_co = TypeVar('R_co', covariant=True)
class OrdinaryCallable(Generic[P, R_co], Protocol):
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R_co:
...
@overload
def __get__(self, instance: None, owner: Any = None) -> OrdinaryCallable[P, R_co]:
...
@overload
def __get__(self, instance: U, owner: Any = None
) -> Callable[PartialApplication[P, tuple[U]], R_co]: # Bind instance!
...
def __get__(self, instance: Any, owner: Any = None) -> Callable[..., R_co]:
...
class StaticMethod(Generic[P, R_co], Protocol):
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R_co:
...
@overload
def __get__(self, instance: None, owner: Any = None) -> StaticMethod[P, R_co]:
...
@overload
def __get__(self, instance: Any, owner: Any = None
) -> OrdinaryCallable[P, R_co]: # Never bind!
...
def __get__(self, instance: Any, owner: Any = None) -> Callable[..., R_co]:
...
class ClassMethod(Generic[P, R_co], Protocol):
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R_co:
...
@overload
def __get__(self, instance: None, owner: U = None
) -> OrdinaryCallable[PartialApplication[P, tuple[U]], R_co]: # Bind owner!
...
@overload
def __get__(self, instance: U, owner: type[U] = None
) -> OrdinaryCallable[PartialApplication[P, tuple[type[U]]], R_co]: # Bind instance!
...
def __get__(self, instance: Any, owner: Any = None) -> Callable[..., R_co]:
... Then, we could do something like def my_decorator(func: OrdinaryCallable[P, R]) -> OrdinaryCallable[P, R]: Or |
I've been thinking about this a lot recently and I think it is the best approach to solving this problem. PEP 612's lack of a way to solve this problem brings me great displeasure (see #946 for another concrete example of where this'd be useful), but I understand that there's no feasible way to solve this with just Concatenate so I think the addition of a special form this is the best way to allow for this to be expressible. I see this problem come up very regularly with people complaining about functools.cache/their own implementation of a similar concept not persevering parameters and also with partial not being fully expressible in the type system. This would also help with an idea for making FunctionType and MethodType types subscriptable to allow for more specific types that just Callable for some things (see #1410). |
Pitch
Add
typing.PartialApplication
to facilitate the implementation of:__get__
, andfunctools.partial
,both of which are practically impossible to natively (without plugins) annotate.
The
__get__
method is currently handled internally by type checkers, and a MyPy plugin forpartial
has proved to be very difficult.Proposal
I created a discussion, but I wanted to flesh this out as a new feature:
The idea is that
PartialApplication
takes three parameters:ParamSpec
parameterP
,T
, andD
(defaulting to an empty dictionary).It returns a new
ParamSpec
with all the arguments ofP
after removinglen(T)
positional parameters,It verifies that this removed parameters are all supertypes of the corresponding arguments, or else returns a type error.
Partial case study
An example with
partial
(might need some tweaks)Thus, calling
partial(f, ...)
would check the parameters, and produce a__call__
method with the right signature.JIT example
Consider trying to create a decorator
jit
that works with both bare functions and methods. The problem is that in the method case, it has to respond to__get__
and strip off the first argument. It seems that we can only do this withConcatenate
:We can't seem to deal with the method case alongside the function case. Here's the proposed solution:
The text was updated successfully, but these errors were encountered: