On Feb 9, 2020, at 13:39, hason.amos@gmail.com wrote:
As of today, in order to make a decorator compatible with both normal and async functions, you have to write something like this:
def decor(func): if asyncio.iscoroutinefunction(func): async def wrapper(*args, **kwargs): ... await func(*args, **kwargs) ... else: def wrapper(*args, **kwargs): ... func(*args, **kwargs) ... return wrapper
This is an unnecessary repetition. It would be cleaner to be able to write it differently, as such:
def decor(func): async? def wrapper(*args, **kwargs): ... await? func(*args, **kwargs) ... return wrapper
How would this work? It can’t possibly resolve `async? def` into `async def` or `def` at compile time, because which one you want depends on the runtime value of `func`. And, even if that weren’t a problem, how is Python supposed to know what to look at to decide whether it’s async or not? The type of the value passed for the first parameter in the innermost frame or something? That’s incredibly magical. And if you have a decorator that double-wraps things, it won’t even be right. Maybe you could write a decorator-generating helper function (like the various things in PyPI libraries like decorator) that’s less magical but that still helps here. I’m not sure if it’s doable in full generality (if it is, I’ll bet someone’s already written it and shared it on PyPI…), but it should be trivial to write helpers for creating a decorator that works on either a regular function or an async coro with by applying before/after functions, or a context to with around the original function, or a param-translator, or other common things that together cover 90% of your needs. The implementations of those handful of helpers would need to be verbose, but your code that uses them to create hundreds of different decorators wouldn’t.