Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Tornado, how can I see Exceptions rasied in a coroutine called by PeriodicCallback?

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?

like image 433
Cuadue Avatar asked May 25 '13 00:05

Cuadue


1 Answers

@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.

like image 178
Nykakin Avatar answered Sep 21 '22 13:09

Nykakin