Open kcdodd opened 3 months ago
I think I have a similar problem when trying to create class-based decorators with alternate constructors:
from __future__ import annotations
import collections.abc
import typing
P = typing.ParamSpec("P")
T_co = typing.TypeVar("T_co", covariant=True)
class MyDecorator(typing.Generic[P, T_co]):
def __init__(
self,
func: collections.abc.Callable[P, T_co],
*,
option: str | None = None,
) -> None:
self._attribute = option
@classmethod
def construct_with_configuration( # Argument 1 has incompatible type "Callable[[int], int]"; expected "Callable[[VarArg(Never), KwArg(Never)], Never]" [arg-type]
cls,
option: str,
) -> collections.abc.Callable[[collections.abc.Callable[P, T_co]], MyDecorator[P, T_co]]:
def decorator(func: collections.abc.Callable[P, T_co]) -> MyDecorator[P, T_co]:
return cls(func, option=option)
return decorator
@MyDecorator.construct_with_configuration(
option="a",
)
def a_function(a: int) -> int:
return a + 1
typing.reveal_type(a_function) # Revealed type is "MyDecorator[Never, Never]"
I encountered a very similar problem when trying to overload a decorator to support it with kwargs or without. I think it all boils down to mypy resolving T
in a return type Callable[[Callable[..., T]], Callable[..., T]]
to Never
unless there are other indicators for how to resolve T
in any args/kwargs. It would be amazing if mypy could support it.
This describes an issue where a generic decorator that returns a generic sub-type of a "callable" (using
__call__
andParamSpec
) cannot be applied to a generic function. In the example below,Callable[_P, _R] -> Traceable[_P, _R]
works, butCallable[_P, _R] -> Decorated[_P, _R]
does not. It seems to work if either the decorated function is not generic (E.G.radius()
instead ofapply()
in the example), or if the return type of the decorator is a super-type of its argument (E.G.Traceable
instead ofDecorated
).Relevant closed issues
15837
15287
1317
To Reproduce
https://mypy-play.net/?mypy=latest&python=3.12&gist=a8f681e6c14ec013bf3ae56c81fe94b2
Expected Behavior
Expected variables transferred from input generic callable to returned generic callable, even if the return is not a super-type of the input.
Actual Behavior
ParamSpec variables are not used to parameterize the returned generic if it is not a super-type of the input.
Your Environment
python -m mypy -v typehint_decorator.py