Python Async Decorator Preserve Typing
Solution 1:
TLDR: A function declaration async def name(parameters) -> R:
creates an object of type (parameters) -> Awaitable[R]
, not Awaitable[R]
. This means the cast cast(Awaitable[T], wrapper)
is wrong and should be omitted, and the various Callable
return types must be adjusted as well.
A naive decorator to show the execution of an async def
function (roughly dec2
) would look like this:
defshow_call(f: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]:
asyncdefwrapper(*args: Any, **kwargs: Any) -> T:
print(f"Executing {f.__name__}")
returnawait f(*args, **kwargs)
return wrapper
This receives an callable matching async def
and returns a callable matching async def
. In other words, it preserves the general type of "an async
function".
Note that there is no cast
needed.
Since the Callable
argument was specified as ...
, the parameter information is lost. This can be fixed with PEP 612 (Python 3.10 / typing_extensions
) parameter variables, which are similar to type variables.
from typing importAny, Awaitable, Callable, TypeVar, ParamSpec
T = TypeVar('T') # the callable/awaitable return type
P = ParamSpec('P') # the callable parametersdefdec(message: Any) -> Callable[[Callable[P, Awaitable[T]]], Callable[P, Awaitable[T]]]:
defdec2(f: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
asyncdefwrapper(*args: Any, **kwargs: Any) -> T:
print(message)
returnawait f(*args, **kwargs)
return wrapper
return dec2
Post a Comment for "Python Async Decorator Preserve Typing"