Search code examples
pythonhtmltornadotemplate-enginecoroutine

How does tornado.web.RequestHandler.render_string() load a template without blocking the whole application?


I'm loading templates from a coroutine in Tornado using render_string() and it came to my mind that render_string() is not a coroutine. So we're accessing the disk form a coroutine, but we're not yielding any futures. I wonder how does this work and if it blocks the application or not.


Solution

  • It's true, the first time a given template is read from the disk, it blocks Tornado's event loop:

    class Loader(BaseLoader):
        def _create_template(self, name):
            path = os.path.join(self.root, name)
            with open(path, "rb") as f:
                template = Template(f.read(), name=name, loader=self)
                return template
    

    This initial load is probably fast, since the template is likely only a few kilobytes and already loaded into the machine's in-memory filesystem cache. Subsequent accesses of the same template by the same Tornado process are cached within Tornado itself:

    class BaseLoader(object):
        def load(self, name, parent_path=None):
            """Loads a template."""
            name = self.resolve_path(name, parent_path=parent_path)
            with self.lock:
                if name not in self.templates:
                    self.templates[name] = self._create_template(name)
                return self.templates[name]
    

    So it doesn't seem worthwhile for Tornado to defer the filesystem access to a thread.

    Often in Python async frameworks you'll see that not all I/O is performed asynchronously - quick and predictable blocking operations like accessing a file or a MySQL query might not block the loop long enough to worry about. What's critical is that long or unpredictable operations, like calling a remote HTTP service, are scheduled on the event loop.