@@ -20,29 +20,41 @@ def on_fetch(self, response):
20
20
21
21
class GenAsyncHandler(RequestHandler):
22
22
@asynchronous
23
- @gen.engine
23
+ @gen.coroutine
24
24
def get(self):
25
25
http_client = AsyncHTTPClient()
26
- response = yield gen.Task( http_client.fetch, "http://example.com")
26
+ response = yield http_client.fetch( "http://example.com")
27
27
do_something_with_response(response)
28
28
self.render("template.html")
29
29
30
- `Task` works with any function that takes a ``callback`` keyword
31
- argument. You can also yield a list of ``Tasks``, which will be
30
+ Most asynchronous functions in Tornado return a `~concurrent.futures.Future`;
31
+ yielding this object returns its `~concurrent.futures.Future.result`.
32
+
33
+ For functions that do not return ``Futures``, `Task` works with any
34
+ function that takes a ``callback`` keyword argument (most Tornado functions
35
+ can be used in either style, although the ``Future`` style is preferred
36
+ since it is both shorter and provides better exception handling)::
37
+
38
+ @gen.coroutine
39
+ def get(self):
40
+ yield gen.Task(AsyncHTTPClient().fetch, "http://example.com")
41
+
42
+ You can also yield a list of ``Futures`` and/or ``Tasks``, which will be
32
43
started at the same time and run in parallel; a list of results will
33
44
be returned when they are all finished::
34
45
46
+ @gen.coroutine
35
47
def get(self):
36
48
http_client = AsyncHTTPClient()
37
- response1, response2 = yield [gen.Task( http_client.fetch, url1),
38
- gen.Task( http_client.fetch, url2)]
49
+ response1, response2 = yield [http_client.fetch( url1),
50
+ http_client.fetch( url2)]
39
51
40
52
For more complicated interfaces, `Task` can be split into two parts:
41
53
`Callback` and `Wait`::
42
54
43
55
class GenAsyncHandler2(RequestHandler):
44
56
@asynchronous
45
- @gen.engine
57
+ @gen.coroutine
46
58
def get(self):
47
59
http_client = AsyncHTTPClient()
48
60
http_client.fetch("http://example.com",
@@ -96,17 +108,21 @@ class ReturnValueIgnoredError(Exception):
96
108
97
109
98
110
def engine (func ):
99
- """Decorator for asynchronous generators.
111
+ """Callback-oriented decorator for asynchronous generators.
100
112
101
- Any generator that yields objects from this module must be wrapped
102
- in this decorator. The decorator only works on functions that are
103
- already asynchronous. For `~tornado.web.RequestHandler`
104
- ``get``/``post``/etc methods, this means that both the
105
- `tornado.web.asynchronous` and `tornado.gen.engine` decorators
106
- must be used (for proper exception handling, ``asynchronous``
107
- should come before ``gen.engine``). In most other cases, it means
108
- that it doesn't make sense to use ``gen.engine`` on functions that
109
- don't already take a callback argument.
113
+ This is an older interface; for new code that does not need to be
114
+ compatible with versions of Tornado older than 3.0 the
115
+ `coroutine` decorator is recommended instead.
116
+
117
+ This decorator is similar to `coroutine`, except it does not
118
+ return a `~concurrent.futures.Future` and the ``callback``
119
+ argument is not treated specially.
120
+
121
+ In most cases, functions decorated with `engine` should take
122
+ a ``callback`` argument and invoke it with their result when
123
+ they are finished. One notable exception is the
124
+ `~tornado.web.RequestHandler` ``get``/``post``/etc methods,
125
+ which use ``self.finish()`` in place of a callback argument.
110
126
"""
111
127
@functools .wraps (func )
112
128
def wrapper (* args , ** kwargs ):
@@ -146,19 +162,32 @@ def final_callback(value):
146
162
147
163
148
164
def coroutine (func ):
149
- """Future-oriented decorator for asynchronous generators.
150
-
151
- Similar to ``@gen.engine``, but the decorated function does not receive
152
- a ``callback`` parameter. Instead, it may "return" by raising the
153
- special exception `gen.Return(value)`. In Python 3.3+, it is also
154
- possible for the function to simply use the ``return`` statement.
155
- (prior to Python 3.3 generators were not allowed to also return values.
165
+ """Decorator for asynchronous generators.
156
166
157
- Functions with this decorator return a `Future`. Additionally,
158
- they may be called with a ``callback`` keyword argument, which will
159
- be invoked with the future's result when it resolves. If the coroutine
160
- fails, the callback will not be run and an exception will be raised
161
- into the surrounding `StackContext`.
167
+ Any generator that yields objects from this module must be wrapped
168
+ in either this decorator or `engine`. These decorators only work
169
+ on functions that are already asynchronous. For
170
+ `~tornado.web.RequestHandler` ``get``/``post``/etc methods, this
171
+ means that both the `tornado.web.asynchronous` and
172
+ `tornado.gen.coroutine` decorators must be used (for proper
173
+ exception handling, ``asynchronous`` should come before
174
+ ``gen.coroutine``).
175
+
176
+ Coroutines may "return" by raising the special exception
177
+ `Return(value) <Return>`. In Python 3.3+, it is also possible for
178
+ the function to simply use the ``return value`` statement (prior to
179
+ Python 3.3 generators were not allowed to also return values).
180
+ In all versions of Python a coroutine that simply wishes to exit
181
+ early may use the ``return`` statement without a value.
182
+
183
+ Functions with this decorator return a
184
+ `~concurrent.futures.Future`. Additionally, they may be called
185
+ with a ``callback`` keyword argument, which will be invoked with
186
+ the future's result when it resolves. If the coroutine fails, the
187
+ callback will not be run and an exception will be raised into the
188
+ surrounding `.StackContext`. The ``callback`` argument is not
189
+ visible inside the decorated function; it is handled by the
190
+ decorator itself.
162
191
163
192
From the caller's perspective, ``@gen.coroutine`` is similar to
164
193
the combination of ``@return_future`` and ``@gen.engine``.
@@ -205,13 +234,36 @@ def final_callback(value):
205
234
206
235
207
236
class Return (Exception ):
237
+ """Special exception to return a value from a `coroutine`.
238
+
239
+ If this exception is raised, its value argument is used as the
240
+ result of the coroutine::
241
+
242
+ @gen.coroutine
243
+ def fetch_json(url):
244
+ response = yield AsyncHTTPClient().fetch(url)
245
+ raise gen.Return(json_decode(response.body))
246
+
247
+ In Python 3.3, this exception is no longer necessary: the ``return``
248
+ statement can be used directly to return a value (previously
249
+ ``yield`` and ``return`` with a value could not be combined in the
250
+ same function).
251
+
252
+ By analogy with the return statement, the value argument is optional,
253
+ but it is never necessary to ``raise gen.Return()``. The ``return``
254
+ statement can be used with no arguments instead.
255
+ """
208
256
def __init__ (self , value = None ):
209
257
super (Return , self ).__init__ ()
210
258
self .value = value
211
259
212
260
213
261
class YieldPoint (object ):
214
- """Base class for objects that may be yielded from the generator."""
262
+ """Base class for objects that may be yielded from the generator.
263
+
264
+ Applications do not normally need to use this class, but it may be
265
+ subclassed to provide additional yielding behavior.
266
+ """
215
267
def start (self , runner ):
216
268
"""Called by the runner after the generator has yielded.
217
269
@@ -277,7 +329,7 @@ def get_result(self):
277
329
278
330
279
331
class WaitAll (YieldPoint ):
280
- """Returns the results of multiple previous `Callbacks`.
332
+ """Returns the results of multiple previous `Callbacks <Callback> `.
281
333
282
334
The argument is a sequence of `Callback` keys, and the result is
283
335
a list of results in the same order.
0 commit comments