New syntax for decorators that are compatible with both normal and async functions
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
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.
While I'm not sure this idea can totally work, I do think we need a method of closing the gap between asyc coroutines and normal functions. As of today, async applications need to create an entirely different codebase, sometimes just by copy and paste, while changing function definitions to async and function calls to await. This creates a lot of work, from porting code to maintaining both variants, to just having code duplication all around. Not necessarily using this syntax, a way for making decorators support both normal and async, could be a first step towards shared codebase. -- Bar Harel On Mon, Feb 10, 2020, 2:08 AM Andrew Barnert via Python-ideas < python-ideas@python.org> wrote:
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.
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/NVZ3A7... Code of Conduct: http://python.org/psf/codeofconduct/
participants (3)
-
Andrew Barnert
-
Bar Harel
-
hason.amos@gmail.com