Make many doc code blocks into sphinx doctests.
This commit is contained in:
parent
0f1c6eec62
commit
b93b8405b3
|
@ -1,6 +1,8 @@
|
|||
.PHONY: all
|
||||
all: sphinx
|
||||
|
||||
# No -W for doctests because that disallows tests with empty output.
|
||||
SPHINX_DOCTEST_OPTS=-n -d build/doctress .
|
||||
SPHINXOPTS=-n -W -d build/doctrees .
|
||||
|
||||
.PHONY: sphinx
|
||||
|
@ -25,7 +27,7 @@ pdf: latex
|
|||
|
||||
.PHONY: doctest
|
||||
doctest:
|
||||
sphinx-build -b doctest $(SPHINXOPTS) build/doctest
|
||||
sphinx-build -b doctest $(SPHINX_DOCTEST_OPTS) build/doctest
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
``tornado.auth`` --- Third-party login with OpenID and OAuth
|
||||
============================================================
|
||||
|
||||
.. testsetup::
|
||||
|
||||
import tornado.auth, tornado.gen, tornado.web
|
||||
|
||||
.. automodule:: tornado.auth
|
||||
|
||||
Common protocols
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
``tornado.concurrent`` --- Work with threads and futures
|
||||
========================================================
|
||||
|
||||
.. testsetup::
|
||||
|
||||
from tornado.concurrent import *
|
||||
from tornado import gen
|
||||
|
||||
.. automodule:: tornado.concurrent
|
||||
:members:
|
||||
:exclude-members: Future, TracebackFuture
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
``tornado.gen`` --- Simplify asynchronous code
|
||||
==============================================
|
||||
|
||||
.. testsetup::
|
||||
|
||||
from tornado.web import *
|
||||
from tornado import gen
|
||||
|
||||
.. automodule:: tornado.gen
|
||||
|
||||
Decorators
|
||||
|
|
|
@ -60,7 +60,9 @@ things asynchronous).
|
|||
Examples
|
||||
~~~~~~~~
|
||||
|
||||
Here is a sample synchronous function::
|
||||
Here is a sample synchronous function:
|
||||
|
||||
.. testcode::
|
||||
|
||||
from tornado.httpclient import HTTPClient
|
||||
|
||||
|
@ -69,8 +71,13 @@ Here is a sample synchronous function::
|
|||
response = http_client.fetch(url)
|
||||
return response.body
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
And here is the same function rewritten to be asynchronous with a
|
||||
callback argument::
|
||||
callback argument:
|
||||
|
||||
.. testcode::
|
||||
|
||||
from tornado.httpclient import AsyncHTTPClient
|
||||
|
||||
|
@ -80,7 +87,12 @@ callback argument::
|
|||
callback(response.body)
|
||||
http_client.fetch(url, callback=handle_response)
|
||||
|
||||
And again with a `.Future` instead of a callback::
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
And again with a `.Future` instead of a callback:
|
||||
|
||||
.. testcode::
|
||||
|
||||
from tornado.concurrent import Future
|
||||
|
||||
|
@ -92,6 +104,9 @@ And again with a `.Future` instead of a callback::
|
|||
lambda f: my_future.set_result(f.result()))
|
||||
return my_future
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
The raw `.Future` version is more complex, but ``Futures`` are
|
||||
nonetheless recommended practice in Tornado because they have two
|
||||
major advantages. Error handling is more consistent since the
|
||||
|
@ -100,7 +115,9 @@ the ad-hoc error handling common in callback-oriented interfaces), and
|
|||
``Futures`` lend themselves well to use with coroutines. Coroutines
|
||||
will be discussed in depth in the next section of this guide. Here is
|
||||
the coroutine version of our sample function, which is very similar to
|
||||
the original synchronous version::
|
||||
the original synchronous version:
|
||||
|
||||
.. testcode::
|
||||
|
||||
from tornado import gen
|
||||
|
||||
|
@ -110,6 +127,9 @@ the original synchronous version::
|
|||
response = yield http_client.fetch(url)
|
||||
raise gen.Return(response.body)
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
The statement ``raise gen.Return(response.body)`` is an artifact of
|
||||
Python 2 (and 3.2), in which generators aren't allowed to return
|
||||
values. To overcome this, Tornado coroutines raise a special kind of
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
Coroutines
|
||||
==========
|
||||
|
||||
.. testsetup::
|
||||
|
||||
from tornado import gen
|
||||
|
||||
**Coroutines** are the recommended way to write asynchronous code in
|
||||
Tornado. Coroutines use the Python ``yield`` keyword to suspend and
|
||||
resume execution instead of a chain of callbacks (cooperative
|
||||
|
@ -65,7 +69,9 @@ Interaction with callbacks
|
|||
|
||||
To interact with asynchronous code that uses callbacks instead of
|
||||
`.Future`, wrap the call in a `.Task`. This will add the callback
|
||||
argument for you and return a `.Future` which you can yield::
|
||||
argument for you and return a `.Future` which you can yield:
|
||||
|
||||
.. testcode::
|
||||
|
||||
@gen.coroutine
|
||||
def call_task():
|
||||
|
@ -74,6 +80,9 @@ argument for you and return a `.Future` which you can yield::
|
|||
# some_function(other_args, callback=callback)
|
||||
yield gen.Task(some_function, other_args)
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Calling blocking functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -91,7 +100,9 @@ Parallelism
|
|||
^^^^^^^^^^^
|
||||
|
||||
The coroutine decorator recognizes lists and dicts whose values are
|
||||
``Futures``, and waits for all of those ``Futures`` in parallel::
|
||||
``Futures``, and waits for all of those ``Futures`` in parallel:
|
||||
|
||||
.. testcode::
|
||||
|
||||
@gen.coroutine
|
||||
def parallel_fetch(url1, url2):
|
||||
|
@ -109,11 +120,16 @@ The coroutine decorator recognizes lists and dicts whose values are
|
|||
for url in urls}
|
||||
# responses is a dict {url: HTTPResponse}
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Interleaving
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Sometimes it is useful to save a `.Future` instead of yielding it
|
||||
immediately, so you can start another operation before waiting::
|
||||
immediately, so you can start another operation before waiting:
|
||||
|
||||
.. testcode::
|
||||
|
||||
@gen.coroutine
|
||||
def get(self):
|
||||
|
@ -125,6 +141,9 @@ immediately, so you can start another operation before waiting::
|
|||
fetch_future = self.fetch_next_chunk()
|
||||
yield self.flush()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Looping
|
||||
^^^^^^^
|
||||
|
||||
|
@ -136,7 +155,7 @@ from `Motor <http://motor.readthedocs.org/en/stable/>`_::
|
|||
|
||||
import motor
|
||||
db = motor.MotorClient().test
|
||||
|
||||
|
||||
@gen.coroutine
|
||||
def loop_example(collection):
|
||||
cursor = db.collection.find()
|
||||
|
|
|
@ -4,7 +4,9 @@ Running and deploying
|
|||
Since Tornado supplies its own HTTPServer, running and deploying it is
|
||||
a little different from other Python web frameworks. Instead of
|
||||
configuring a WSGI container to find your application, you write a
|
||||
``main()`` function that starts the server::
|
||||
``main()`` function that starts the server:
|
||||
|
||||
.. testcode::
|
||||
|
||||
def main():
|
||||
app = make_app()
|
||||
|
@ -14,6 +16,9 @@ configuring a WSGI container to find your application, you write a
|
|||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Configure your operating system or process manager to run this program to
|
||||
start the server. Please note that it may be necessary to increase the number
|
||||
of open files per process (to avoid "Too many open files"-Error).
|
||||
|
@ -29,7 +34,9 @@ Typically it is best to run one process per CPU.
|
|||
|
||||
Tornado includes a built-in multi-process mode to start several
|
||||
processes at once. This requires a slight alteration to the standard
|
||||
main function::
|
||||
main function:
|
||||
|
||||
.. testcode::
|
||||
|
||||
def main():
|
||||
app = make_app()
|
||||
|
@ -38,6 +45,9 @@ main function::
|
|||
server.start(0) # forks one process per cpu
|
||||
IOLoop.current().start()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
This is the easiest way to start multiple processes and have them all
|
||||
share the same port, although it has some limitations. First, each
|
||||
child process will have its own IOLoop, so it is important that
|
||||
|
@ -279,7 +289,9 @@ decorator, `.AsyncHTTPClient`, the ``auth`` module, and WebSockets.
|
|||
|
||||
You can convert a Tornado `.Application` to a WSGI application
|
||||
with `tornado.wsgi.WSGIAdapter`. In this example, configure
|
||||
your WSGI container to find the ``application`` object::
|
||||
your WSGI container to find the ``application`` object:
|
||||
|
||||
.. testcode::
|
||||
|
||||
import tornado.web
|
||||
import tornado.wsgi
|
||||
|
@ -293,6 +305,9 @@ your WSGI container to find the ``application`` object::
|
|||
])
|
||||
application = tornado.wsgi.WSGIAdapter(tornado_app)
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
See the `appengine example application
|
||||
<https://github.com/tornadoweb/tornado/tree/stable/demos/appengine>`_ for a
|
||||
full-featured AppEngine app built on Tornado.
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
Authentication and security
|
||||
===========================
|
||||
|
||||
.. testsetup::
|
||||
|
||||
import tornado.web
|
||||
|
||||
Cookies and secure cookies
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can set cookies in the user's browser with the ``set_cookie``
|
||||
method::
|
||||
method:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
|
@ -15,6 +21,9 @@ method::
|
|||
else:
|
||||
self.write("Your cookie was set!")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Cookies are not secure and can easily be modified by clients. If you
|
||||
need to set cookies to, e.g., identify the currently logged in user,
|
||||
you need to sign your cookies to prevent forgery. Tornado supports
|
||||
|
@ -22,17 +31,24 @@ signed cookies with the `~.RequestHandler.set_secure_cookie` and
|
|||
`~.RequestHandler.get_secure_cookie` methods. To use these methods,
|
||||
you need to specify a secret key named ``cookie_secret`` when you
|
||||
create your application. You can pass in application settings as
|
||||
keyword arguments to your application::
|
||||
keyword arguments to your application:
|
||||
|
||||
.. testcode::
|
||||
|
||||
application = tornado.web.Application([
|
||||
(r"/", MainHandler),
|
||||
], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Signed cookies contain the encoded value of the cookie in addition to a
|
||||
timestamp and an `HMAC <http://en.wikipedia.org/wiki/HMAC>`_ signature.
|
||||
If the cookie is old or if the signature doesn't match,
|
||||
``get_secure_cookie`` will return ``None`` just as if the cookie isn't
|
||||
set. The secure version of the example above::
|
||||
set. The secure version of the example above:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
|
@ -42,6 +58,9 @@ set. The secure version of the example above::
|
|||
else:
|
||||
self.write("Your cookie was set!")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Tornado's secure cookies guarantee integrity but not confidentiality.
|
||||
That is, the cookie cannot be modified but its contents can be seen by the
|
||||
user. The ``cookie_secret`` is a symmetric key and must be kept secret --
|
||||
|
@ -69,7 +88,9 @@ To implement user authentication in your application, you need to
|
|||
override the ``get_current_user()`` method in your request handlers to
|
||||
determine the current user based on, e.g., the value of a cookie. Here
|
||||
is an example that lets users log into the application simply by
|
||||
specifying a nickname, which is then saved in a cookie::
|
||||
specifying a nickname, which is then saved in a cookie:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class BaseHandler(tornado.web.RequestHandler):
|
||||
def get_current_user(self):
|
||||
|
@ -99,12 +120,17 @@ specifying a nickname, which is then saved in a cookie::
|
|||
(r"/login", LoginHandler),
|
||||
], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
You can require that the user be logged in using the `Python
|
||||
decorator <http://www.python.org/dev/peps/pep-0318/>`_
|
||||
`tornado.web.authenticated`. If a request goes to a method with this
|
||||
decorator, and the user is not logged in, they will be redirected to
|
||||
``login_url`` (another application setting). The example above could be
|
||||
rewritten::
|
||||
rewritten:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class MainHandler(BaseHandler):
|
||||
@tornado.web.authenticated
|
||||
|
@ -121,6 +147,9 @@ rewritten::
|
|||
(r"/login", LoginHandler),
|
||||
], **settings)
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
If you decorate ``post()`` methods with the ``authenticated``
|
||||
decorator, and the user is not logged in, the server will send a
|
||||
``403`` response. The ``@authenticated`` decorator is simply
|
||||
|
@ -144,7 +173,9 @@ download a user's address book or publish a Twitter message on their
|
|||
behalf.
|
||||
|
||||
Here is an example handler that uses Google for authentication, saving
|
||||
the Google credentials in a cookie for later access::
|
||||
the Google credentials in a cookie for later access:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class GoogleHandler(tornado.web.RequestHandler, tornado.auth.GoogleMixin):
|
||||
@tornado.web.asynchronous
|
||||
|
@ -160,6 +191,9 @@ the Google credentials in a cookie for later access::
|
|||
return
|
||||
# Save the user with, e.g., set_secure_cookie()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
See the `tornado.auth` module documentation for more details.
|
||||
|
||||
.. _xsrf:
|
||||
|
@ -181,7 +215,9 @@ value in the form submission do not match, then the request is likely
|
|||
forged.
|
||||
|
||||
Tornado comes with built-in XSRF protection. To include it in your site,
|
||||
include the application setting ``xsrf_cookies``::
|
||||
include the application setting ``xsrf_cookies``:
|
||||
|
||||
.. testcode::
|
||||
|
||||
settings = {
|
||||
"cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
|
||||
|
@ -193,6 +229,9 @@ include the application setting ``xsrf_cookies``::
|
|||
(r"/login", LoginHandler),
|
||||
], **settings)
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
If ``xsrf_cookies`` is set, the Tornado web application will set the
|
||||
``_xsrf`` cookie for all users and reject all ``POST``, ``PUT``, and
|
||||
``DELETE`` requests that do not contain a correct ``_xsrf`` value. If
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
.. currentmodule:: tornado.web
|
||||
|
||||
.. testsetup::
|
||||
|
||||
import tornado.web
|
||||
|
||||
Structure of a Tornado web application
|
||||
======================================
|
||||
|
||||
|
@ -8,7 +12,9 @@ A Tornado web application generally consists of one or more
|
|||
routes incoming requests to handlers, and a ``main()`` function
|
||||
to start the server.
|
||||
|
||||
A minimal "hello world" example looks something like this::
|
||||
A minimal "hello world" example looks something like this:
|
||||
|
||||
.. testcode::
|
||||
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado.web import RequestHandler, Application, url
|
||||
|
@ -27,6 +33,9 @@ A minimal "hello world" example looks something like this::
|
|||
app.listen(8888)
|
||||
IOLoop.current().start()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
The ``Application`` object
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -109,7 +118,7 @@ Request data in the formats used by HTML forms will be parsed for you
|
|||
and is made available in methods like `~.RequestHandler.get_query_argument`
|
||||
and `~.RequestHandler.get_body_argument`.
|
||||
|
||||
::
|
||||
.. testcode::
|
||||
|
||||
class MyFormHandler(RequestHandler):
|
||||
def get(self):
|
||||
|
@ -122,6 +131,9 @@ and `~.RequestHandler.get_body_argument`.
|
|||
self.set_header("Content-Type", "text/plain")
|
||||
self.write("You wrote " + self.get_body_argument("message"))
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Since the HTML form encoding is ambiguous as to whether an argument is
|
||||
a single value or a list with one element, `.RequestHandler` has
|
||||
distinct methods to allow the application to indicate whether or not
|
||||
|
@ -302,7 +314,9 @@ to ensure that this method is called, or else the user's browser will
|
|||
simply hang.
|
||||
|
||||
Here is an example that makes a call to the FriendFeed API using
|
||||
Tornado's built-in `.AsyncHTTPClient`::
|
||||
Tornado's built-in `.AsyncHTTPClient`:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
|
@ -318,12 +332,17 @@ Tornado's built-in `.AsyncHTTPClient`::
|
|||
"from the FriendFeed API")
|
||||
self.finish()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
When ``get()`` returns, the request has not finished. When the HTTP
|
||||
client eventually calls ``on_response()``, the request is still open,
|
||||
and the response is finally flushed to the client with the call to
|
||||
``self.finish()``.
|
||||
|
||||
For comparison, here is the same example using a coroutine::
|
||||
For comparison, here is the same example using a coroutine:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler):
|
||||
@tornado.gen.coroutine
|
||||
|
@ -334,6 +353,9 @@ For comparison, here is the same example using a coroutine::
|
|||
self.write("Fetched " + str(len(json["entries"])) + " entries "
|
||||
"from the FriendFeed API")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
For a more advanced asynchronous example, take a look at the `chat
|
||||
example application
|
||||
<https://github.com/tornadoweb/tornado/tree/stable/demos/chat>`_, which
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
Templates and UI
|
||||
================
|
||||
|
||||
.. testsetup::
|
||||
|
||||
import tornado.web
|
||||
|
||||
Tornado includes a simple, fast, and flexible templating language.
|
||||
This section describes that language as well as related issues
|
||||
such as internationalization.
|
||||
|
@ -51,13 +55,16 @@ Python control sequences and expressions embedded within the markup::
|
|||
If you saved this template as "template.html" and put it in the same
|
||||
directory as your Python file, you could render this template with:
|
||||
|
||||
::
|
||||
.. testcode::
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
items = ["Item 1", "Item 2", "Item 3"]
|
||||
self.render("template.html", title="My title", items=items)
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Tornado templates support *control statements* and *expressions*.
|
||||
Control statements are surrounded by ``{%`` and ``%}``, e.g.,
|
||||
``{% if len(items) > 2 %}``. Expressions are surrounded by ``{{`` and
|
||||
|
@ -178,7 +185,9 @@ By default, we detect the user's locale using the ``Accept-Language``
|
|||
header sent by the user's browser. We choose ``en_US`` if we can't find
|
||||
an appropriate ``Accept-Language`` value. If you let user's set their
|
||||
locale as a preference, you can override this default locale selection
|
||||
by overriding `.RequestHandler.get_user_locale`::
|
||||
by overriding `.RequestHandler.get_user_locale`:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class BaseHandler(tornado.web.RequestHandler):
|
||||
def get_current_user(self):
|
||||
|
@ -192,6 +201,9 @@ by overriding `.RequestHandler.get_user_locale`::
|
|||
return None
|
||||
return self.current_user.prefs["locale"]
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
If ``get_user_locale`` returns ``None``, we fall back on the
|
||||
``Accept-Language`` header.
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
``tornado.web`` --- ``RequestHandler`` and ``Application`` classes
|
||||
==================================================================
|
||||
|
||||
.. testsetup::
|
||||
|
||||
from tornado.web import *
|
||||
|
||||
.. automodule:: tornado.web
|
||||
|
||||
Request handlers
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
``tornado.websocket`` --- Bidirectional communication to the browser
|
||||
====================================================================
|
||||
|
||||
.. testsetup::
|
||||
|
||||
import tornado.websocket
|
||||
|
||||
.. automodule:: tornado.websocket
|
||||
|
||||
.. autoclass:: WebSocketHandler
|
||||
|
|
|
@ -32,7 +32,9 @@ They all take slightly different arguments due to the fact all these
|
|||
services implement authentication and authorization slightly differently.
|
||||
See the individual service classes below for complete documentation.
|
||||
|
||||
Example usage for Google OpenID::
|
||||
Example usage for Google OpenID:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class GoogleOAuth2LoginHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.GoogleOAuth2Mixin):
|
||||
|
@ -51,6 +53,10 @@ Example usage for Google OpenID::
|
|||
response_type='code',
|
||||
extra_params={'approval_prompt': 'auto'})
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
|
||||
.. versionchanged:: 4.0
|
||||
All of the callback interfaces in this module are now guaranteed
|
||||
to run their callback with an argument of ``None`` on error.
|
||||
|
@ -630,7 +636,9 @@ class TwitterMixin(OAuthMixin):
|
|||
URL you registered as your application's callback URL.
|
||||
|
||||
When your application is set up, you can use this mixin like this
|
||||
to authenticate the user with Twitter and get access to their stream::
|
||||
to authenticate the user with Twitter and get access to their stream:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class TwitterLoginHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.TwitterMixin):
|
||||
|
@ -642,6 +650,9 @@ class TwitterMixin(OAuthMixin):
|
|||
else:
|
||||
yield self.authorize_redirect()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
The user object returned by `~OAuthMixin.get_authenticated_user`
|
||||
includes the attributes ``username``, ``name``, ``access_token``,
|
||||
and all of the custom Twitter user attributes described at
|
||||
|
@ -690,7 +701,9 @@ class TwitterMixin(OAuthMixin):
|
|||
`~OAuthMixin.get_authenticated_user`. The user returned through that
|
||||
process includes an 'access_token' attribute that can be used
|
||||
to make authenticated requests via this method. Example
|
||||
usage::
|
||||
usage:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.TwitterMixin):
|
||||
|
@ -707,6 +720,9 @@ class TwitterMixin(OAuthMixin):
|
|||
return
|
||||
self.finish("Posted a message!")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
"""
|
||||
if path.startswith('http:') or path.startswith('https:'):
|
||||
# Raw urls are useful for e.g. search which doesn't follow the
|
||||
|
@ -769,7 +785,9 @@ class FriendFeedMixin(OAuthMixin):
|
|||
for the URL you registered as your application's Callback URL.
|
||||
|
||||
When your application is set up, you can use this mixin like this
|
||||
to authenticate the user with FriendFeed and get access to their feed::
|
||||
to authenticate the user with FriendFeed and get access to their feed:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class FriendFeedLoginHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.FriendFeedMixin):
|
||||
|
@ -781,6 +799,10 @@ class FriendFeedMixin(OAuthMixin):
|
|||
else:
|
||||
yield self.authorize_redirect()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
|
||||
The user object returned by `~OAuthMixin.get_authenticated_user()` includes the
|
||||
attributes ``username``, ``name``, and ``description`` in addition to
|
||||
``access_token``. You should save the access token with the user;
|
||||
|
@ -812,7 +834,9 @@ class FriendFeedMixin(OAuthMixin):
|
|||
can be used to make authenticated requests via this
|
||||
method.
|
||||
|
||||
Example usage::
|
||||
Example usage:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.FriendFeedMixin):
|
||||
|
@ -830,6 +854,9 @@ class FriendFeedMixin(OAuthMixin):
|
|||
return
|
||||
self.finish("Posted a message!")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
"""
|
||||
# Add the OAuth resource request signature if we have credentials
|
||||
url = "http://friendfeed-api.com/v2" + path
|
||||
|
@ -901,7 +928,9 @@ class GoogleMixin(OpenIdMixin, OAuthMixin):
|
|||
the values for the user, including ``email``, ``name``, and
|
||||
``locale``.
|
||||
|
||||
Example usage::
|
||||
Example usage:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class GoogleLoginHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.GoogleMixin):
|
||||
|
@ -912,6 +941,10 @@ class GoogleMixin(OpenIdMixin, OAuthMixin):
|
|||
# Save the user with e.g. set_secure_cookie()
|
||||
else:
|
||||
yield self.authenticate_redirect()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
"""
|
||||
_OPENID_ENDPOINT = "https://www.google.com/accounts/o8/ud"
|
||||
_OAUTH_ACCESS_TOKEN_URL = "https://www.google.com/accounts/OAuthGetAccessToken"
|
||||
|
@ -1002,7 +1035,9 @@ class GoogleOAuth2Mixin(OAuth2Mixin):
|
|||
def get_authenticated_user(self, redirect_uri, code, callback):
|
||||
"""Handles the login for the Google user, returning a user object.
|
||||
|
||||
Example usage::
|
||||
Example usage:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class GoogleOAuth2LoginHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.GoogleOAuth2Mixin):
|
||||
|
@ -1020,6 +1055,10 @@ class GoogleOAuth2Mixin(OAuth2Mixin):
|
|||
scope=['profile', 'email'],
|
||||
response_type='code',
|
||||
extra_params={'approval_prompt': 'auto'})
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
"""
|
||||
http = self.get_auth_http_client()
|
||||
body = urllib_parse.urlencode({
|
||||
|
@ -1066,7 +1105,9 @@ class FacebookMixin(object):
|
|||
``facebook_api_key`` and ``facebook_secret``.
|
||||
|
||||
When your application is set up, you can use this mixin like this
|
||||
to authenticate the user with Facebook::
|
||||
to authenticate the user with Facebook:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class FacebookHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.FacebookMixin):
|
||||
|
@ -1082,6 +1123,9 @@ class FacebookMixin(object):
|
|||
raise tornado.web.HTTPError(500, "Facebook auth failed")
|
||||
# Save the user using, e.g., set_secure_cookie()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
The user object returned by `get_authenticated_user` includes the
|
||||
attributes ``facebook_uid`` and ``name`` in addition to session attributes
|
||||
like ``session_key``. You should save the session key with the user; it is
|
||||
|
@ -1176,7 +1220,9 @@ class FacebookMixin(object):
|
|||
The available Facebook methods are documented here:
|
||||
http://wiki.developers.facebook.com/index.php/API
|
||||
|
||||
Here is an example for the stream.get() method::
|
||||
Here is an example for the stream.get() method:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.FacebookMixin):
|
||||
|
@ -1195,6 +1241,9 @@ class FacebookMixin(object):
|
|||
return
|
||||
self.render("stream.html", stream=stream)
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
"""
|
||||
self.require_setting("facebook_api_key", "Facebook Connect")
|
||||
self.require_setting("facebook_secret", "Facebook Connect")
|
||||
|
@ -1275,9 +1324,12 @@ class FacebookGraphMixin(OAuth2Mixin):
|
|||
code, callback, extra_fields=None):
|
||||
"""Handles the login for the Facebook user, returning a user object.
|
||||
|
||||
Example usage::
|
||||
Example usage:
|
||||
|
||||
class FacebookGraphLoginHandler(LoginHandler, tornado.auth.FacebookGraphMixin):
|
||||
.. testcode::
|
||||
|
||||
class FacebookGraphLoginHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.FacebookGraphMixin):
|
||||
@tornado.gen.coroutine
|
||||
def get(self):
|
||||
if self.get_argument("code", False):
|
||||
|
@ -1292,6 +1344,10 @@ class FacebookGraphMixin(OAuth2Mixin):
|
|||
redirect_uri='/auth/facebookgraph/',
|
||||
client_id=self.settings["facebook_api_key"],
|
||||
extra_params={"scope": "read_stream,offline_access"})
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
"""
|
||||
http = self.get_auth_http_client()
|
||||
args = {
|
||||
|
@ -1359,7 +1415,9 @@ class FacebookGraphMixin(OAuth2Mixin):
|
|||
process includes an ``access_token`` attribute that can be
|
||||
used to make authenticated requests via this method.
|
||||
|
||||
Example usage::
|
||||
Example usage:
|
||||
|
||||
..testcode::
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler,
|
||||
tornado.auth.FacebookGraphMixin):
|
||||
|
@ -1377,6 +1435,9 @@ class FacebookGraphMixin(OAuth2Mixin):
|
|||
return
|
||||
self.finish("Posted a message!")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
The given path is relative to ``self._FACEBOOK_BASE_URL``,
|
||||
by default "https://graph.facebook.com".
|
||||
|
||||
|
|
|
@ -379,7 +379,9 @@ def return_future(f):
|
|||
wait for the function to complete (perhaps by yielding it in a
|
||||
`.gen.engine` function, or passing it to `.IOLoop.add_future`).
|
||||
|
||||
Usage::
|
||||
Usage:
|
||||
|
||||
.. testcode::
|
||||
|
||||
@return_future
|
||||
def future_func(arg1, arg2, callback):
|
||||
|
@ -391,6 +393,8 @@ def return_future(f):
|
|||
yield future_func(arg1, arg2)
|
||||
callback()
|
||||
|
||||
..
|
||||
|
||||
Note that ``@return_future`` and ``@gen.engine`` can be applied to the
|
||||
same function, provided ``@return_future`` appears first. However,
|
||||
consider using ``@gen.coroutine`` instead of this combination.
|
||||
|
|
|
@ -3,7 +3,9 @@ work in an asynchronous environment. Code using the ``gen`` module
|
|||
is technically asynchronous, but it is written as a single generator
|
||||
instead of a collection of separate functions.
|
||||
|
||||
For example, the following asynchronous handler::
|
||||
For example, the following asynchronous handler:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class AsyncHandler(RequestHandler):
|
||||
@asynchronous
|
||||
|
@ -16,7 +18,12 @@ For example, the following asynchronous handler::
|
|||
do_something_with_response(response)
|
||||
self.render("template.html")
|
||||
|
||||
could be written with ``gen`` as::
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
could be written with ``gen`` as:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class GenAsyncHandler(RequestHandler):
|
||||
@gen.coroutine
|
||||
|
@ -26,12 +33,17 @@ could be written with ``gen`` as::
|
|||
do_something_with_response(response)
|
||||
self.render("template.html")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
Most asynchronous functions in Tornado return a `.Future`;
|
||||
yielding this object returns its `~.Future.result`.
|
||||
|
||||
You can also yield a list or dict of ``Futures``, which will be
|
||||
started at the same time and run in parallel; a list or dict of results will
|
||||
be returned when they are all finished::
|
||||
be returned when they are all finished:
|
||||
|
||||
.. testcode::
|
||||
|
||||
@gen.coroutine
|
||||
def get(self):
|
||||
|
@ -43,6 +55,9 @@ be returned when they are all finished::
|
|||
response3 = response_dict['response3']
|
||||
response4 = response_dict['response4']
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
If the `~functools.singledispatch` library is available (standard in
|
||||
Python 3.4, available via the `singledispatch
|
||||
<https://pypi.python.org/pypi/singledispatch>`_ package on older
|
||||
|
@ -280,20 +295,18 @@ class WaitIterator(object):
|
|||
|
||||
If you need to get the result of each future as soon as possible,
|
||||
or if you need the result of some futures even if others produce
|
||||
errors, you can use ``WaitIterator``:
|
||||
|
||||
::
|
||||
errors, you can use ``WaitIterator``::
|
||||
|
||||
wait_iterator = gen.WaitIterator(future1, future2)
|
||||
while not wait_iterator.done():
|
||||
try:
|
||||
result = yield wait_iterator.next()
|
||||
except Exception as e:
|
||||
print "Error {} from {}".format(e, wait_iterator.current_future)
|
||||
print("Error {} from {}".format(e, wait_iterator.current_future))
|
||||
else:
|
||||
print "Result {} recieved from {} at {}".format(
|
||||
print("Result {} recieved from {} at {}".format(
|
||||
result, wait_iterator.current_future,
|
||||
wait_iterator.current_index)
|
||||
wait_iterator.current_index))
|
||||
|
||||
Because results are returned as soon as they are available the
|
||||
output from the iterator *will not be in the same order as the
|
||||
|
|
|
@ -76,34 +76,40 @@ class IOLoop(Configurable):
|
|||
simultaneous connections, you should use a system that supports
|
||||
either ``epoll`` or ``kqueue``.
|
||||
|
||||
Example usage for a simple TCP server::
|
||||
Example usage for a simple TCP server:
|
||||
|
||||
.. testcode::
|
||||
|
||||
import errno
|
||||
import functools
|
||||
import ioloop
|
||||
import tornado.ioloop
|
||||
import socket
|
||||
|
||||
def connection_ready(sock, fd, events):
|
||||
while True:
|
||||
try:
|
||||
connection, address = sock.accept()
|
||||
except socket.error, e:
|
||||
except socket.error as e:
|
||||
if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN):
|
||||
raise
|
||||
return
|
||||
connection.setblocking(0)
|
||||
handle_connection(connection, address)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.setblocking(0)
|
||||
sock.bind(("", port))
|
||||
sock.listen(128)
|
||||
if __name__ == '__main__':
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.setblocking(0)
|
||||
sock.bind(("", port))
|
||||
sock.listen(128)
|
||||
|
||||
io_loop = ioloop.IOLoop.instance()
|
||||
callback = functools.partial(connection_ready, sock)
|
||||
io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
|
||||
io_loop.start()
|
||||
io_loop = tornado.ioloop.IOLoop.instance()
|
||||
callback = functools.partial(connection_ready, sock)
|
||||
io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
|
||||
io_loop.start()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
"""
|
||||
# Constants from the epoll module
|
||||
|
|
|
@ -928,7 +928,9 @@ class IOStream(BaseIOStream):
|
|||
connected before passing it to the `IOStream` or connected with
|
||||
`IOStream.connect`.
|
||||
|
||||
A very simple (and broken) HTTP client using this class::
|
||||
A very simple (and broken) HTTP client using this class:
|
||||
|
||||
.. testcode::
|
||||
|
||||
import tornado.ioloop
|
||||
import tornado.iostream
|
||||
|
@ -947,14 +949,19 @@ class IOStream(BaseIOStream):
|
|||
stream.read_bytes(int(headers[b"Content-Length"]), on_body)
|
||||
|
||||
def on_body(data):
|
||||
print data
|
||||
print(data)
|
||||
stream.close()
|
||||
tornado.ioloop.IOLoop.instance().stop()
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
||||
stream = tornado.iostream.IOStream(s)
|
||||
stream.connect(("friendfeed.com", 80), send_request)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
if __name__ == '__main__':
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
||||
stream = tornado.iostream.IOStream(s)
|
||||
stream.connect(("friendfeed.com", 80), send_request)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
"""
|
||||
def __init__(self, socket, *args, **kwargs):
|
||||
self.socket = socket
|
||||
|
|
|
@ -19,7 +19,9 @@ features that allow it to scale to large numbers of open connections,
|
|||
making it ideal for `long polling
|
||||
<http://en.wikipedia.org/wiki/Push_technology#Long_polling>`_.
|
||||
|
||||
Here is a simple "Hello, world" example app::
|
||||
Here is a simple "Hello, world" example app:
|
||||
|
||||
.. testcode::
|
||||
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
|
@ -35,6 +37,10 @@ Here is a simple "Hello, world" example app::
|
|||
application.listen(8888)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
|
||||
See the :doc:`guide` for additional information.
|
||||
|
||||
Thread-safety notes
|
||||
|
@ -1470,10 +1476,12 @@ def asynchronous(method):
|
|||
method returns. It is up to the request handler to call
|
||||
`self.finish() <RequestHandler.finish>` to finish the HTTP
|
||||
request. Without this decorator, the request is automatically
|
||||
finished when the ``get()`` or ``post()`` method returns. Example::
|
||||
finished when the ``get()`` or ``post()`` method returns. Example:
|
||||
|
||||
class MyRequestHandler(web.RequestHandler):
|
||||
@web.asynchronous
|
||||
.. testcode::
|
||||
|
||||
class MyRequestHandler(RequestHandler):
|
||||
@asynchronous
|
||||
def get(self):
|
||||
http = httpclient.AsyncHTTPClient()
|
||||
http.fetch("http://friendfeed.com/", self._on_download)
|
||||
|
@ -1482,6 +1490,9 @@ def asynchronous(method):
|
|||
self.write("Downloaded!")
|
||||
self.finish()
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
.. versionadded:: 3.1
|
||||
The ability to use ``@gen.coroutine`` without ``@asynchronous``.
|
||||
"""
|
||||
|
|
|
@ -74,17 +74,22 @@ class WebSocketHandler(tornado.web.RequestHandler):
|
|||
http://tools.ietf.org/html/rfc6455.
|
||||
|
||||
Here is an example WebSocket handler that echos back all received messages
|
||||
back to the client::
|
||||
back to the client:
|
||||
|
||||
class EchoWebSocket(websocket.WebSocketHandler):
|
||||
.. testcode::
|
||||
|
||||
class EchoWebSocket(tornado.websocket.WebSocketHandler):
|
||||
def open(self):
|
||||
print "WebSocket opened"
|
||||
print("WebSocket opened")
|
||||
|
||||
def on_message(self, message):
|
||||
self.write_message(u"You said: " + message)
|
||||
|
||||
def on_close(self):
|
||||
print "WebSocket closed"
|
||||
print("WebSocket closed")
|
||||
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
WebSockets are not standard HTTP connections. The "handshake" is
|
||||
HTTP, but after the handshake, the protocol is
|
||||
|
|
5
tox.ini
5
tox.ini
|
@ -155,11 +155,12 @@ commands =
|
|||
[testenv:py2-sphinx-doctest]
|
||||
changedir = docs
|
||||
setenv = TORNADO_EXTENSION=0
|
||||
# No -W for doctests because that disallows tests with empty output.
|
||||
commands =
|
||||
sphinx-build -q -E -n -W -b doctest . {envtmpdir}/doctest
|
||||
sphinx-build -q -E -n -b doctest . {envtmpdir}/doctest
|
||||
|
||||
[testenv:py3-sphinx-doctest]
|
||||
changedir = docs
|
||||
setenv = TORNADO_EXTENSION=0
|
||||
commands =
|
||||
sphinx-build -q -E -n -W -b doctest . {envtmpdir}/doctest
|
||||
sphinx-build -q -E -n -b doctest . {envtmpdir}/doctest
|
||||
|
|
Loading…
Reference in New Issue