Diferencia en tornado.gen.engine v / s tornado.gen.coroutine

Pasando por la documentación de tornado.gen alguien puede ayudarme a entender la diferencia exacta entre tornado.gen.coroutine y tornado.gen.engine

Como dice la documentación del tornado para gen.engine :

Este decorador es similar a Coroutine, excepto que no devuelve un futuro y el argumento de callback no se trata especialmente.

Y como dice la documentación de gen.coroutine

Desde la perspectiva de la persona que llama, @ gen.coroutine es similar a la combinación de @return_future y @ gen.engine.

gen.engine es básicamente una versión más antigua y menos simplificada de lo que hace coroutine. Si está escribiendo un código nuevo, debe seguir los consejos de la documentación y usar siempre tornado.gen.coroutine .

Es bastante evidente si observa el código para ambas funciones (con la documentación eliminada).

motor:

 def engine(func): @functools.wraps(func) def wrapper(*args, **kwargs): runner = None def handle_exception(typ, value, tb): if runner is not None: return runner.handle_exception(typ, value, tb) return False with ExceptionStackContext(handle_exception) as deactivate: try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) else: if isinstance(result, types.GeneratorType): def final_callback(value): if value is not None: raise ReturnValueIgnoredError( "@gen.engine functions cannot return values: " "%r" % (value,)) assert value is None deactivate() runner = Runner(result, final_callback) runner.run() return if result is not None: raise ReturnValueIgnoredError( "@gen.engine functions cannot return values: %r" % (result,)) deactivate() # no yield, so we're done return wrapper 

coroutine

 def coroutine(func): @functools.wraps(func) def wrapper(*args, **kwargs): runner = None future = TracebackFuture() if 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) def handle_exception(typ, value, tb): try: if runner is not None and runner.handle_exception(typ, value, tb): return True except Exception: typ, value, tb = sys.exc_info() future.set_exc_info((typ, value, tb)) return True with ExceptionStackContext(handle_exception) as deactivate: try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: deactivate() future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): def final_callback(value): deactivate() future.set_result(value) runner = Runner(result, final_callback) runner.run() return future deactivate() future.set_result(result) return future return wrapper 

Ambos son probablemente bastante difíciles de entender a primera vista. Pero aún así, es obvio que el código es muy similar, excepto que @gen.coroutine tiene un manejo especial del kwarg de callback de callback , y construye / devuelve un Future . @gen.engine tiene algún código que específicamente arroja un error si intenta devolver algo de él, en lugar de ponerlo en el Future .