Skip to content Skip to sidebar Skip to footer

Python Async Decorator Preserve Typing

For the following file: from abc import ABC, abstractmethod from typing import Any, Awaitable, Callable, TypeVar, cast T = TypeVar('T') def dec(args: Any) -> Callable[..., Aw

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"