如何通过错误调用的Tornado协程引发异常?

时间:2016-08-11 14:25:36

标签: python python-2.7 asynchronous tornado

我有一个Tornado的场景,我有一个从非协同调用或没有屈服的协程,但我需要传回异常。

想象一下以下方法:

@gen.coroutine
def create_exception(with_yield):
    if with_yield:
        yield exception_coroutine()
    else:
        exception_coroutine()

@gen.coroutine
def exception_coroutine():
    raise RuntimeError('boom')

def no_coroutine_create_exception(with_yield):
    if with_yield:
        yield create_exception(with_yield)
    else:
        create_exception(with_yield)

通话:

try:
    # Throws exception
    yield create_exception(True)
except Exception as e:
    print(e)

将正确引发异常。但是,以下都不会引发异常:

try:
    # none of these throw the exception at this level
    yield create_exception(False)
    no_coroutine_create_exception(True)
    no_coroutine_create_exception(False)
except Exception as e:
    print('This is never hit)

后者是与我的问题类似的变体 - 我的控制之外的代码在不使用yield的情况下调用coroutines。在某些情况下,它们本身不是协同程序。无论哪种情况,都意味着它们产生的任何异常都会被吞噬,直到Tornado将其返回为"未来未收到异常。"

这与Tornado的意图相反,他们的文档基本上表明你需要在整个堆栈中执行yield / coroutine,以便它能够在没有hackery / trickery的情况下工作。

我可以改变引发异常的方式(即修改exception_coroutine)。但我无法改变几种中间方法。

为了强制在整个Tornado堆栈中引发异常,我能做些什么,即使它没有正确地产生?基本上是在最后三种情况下适当地提出异常?

这很复杂,因为我无法更改导致此情况的代码。我只能在上面更改exception_coroutine

2 个答案:

答案 0 :(得分:1)

你在Python中要求的是不可能的,因为在协程完成之后,调用函数决定yield是否yielded。协程必须在不引发异常的情况下返回,因此它可以是Future,之后如果yielded不是{{2016_08_13_001252_create_roles_table.php,它就不再可能将异常引发到调用方的上下文中1}}。

你可以做的最好的事情就是检测Future的垃圾收集,但这除了log之外什么也做不了(这就是“未检索到的未来异常”消息的工作原理)

答案 1 :(得分:-1)

如果你很好奇为什么这不起作用,那是因为no_coroutine_create_exception包含一个yield语句。因此它是一个生成器函数,调用它执行它的代码,它只创建一个生成器对象:

>>> no_coroutine_create_exception(True)
<generator object no_coroutine_create_exception at 0x101651678>
>>> no_coroutine_create_exception(False)
<generator object no_coroutine_create_exception at 0x1016516d0>

上面的调用都没有执行任何Python代码,它只创建必须迭代的生成器。

你必须创建一个阻止函数来启动IOLoop并运行它直到你的协程完成:

def exception_blocking():
    return ioloop.IOLoop.current().run_sync(exception_coroutine)


exception_blocking()

(IOLoop充当多个非阻塞任务的调度程序,gen.coroutine装饰器负责迭代协程直到完成。)

但是,我认为我很可能会回答你的问题,但只是让你继续沿着非生产性的道路前进。你几乎肯定最好不要使用异步代码或阻止代码而不是试图混合它们。