You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm trying to type hint a decorator that should be applied to a method, and returns a different signature. I managed to get a working solution, with a small downside related to positional only arguments:
fromtypingimportCallable, TypeVarfromabcimportABC, abstractmethodclassBase(ABC):
@abstractmethoddeffunc(self, arg: int|str, /) ->int:
passBaseT=TypeVar("BaseT", bound=Base, contravariant=True) # used to type the `self` argumentdefhandle_int(func: Callable[[BaseT, str], int], /) ->Callable[[BaseT, int|str], int]:
# A wrapper would be defined to convert ints to strings
...
classImpl(Base):
@handle_int# Breaks if Base.func doesn't have the pos. only '/' markerdeffunc(self, arg: str) ->int:
return1defother(self):
reveal_type(self.func) # (int | str) -> int
Using callback protocols, I'm able to make it work without having to define the base class as being pos. only:
fromtypingimportCallable, Protocol, TypeVar, Self, overloadfromabcimportABC, abstractmethodclassBase(ABC):
@abstractmethoddeffunc(self, arg: int|str) ->int:
passBaseT=TypeVar("BaseT", bound=Base, contravariant=True)
classBoundFuncProto(Protocol):
def__call__(self, arg: int|str) ->int: ...
classFuncProto(Protocol[BaseT]):
def__call__(_self, self: BaseT, arg: int|str) ->int: ...
# Trying to define `__get__`, as suggested in other discussions, and to make it work# with instance vs class access, but to no eval:@overloaddef__get__(self, obj: None, objtype: type[Base] |None= ..., /) ->Self: ...
@overloaddef__get__(self, obj: BaseT, objtype: type[BaseT] |None= ..., /) ->BoundFuncProto: ...
defhandle_int(func: Callable[[BaseT, str], int], /) ->FuncProto[BaseT]:
# A wrapper would be defined to convert ints to strings
...
classImpl(Base):
@handle_intdeffunc(self, arg: str) ->int: # "func" overrides method of same name in class "Base" with incompatible type "FuncProto[Impl]"return1defother(self):
reveal_type(self.func) # BoundFuncProtoself.func(1) # type checksreveal_type(Impl.func) # FuncProto[Impl]
So I'm wondering if I'm missing anything in my FuncProto definition that would make it compatible with Base.func? Is using protocols the right path?
The first solution (returning a new Callable from the decorator) is way easier to implement, but it's inconvenient to have them considered as having the arguments pos. only.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Related but seems a bit different: #1040, #1357.
I'm trying to type hint a decorator that should be applied to a method, and returns a different signature. I managed to get a working solution, with a small downside related to positional only arguments:
Code sample in pyright playground
Using callback protocols, I'm able to make it work without having to define the base class as being pos. only:
Code sample in pyright playground
So I'm wondering if I'm missing anything in my
FuncProto
definition that would make it compatible withBase.func
? Is using protocols the right path?The first solution (returning a new
Callable
from the decorator) is way easier to implement, but it's inconvenient to have them considered as having the arguments pos. only.Beta Was this translation helpful? Give feedback.
All reactions