Handling specific exceptions from Jinja2 and futures?

1,641 views
Skip to first unread message

David Rueter

unread,
Jan 30, 2016, 11:34:16 PM1/30/16
to Tornado Web Server
I am using Tornado (4.3) under Python (3.4) with Jinja2 (2.8) and things work well--except I am having trouble catching exceptions that Jinja2 raises while processing templates.

Since I am using @tornado.gen.coroutine and @run_on_executor decorators, I can't catch the exceptions with something like this:

                try:
                    buf = template.render(data=this_data)
                except Exception as e:
                    buf = '<html><body>Error rendering template.</body></html>'

When an exception is raised by Jinja2 while rendering, it is not caught by the except block.  Instead, the exception bubbles up with a traceback like this:

Can't convert 'NoneType' object to str implicitly

['Traceback (most recent call last):\n', ' File "C:/Projects/OsPy/OsPy.py", line 1020, in background_process_post_authenticated\n buf = template.render(data=this_data)\n', ' File "C:\\Prog\\Python34\\lib\\site-packages\\jinja2\\environment.py", line 989, in render\n return self.environment.handle_exception(exc_info, True)\n', ' File "C:\\Prog\\Python34\\lib\\site-packages\\jinja2\\environment.py", line 754, in handle_exception\n reraise(exc_type, exc_value, tb)\n', ' File "C:\\Prog\\Python34\\lib\\site-packages\\jinja2\\_compat.py", line 37, in reraise\n raise value.with_traceback(tb)\n', ' File "

I do have a write_error method defined, so I can catch the exception that Tornado raises, but I don't have any visibility into detailed template errors encountered by Jinja2.  This makes debugging difficult.

Can anyone share a pattern for handling detailed exceptions such as template errors thrown by Jinja2 when rendering from the template inside a @run_on_executor routine called from a @tornado.gen.coroutine method?

A. Jesse Jiryu Davis

unread,
Jan 31, 2016, 5:34:07 AM1/31/16
to python-...@googlegroups.com
Can we see your actual code where you use run_on_executor and Jinja2's "render" method?

--
You received this message because you are subscribed to the Google Groups "Tornado Web Server" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-tornad...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

David Rueter

unread,
Jan 31, 2016, 7:51:06 PM1/31/16
to Tornado Web Server
My code goes something like this:



#!/usr/bin/env python

import xxxx
...import a bunch of stuff, including jinja2, tornado


#create Jinja2 environment
jinja_env = Environment()
jinja_env.undefined = SilentUndefined


class OSHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)
    ...
   #various methods here
    ...

    @run_on_executor
    def background_process_post_authenticated(self):
        # Do everything that is needed to process an HTTP post on an authenticated session

        ...Code here to implement needed functionality, retrieve a template and data
        ...On a background thread because the request may take several seconds to service as there is a complex
        ...SQL database to access, complex business rules to process, etc

        ...Note:  this is a corporate application for dozens or hundreds of users--not a public-facing website
       
        #create Jinja2 template object from the template_str we retrieved above
        template = jinja_env.from_string(template_str)

        #render output using template and data
        try:
            buf = template.render(data=this_data)
        except Exception as e:
            buf = '<html><body>Error rendering template (' + next_page + '). ' + str(e)

        return buf

    @tornado.gen.coroutine
    def post(self):
        #MAIN ENTRY POINT FOR HTTP POST REQUEST
        ...code here to manage application-level session, database connection, etc.

        buf = yield self.background_process_post_authenticated()
        self.write(buf)
        self.finish()

def run():
    ...code here to load settings, get ready to start server

    application = tornado.web.Application([
        (r"/", OSHandler),
        ],
        debug=True,
        xsrf_cookies=True,
        cookie_secret='txxxxx secret stuff =')

    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)

    ioloop = tornado.ioloop.IOLoop.instance()

    tornado.ioloop.PeriodicCallback(do_periodic_callback, 1000).start()

    ioloop.start()

    ioloop.close()


if __name__ == "__main__":
    run()

A. Jesse Jiryu Davis

unread,
Jan 31, 2016, 9:41:37 PM1/31/16
to python-...@googlegroups.com
That appears, to me, like it should work. It may not be useful to render the template on a thread, since Python can't take advantage of multithreading for computational tasks like template-rendering. But I don't see why your code wouldn't catch rendering exceptions using that pattern, yet the traceback does appear to show an uncaught exception from render().

--
Reply all
Reply to author
Forward
0 new messages