Had a complete headache trying to figure out how a decorator as a class can maintain the possible async properties of a method. The solution is actually very simple. When called, use inspect.iscoroutinefunction
to check whether it is a coroutine, and return again an async method!
The example adds given paths to a registry,
import inspect
from functools import wraps
paths_registry = []
class route(object):
def __init__(self, path: str, **kwargs) -> None:
self._path = path
def __call__(self, fn):
paths_registry.append(
{
"path": self._path,
}
)
@wraps(fn)
def decorated(*args, **kwargs):
return fn(*args, **kwargs)
@wraps(fn)
async def asyncdecorated(*args, **kwargs):
return await fn(*args, **kwargs)
if inspect.iscoroutinefunction(fn):
return asyncdecorated
return decorated
A method with this decorator would look like:
@route('hello')
async def hello():
print("haai")
and the method with decorator is still a coroutine:
>>> inspect.iscoroutinefunction(hello)
True