Skip to content
This repository

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

1 participant

Alek Storm
Alek Storm

No description provided.

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

and return their results through a callback
ba78c5c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Jun 29, 2012
Alek Storm Add {%asynchronous%} directive to allow templates to call asynchronou…
…s functions with gen.Task

and return their results through a callback
ba78c5c
This page is out of date. Refresh to see the latest.
29  tornado/template.py
@@ -106,6 +106,16 @@ def add(x, y):
106 106
     set via ``{% set %}``, or the use of ``{% break %}`` or ``{% continue %}``
107 107
     within loops.
108 108
 
  109
+``{% asynchronous *boolean* %}``
  110
+    Sets whether the template returns its result asynchronously. If ``False``
  111
+    (the default), `Template.generate` will return synchronously. If ``True``,
  112
+    the `callback` parameter passed to `Template.generate` will be called with
  113
+    the result, the `Task` class from the `gen` module will be available, and
  114
+    the template generation function will be wrapped with `gen.engine`.::
  115
+
  116
+        {% asynchronous True %}
  117
+        {{ yield Task(foo, 'bar') }}
  118
+
109 119
 ``{% autoescape *function* %}``
110 120
     Sets the autoescape mode for the current file.  This does not affect
111 121
     other files, even those referenced by ``{% include %}``.  Note that
@@ -190,7 +200,7 @@ def add(x, y):
190 200
 import re
191 201
 import threading
192 202
 
193  
-from tornado import escape
  203
+from tornado import escape, gen
194 204
 from tornado.util import bytes_type, ObjectDict
195 205
 
196 206
 _DEFAULT_AUTOESCAPE = "xhtml_escape"
@@ -216,6 +226,7 @@ def __init__(self, template_string, name="<string>", loader=None,
216 226
         else:
217 227
             self.autoescape = _DEFAULT_AUTOESCAPE
218 228
         self.namespace = loader.namespace if loader else {}
  229
+        self.asynchronous = False
219 230
         reader = _TemplateReader(name, escape.native_str(template_string))
220 231
         self.file = _File(self, _parse(reader, self))
221 232
         self.code = self._generate_python(loader, compress_whitespace)
@@ -249,6 +260,8 @@ def generate(self, **kwargs):
249 260
             "__name__": self.name.replace('.', '_'),
250 261
             "__loader__": ObjectDict(get_source=lambda name: self.code),
251 262
         }
  263
+        if self.asynchronous:
  264
+            namespace["Task"] = gen.Task
252 265
         namespace.update(self.namespace)
253 266
         namespace.update(kwargs)
254 267
         exec self.compiled in namespace
@@ -257,6 +270,9 @@ def generate(self, **kwargs):
257 270
         # we've generated a new template (mainly for this module's
258 271
         # unittests, where different tests reuse the same name).
259 272
         linecache.clearcache()
  273
+        assert kwargs.get("callback") or not self.asynchronous
  274
+        if self.asynchronous:
  275
+            execute = gen.engine(execute)
260 276
         try:
261 277
             return execute()
262 278
         except Exception:
@@ -407,7 +423,10 @@ def generate(self, writer):
407 423
             writer.write_line("_buffer = []", self.line)
408 424
             writer.write_line("_append = _buffer.append", self.line)
409 425
             self.body.generate(writer)
410  
-            writer.write_line("return _utf8('').join(_buffer)", self.line)
  426
+            if writer.current_template.asynchronous:
  427
+                writer.write_line("callback(_utf8('').join(_buffer))", self.line)
  428
+            else:
  429
+                writer.write_line("return _utf8('').join(_buffer)", self.line)
411 430
 
412 431
     def each_child(self):
413 432
         return (self.body,)
@@ -786,7 +805,8 @@ def _parse(reader, template, in_block=None, in_loop=None):
786 805
             return body
787 806
 
788 807
         elif operator in ("extends", "include", "set", "import", "from",
789  
-                          "comment", "autoescape", "raw", "module"):
  808
+                          "comment", "autoescape", "asynchronous", "raw",
  809
+                          "module"):
790 810
             if operator == "comment":
791 811
                 continue
792 812
             if operator == "extends":
@@ -813,6 +833,9 @@ def _parse(reader, template, in_block=None, in_loop=None):
813 833
                     fn = None
814 834
                 template.autoescape = fn
815 835
                 continue
  836
+            elif operator == "asynchronous":
  837
+                template.asynchronous = suffix.strip() == "True"
  838
+                continue
816 839
             elif operator == "raw":
817 840
                 block = _Expression(suffix, line, raw=True)
818 841
             elif operator == "module":
10  tornado/test/template_test.py
@@ -147,6 +147,16 @@ def test_break_in_apply(self):
147 147
         except ParseError:
148 148
             pass
149 149
 
  150
+    def test_asynchronous(self):
  151
+        def foo(arg, callback):
  152
+            callback("foo-"+arg)
  153
+        results = []
  154
+        def bar(result):
  155
+            results.append(result)
  156
+        Template("{%asynchronous True%}{%set result = yield Task(foo, 'a')%}{{result}}").generate(foo=foo, callback=bar)
  157
+        Template("{%asynchronous True%}{{yield Task(foo, 'b')}}").generate(foo=foo, callback=bar)
  158
+        self.assertEqual(results, ["foo-a", "foo-b"])
  159
+
150 160
 
151 161
 class StackTraceTest(LogTrapTestCase):
152 162
     def test_error_line_number_expression(self):
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.