我写了一个程序,它有一个定期从主ioloop
调用的协程,如下所示:
from tornado import ioloop, web, gen, log
tornado.log.enable_pretty_printing()
import logging; logging.basicConfig()
@gen.coroutine
def callback():
print 'get ready for an error...'
raise Exception()
result = yield gen.Task(my_async_func)
l = ioloop.IOLoop.instance()
cb = ioloop.PeriodicCallback(callback, 1000, io_loop=l)
cb.start
l.start()
我得到的输出很简单:
$ python2 app.py
get ready for an error...
get ready for an error...
get ready for an error...
get ready for an error...
raise Exception()
被默默忽略!如果我将回调更改为
def callback():
print 'get ready for an error...'
raise Exception()
我得到了一个完整的堆栈跟踪,就像我期望的那样(并且需要)。如何在使用协程时获得堆栈跟踪?
答案 0 :(得分:7)
@tornado.gen.coroutine
使函数返回tornado.concurrent.Future
对象,因此您不必将其包装到tornado.gen.Task
中,但可以使用yield
关键字调用它:
@tornado.gen.coroutine
def inner():
logging.info('inner')
@tornado.gen.coroutine
def outer():
logging.info('outer')
yield inner()
以这种方式修饰的函数中的异常被包装到此tornado.concurrent.Future
对象中,稍后可以使用其exception()
方法返回它。在你的情况下,tornado.ioloop.PeriodicCallback
会调用你的回调方法,然后它就会丢弃返回的tornado.concurrent.Future
对象及其包含的异常。要检测异常,您可以使用链调用:
@tornado.gen.coroutine
def inner():
raise Exception()
@tornado.gen.coroutine
def outer():
try:
yield inner()
except Exception, e:
logging.exception(e)
但是在你的情况下,只是在投掷之后抓住它就更容易了:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import tornado.gen
import tornado.ioloop
import tornado.options
import logging
tornado.options.parse_command_line()
@tornado.gen.coroutine
def callback():
logging.info('get ready for an error...')
try:
raise Exception()
except Exception, e:
logging.exception(e)
main_loop = tornado.ioloop.IOLoop.instance()
scheduler = tornado.ioloop.PeriodicCallback(callback, 1000, io_loop = main_loop)
scheduler.start()
main_loop.start()
@gen.engine
不会使函数返回tornado.concurrent.Future
,因此异常不会被包装。
答案 1 :(得分:4)
我不明白为什么,但将@gen.coroutine
更改为@gen.engine
可以让异常正确起泡。它仍然可以异步工作。