I have seen at least two examples of decorator that work with both normal def sync_func() and async def async_function().
But all these example basically duplicated the decorator code like this
import asyncio
import typing as t
import functools
R = t.TypeVar("R")
P = t.ParamSpec("P")
def decorator(func: t.Callable[P, R]) -> t.Callable[P, R]:
if asyncio.iscoroutinefunction(func):
@functools.wraps(func)
async def decorated(*args: P.args, **kwargs: P.kwargs) -> R:
# … do something before
result = await func(*args, **kwargs)
# … do something after
return result
else:
@functools.wraps(func)
def decorated(*args: P.args, **kwargs: P.kwargs) -> R:
# … do something before [code duplication!]
result = func(*args, **kwargs)
# … do something after [code duplication!]
return result
return decorated
Is there a possibility to create a decorator without code duplication inside decorated ?
You can use the iterable property of
Couroutine.sendto run aasync deffunction without using an event loop.This inspired by a comment in cpython.
⚠️ Attention
Even so
async def decorated(…)looks like a normalasyncfunction and you are able toawaitotherasync deffunctions, they can not access the event loop (anything fromasyncio.*or functions that use it) ifis_syncisTrue. You have been warned!Best use only sync code within the decorator or use async code only after
if not is_sync:.Usage
It just works as expected
Leading to
Available on MyPy Play