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