Browse files

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

…s functions with gen.Task

and return their results through a callback
  • Loading branch information...
1 parent cfd7a96 commit ba78c5c5362e75387dd6c5f2e094e398ef416094 @alekstorm committed Jun 29, 2012
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):

0 comments on commit ba78c5c

Please sign in to comment.