en este escenario:
async def foo(f): async def wrapper(*args, **kwargs): return f(*args, **kwargs) return wrapper @foo async def boo(*args, **kwargs): pass¿La llamada a foo como decorador para boo decorator es una llamada asíncrona?
--Primera edición: también, ¿cómo se maneja la cadena de llamadas de rutinas como decoradores?
Gracias al comentario de @blacknght, considerando
def foo(): def wrapper(func): @functools.wraps(func) async def wrapped(*args): # Some fancy foo stuff return await func(*args) return wrapped return wrappery
def boo(): def wrapper(func): @functools.wraps(func) async def wrapped(*args): # Some fancy boo stuff return await func(*args) return wrapped return wrappercomo dos decoradores, y
@foo() @boo() async def work(*args): pass Como foo está envolviendo la rutina de work , la clave es await la func(*arg) en ambos decoradores.
def foo(f): async def wrapper(*args, **kwargs): return await f(*args, **kwargs) return wrapper @foo async def boo(*args, **kwargs): passSu decorador debe ser una función normal y funcionará bien.
Cuando se evalúa un decorador, Python ejecuta el método con la función como argumento.
@foo async def boo(): passEvalúa a:
__main__.boo = foo(boo)Si foo es un tipo de función asíncrona ( main .boo) será un objeto de rutina, no un objeto de función. Pero si foo es una función de sincronización regular, se evaluará de inmediato y main .boo será el contenedor devuelto.
Aquí hay un enfoque alternativo usando la biblioteca decorator (es decir, pip install decorator primero):
import asyncio import decorator @decorator.decorator async def decorate_coro(coro, *args, **kwargs): try: res = await coro(*args, **kwargs) except Exception as e: print(e) else: print(res) @decorate_coro async def f(): return 42 @decorate_coro async def g(): return 1 / 0 async def main(): return await asyncio.gather(f(), g()) if __name__ == '__main__': asyncio.run(main())Producción:
42 division by zero