typeshed icon indicating copy to clipboard operation
typeshed copied to clipboard

functools.partial should use PEP612 ParamSpec (depends on #8708)

Open benbariteau opened this issue 2 years ago • 8 comments

Currently, functools.partial's __call__ method takes *args: Any, **kwargs: Any and returns _T (a TypeVar for the return type of the callable the partial is wrapping). This is decent, but matching the parameter typing would make this more useful in more situations.

I can sort of see how to deal with this, although I'll admit I'm unsure how to make this generically work. I imagine you could probably deal with some number of Concatenates (maybe up to 5 or 10) to deal with most cases. It's possible fixing this requires another PEP to allow an arbitrary-length Concatenate-type pattern.

Related to https://github.com/python/mypy/issues/1484

benbariteau avatar Sep 07 '22 23:09 benbariteau

You can nearly get there with a combination of PEP 612 and PEP 646:

import sys
from types import GenericAlias
from typing import Any, Callable, Concatenate, Generic, ParamSpec, TypeVar, TypeVarTuple, overload

_P1 = ParamSpec("_P1")
_P2 = ParamSpec("_P2")
_T = TypeVar("_T")
_R_co = TypeVar("_R_co", covariant=True)
_Ts = TypeVarTuple("_Ts")

class partial(Generic[_P1, _P2, _T, _R_co, *_Ts]):
    @overload
    def __new__(cls, __func: Callable[_P1, _R_co]) -> partial[_P1, _P1, Any, _R_co]: ...
    @overload
    def __new__(cls, __func: Callable[Concatenate[*_Ts, _P2], _R_co], *args: *_Ts) -> partial[Concatenate[*_Ts, _P2], _P2, Any, _R_co, *_Ts]: ...
    @overload
    def __new__(cls, __func: Callable[_P1, _R_co], *args: *_Ts, **kwargs: _T) -> partial[_P1, ..., _T, _R_co, *_Ts]: ...
    def __call__(self, *args: _P2.args, **kwargs: _P2.kwargs) -> _R_co: ...
    @property
    def func(self) -> Callable[_P1, _R_co]: ...
    @property
    def args(self) -> tuple[*_Ts]: ...
    @property
    def keywords(self) -> dict[str, _T]: ...
    if sys.version_info >= (3, 9):
        def __class_getitem__(cls, item: Any) -> GenericAlias: ...

Mypy doesn't support PEP 646 yet, however. And even if it did, there would still be some corner cases that don't work (https://github.com/PyCQA/flake8-pyi/pull/150#issuecomment-1023575984)

AlexWaygood avatar Sep 07 '22 23:09 AlexWaygood

Hmmm, I think that's probably the best we're gonna get for now. It's a strict improvement in my opinion.

benbariteau avatar Sep 08 '22 00:09 benbariteau

Cf. #8708

srittau avatar Sep 08 '22 11:09 srittau

#8708 has been closed. Can this be un-deferred yet?

andersk avatar Dec 11 '23 07:12 andersk

#8708 has been closed. Can this be un-deferred yet?

No, pytype still crashes (meaning our CI would fail) if we make a class Generic over a TypeVarTuple, which is what the solution outlined in https://github.com/python/typeshed/issues/8703#issuecomment-1240022635 would require.

AlexWaygood avatar Dec 11 '23 08:12 AlexWaygood

pytype still crashes

Is that the one that has apparently been fixed a couple of days ago: https://github.com/google/pytype/issues/1525#issuecomment-1847874572 Or is there another issue to track?

Dreamsorcerer avatar Dec 11 '23 13:12 Dreamsorcerer

Is that the one that has apparently been fixed a couple of days ago

no

AlexWaygood avatar Dec 11 '23 15:12 AlexWaygood