Search code examples
pythonweb-servicestornado

Parametrizing Tornado RequestHandler


Let's say I have a very simple web app in python Tornado framework with a single endpoint. All I'm interested in is returning a value calculated before starting the server. Slightly modified example from https://www.tornadoweb.org/en/stable/index.html will do just fine.

handler.py

import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('I want to return var `expensive_value`')

main.py

import tornado.ioloop
import tornado.web


def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    # calculate some var here before starting the server
    expensive_value = 'value from long_calculation()'
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

When running python main.py and sending a request to the endpoint it returns only a string of course. But I'd like to return the actual value of expensive_value. Currently I'm aware of two solutions to the problem.

1. Using global variable in handler

handler.py

import tornado.web


global_variable = None


def setter(val):
    global global_variable
    global_variable = val


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write(global_variable)

main.py

import tornado.ioloop
import tornado.web

from handler import MainHandler, setter


def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])


if __name__ == "__main__":
    expensive_value = 'value from long_calculation()'
    setter(expensive_value)
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

Having a global var and setting its value from some other module sounds like an antipattern to me.

2. Using initialize method in handler

handler.py

import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def initialize(self, expensive_value):
        self.expensive_value = expensive_value

    def get(self):
        self.write(self.expensive_value)

main.py

import tornado.ioloop
import tornado.web

from handler import MainHandler


def make_app(parameter):
    return tornado.web.Application([
        (r"/", MainHandler, {'expensive_value': parameter}),
    ])


if __name__ == "__main__":
    expensive_value = 'value from long_calculation()'
    app = make_app(expensive_value)
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

This solution is better. But initialize method is called for every request. I realize the overhead for that would be rather small but I think it might be misleading for potential reader of the code since expensive_value never changes.

Summary

Both of these solution work. But I don't like any of them and it seems like I'm missing some Tornado functionality. What would be a pythonic way to solve this?

For example I believe Flask has app.config dictionary that is accessible in handlers and it seems to be a nice solution to this as expensive_value is indeed a configuration to the app. But I'm not aware of anything similar in Tornado.


Solution

  • Handlers have access to self.application.settings which is a dictionary containing additional arguments passed to the Application constructor.

    So you can pass expensive_value directly to the Application class like this:

    def make_app(parameter):
        return tornado.web.Application(
            [
                (r"/", MainHandler),
            ],
    
            expensive_value=parameter
        )
    

    And access this value in any handler like this:

    def initialize(self):
        self.expensive_value = self.application.settings.get('expensive_value')