Skip to content

Commit 53fc8fd

Browse files
committed
Rewrite gen docs to make coroutine the primary decorator.
1 parent 9c1b189 commit 53fc8fd

File tree

3 files changed

+96
-35
lines changed

3 files changed

+96
-35
lines changed

docs/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
# tornado.gen
3535
"Multi",
3636
"Runner",
37-
"YieldPoint",
3837

3938
# tornado.ioloop
4039
"PollIOLoop",

docs/gen.rst

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
------------
1515

1616
Instances of the following classes may be used in yield expressions
17-
in the generator.
17+
in the generator. `Futures <concurrent.futures.Future>` may be yielded as
18+
well; their result method will be called automatically when they
19+
are ready. Additionally, lists of any combination of these objects
20+
may be yielded; the result is a list of the results of each yield
21+
point in the same order.
1822

1923
.. autoclass:: Task
2024

@@ -24,13 +28,19 @@
2428

2529
.. autoclass:: WaitAll
2630

31+
.. autoclass:: YieldPoint
32+
:members:
33+
2734
Other classes
2835
-------------
2936

37+
.. autoexception:: Return
38+
3039
.. class:: Arguments
3140

3241
The result of a yield expression whose callback had more than one
3342
argument (or keyword arguments).
3443

35-
The `Arguments` object can be used as a tuple ``(args, kwargs)``
36-
or an object with attributes ``args`` and ``kwargs``.
44+
The `Arguments` object is a `collections.namedtuple` and can be
45+
used either as a tuple ``(args, kwargs)`` or an object with attributes
46+
``args`` and ``kwargs``.

tornado/gen.py

Lines changed: 83 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,41 @@ def on_fetch(self, response):
2020
2121
class GenAsyncHandler(RequestHandler):
2222
@asynchronous
23-
@gen.engine
23+
@gen.coroutine
2424
def get(self):
2525
http_client = AsyncHTTPClient()
26-
response = yield gen.Task(http_client.fetch, "http://example.com")
26+
response = yield http_client.fetch("http://example.com")
2727
do_something_with_response(response)
2828
self.render("template.html")
2929
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
3243
started at the same time and run in parallel; a list of results will
3344
be returned when they are all finished::
3445
46+
@gen.coroutine
3547
def get(self):
3648
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)]
3951
4052
For more complicated interfaces, `Task` can be split into two parts:
4153
`Callback` and `Wait`::
4254
4355
class GenAsyncHandler2(RequestHandler):
4456
@asynchronous
45-
@gen.engine
57+
@gen.coroutine
4658
def get(self):
4759
http_client = AsyncHTTPClient()
4860
http_client.fetch("http://example.com",
@@ -96,17 +108,21 @@ class ReturnValueIgnoredError(Exception):
96108

97109

98110
def engine(func):
99-
"""Decorator for asynchronous generators.
111+
"""Callback-oriented decorator for asynchronous generators.
100112
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.
110126
"""
111127
@functools.wraps(func)
112128
def wrapper(*args, **kwargs):
@@ -146,19 +162,32 @@ def final_callback(value):
146162

147163

148164
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.
156166
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.
162191
163192
From the caller's perspective, ``@gen.coroutine`` is similar to
164193
the combination of ``@return_future`` and ``@gen.engine``.
@@ -205,13 +234,36 @@ def final_callback(value):
205234

206235

207236
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+
"""
208256
def __init__(self, value=None):
209257
super(Return, self).__init__()
210258
self.value = value
211259

212260

213261
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+
"""
215267
def start(self, runner):
216268
"""Called by the runner after the generator has yielded.
217269
@@ -277,7 +329,7 @@ def get_result(self):
277329

278330

279331
class WaitAll(YieldPoint):
280-
"""Returns the results of multiple previous `Callbacks`.
332+
"""Returns the results of multiple previous `Callbacks <Callback>`.
281333
282334
The argument is a sequence of `Callback` keys, and the result is
283335
a list of results in the same order.

0 commit comments

Comments
 (0)