I wrote a program which has a coroutine called periodically from the main ioloop like this:
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()
The output I get is simply:
$ python2 app.py
get ready for an error...
get ready for an error...
get ready for an error...
get ready for an error...
The raise Exception() is silently ignored! If I change the callback to be just
def callback():
print 'get ready for an error...'
raise Exception()
I get a full stack trace like I expect (and need). How can I get that stack trace while using the coroutine?
@tornado.gen.coroutine makes function return tornado.concurrent.Future object so you don't have to wrap it into tornado.gen.Task but you can call it using yield keyword:
@tornado.gen.coroutine
def inner():
logging.info('inner')
@tornado.gen.coroutine
def outer():
logging.info('outer')
yield inner()
An exception in function decorated this way is wrapped into this tornado.concurrent.Future object and it can be returned later using its exception() method. In your case tornado.ioloop.PeriodicCallback calls your callback method and after that it's just throwing away returned tornado.concurrent.Future object along with exception it contained. To detect exception you can use chain calling:
@tornado.gen.coroutine
def inner():
raise Exception()
@tornado.gen.coroutine
def outer():
try:
yield inner()
except Exception, e:
logging.exception(e)
But in your case it's actually easier just to catch it just after throwing:
#!/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 does not make function return a tornado.concurrent.Future so exceptions aren't wrapped.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With