Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add {%asynchronous%} directive to allow templates to call asynchronous functions with gen.Task and return their results through a callback #553

Open
wants to merge 1 commit into from

3 participants

@alekstorm

No description provided.

@alekstorm alekstorm Add {%asynchronous%} directive to allow templates to call asynchronou…
…s functions with gen.Task

and return their results through a callback
ba78c5c
@bdarnell bdarnell added the template label
@stalkerg

It is not necessary for the Tornado templates. But it is very necessary for Mako templates where there caching template functions. It is up to them to perform or not perform should query to the database.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 29, 2012
  1. @alekstorm

    Add {%asynchronous%} directive to allow templates to call asynchronou…

    alekstorm authored
    …s functions with gen.Task
    
    and return their results through a callback
This page is out of date. Refresh to see the latest.
Showing with 36 additions and 3 deletions.
  1. +26 −3 tornado/template.py
  2. +10 −0 tornado/test/template_test.py
View
29 tornado/template.py
@@ -106,6 +106,16 @@ def add(x, y):
set via ``{% set %}``, or the use of ``{% break %}`` or ``{% continue %}``
within loops.
+``{% asynchronous *boolean* %}``
+ Sets whether the template returns its result asynchronously. If ``False``
+ (the default), `Template.generate` will return synchronously. If ``True``,
+ the `callback` parameter passed to `Template.generate` will be called with
+ the result, the `Task` class from the `gen` module will be available, and
+ the template generation function will be wrapped with `gen.engine`.::
+
+ {% asynchronous True %}
+ {{ yield Task(foo, 'bar') }}
+
``{% autoescape *function* %}``
Sets the autoescape mode for the current file. This does not affect
other files, even those referenced by ``{% include %}``. Note that
@@ -190,7 +200,7 @@ def add(x, y):
import re
import threading
-from tornado import escape
+from tornado import escape, gen
from tornado.util import bytes_type, ObjectDict
_DEFAULT_AUTOESCAPE = "xhtml_escape"
@@ -216,6 +226,7 @@ def __init__(self, template_string, name="<string>", loader=None,
else:
self.autoescape = _DEFAULT_AUTOESCAPE
self.namespace = loader.namespace if loader else {}
+ self.asynchronous = False
reader = _TemplateReader(name, escape.native_str(template_string))
self.file = _File(self, _parse(reader, self))
self.code = self._generate_python(loader, compress_whitespace)
@@ -249,6 +260,8 @@ def generate(self, **kwargs):
"__name__": self.name.replace('.', '_'),
"__loader__": ObjectDict(get_source=lambda name: self.code),
}
+ if self.asynchronous:
+ namespace["Task"] = gen.Task
namespace.update(self.namespace)
namespace.update(kwargs)
exec self.compiled in namespace
@@ -257,6 +270,9 @@ def generate(self, **kwargs):
# we've generated a new template (mainly for this module's
# unittests, where different tests reuse the same name).
linecache.clearcache()
+ assert kwargs.get("callback") or not self.asynchronous
+ if self.asynchronous:
+ execute = gen.engine(execute)
try:
return execute()
except Exception:
@@ -407,7 +423,10 @@ def generate(self, writer):
writer.write_line("_buffer = []", self.line)
writer.write_line("_append = _buffer.append", self.line)
self.body.generate(writer)
- writer.write_line("return _utf8('').join(_buffer)", self.line)
+ if writer.current_template.asynchronous:
+ writer.write_line("callback(_utf8('').join(_buffer))", self.line)
+ else:
+ writer.write_line("return _utf8('').join(_buffer)", self.line)
def each_child(self):
return (self.body,)
@@ -786,7 +805,8 @@ def _parse(reader, template, in_block=None, in_loop=None):
return body
elif operator in ("extends", "include", "set", "import", "from",
- "comment", "autoescape", "raw", "module"):
+ "comment", "autoescape", "asynchronous", "raw",
+ "module"):
if operator == "comment":
continue
if operator == "extends":
@@ -813,6 +833,9 @@ def _parse(reader, template, in_block=None, in_loop=None):
fn = None
template.autoescape = fn
continue
+ elif operator == "asynchronous":
+ template.asynchronous = suffix.strip() == "True"
+ continue
elif operator == "raw":
block = _Expression(suffix, line, raw=True)
elif operator == "module":
View
10 tornado/test/template_test.py
@@ -147,6 +147,16 @@ def test_break_in_apply(self):
except ParseError:
pass
+ def test_asynchronous(self):
+ def foo(arg, callback):
+ callback("foo-"+arg)
+ results = []
+ def bar(result):
+ results.append(result)
+ Template("{%asynchronous True%}{%set result = yield Task(foo, 'a')%}{{result}}").generate(foo=foo, callback=bar)
+ Template("{%asynchronous True%}{{yield Task(foo, 'b')}}").generate(foo=foo, callback=bar)
+ self.assertEqual(results, ["foo-a", "foo-b"])
+
class StackTraceTest(LogTrapTestCase):
def test_error_line_number_expression(self):
Something went wrong with that request. Please try again.