]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Make many doc code blocks into sphinx doctests.
authorBen Darnell <ben@bendarnell.com>
Mon, 9 Feb 2015 05:19:47 +0000 (00:19 -0500)
committerBen Darnell <ben@bendarnell.com>
Mon, 9 Feb 2015 05:19:47 +0000 (00:19 -0500)
20 files changed:
docs/Makefile
docs/auth.rst
docs/concurrent.rst
docs/gen.rst
docs/guide/async.rst
docs/guide/coroutines.rst
docs/guide/running.rst
docs/guide/security.rst
docs/guide/structure.rst
docs/guide/templates.rst
docs/web.rst
docs/websocket.rst
tornado/auth.py
tornado/concurrent.py
tornado/gen.py
tornado/ioloop.py
tornado/iostream.py
tornado/web.py
tornado/websocket.py
tox.ini

index b156c257dd0cbf93dea091defe6bba2e051a7a12..7001b801f634e3fff7ff589fcfcb6410eb693af5 100644 (file)
@@ -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
index dd2ca554f617d2dbfb44e7f9c450f09ff2e1dfb4..b2dad547f52ddf08e50e79cb722a36aad805b975 100644 (file)
@@ -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
index 051c91ab11a41db9ff52d4540d13a641b7a46ed3..72047811b5b04bb77d7e36c401c08e1de5d0d156 100644 (file)
@@ -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
index a6f81ed104cf1cd66efe385605e85a935f25e555..8a3c99b9b09c8ee14ae5aeddd9420965b1bf7a5a 100644 (file)
@@ -1,6 +1,11 @@
 ``tornado.gen`` --- Simplify asynchronous code
 ==============================================
 
+.. testsetup::
+
+   from tornado.web import *
+   from tornado import gen
+
 .. automodule:: tornado.gen
 
    Decorators
index 7430031f345f93cfeec159ac62da6df7592f9ed4..6a3e26bb525d0418265efec950c3389ccadc8433 100644 (file)
@@ -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
index f10dda302031e9d3449ed8b9628c4cdaad6ebbdb..a3fed1fb2b090fa1a8d761a9a7044c6be511879e 100644 (file)
@@ -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()
index 7ae3f4ff8ee4aa9dc054b781ce2c347766c2a35e..7b1b415f1ad6e102cd6673d7786ba1528a278835 100644 (file)
@@ -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.
index 57a1cba7f2b239662595f28755a5a81ea1799ced..793fb0500638a4122d0e2566612e0b18e75a393f 100644 (file)
@@ -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
index e83a3f30aff23242c417efbe9ef43a93231def4e..57d7b82a416b6b6cbbc4d5ab5ad60648d278ac44 100644 (file)
@@ -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
index 2156ad6c1e045243f1b2adc2b38c4a99deaa3aea..8755d25e13aa3d58d7f4e5d7edcebe5766cc500e 100644 (file)
@@ -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.
 
index 360d21afe3cfe4170abaebcd8d48e375162f0660..0845641c7302cff4dde3cbf3f08a004d597ca1c2 100644 (file)
@@ -1,6 +1,10 @@
 ``tornado.web`` --- ``RequestHandler`` and ``Application`` classes
 ==================================================================
 
+.. testsetup::
+
+   from tornado.web import *
+
 .. automodule:: tornado.web
 
    Request handlers
index d5e7921bca274c5d60b9e99a6142fd20aa62e1f3..b3596d4cf49358d47b188540347a8667606a77c6 100644 (file)
@@ -1,6 +1,10 @@
 ``tornado.websocket`` --- Bidirectional communication to the browser
 ====================================================================
 
+.. testsetup::
+
+   import tornado.websocket
+
 .. automodule:: tornado.websocket
 
    .. autoclass:: WebSocketHandler
index 00c833aa231a9321152c5ea361289412fd7d2fdb..85f967cb0f4732091ee11aaef3d6c3254a7f9912 100644 (file)
@@ -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:
+
+        .. testcode::
 
-            class FacebookGraphLoginHandler(LoginHandler, tornado.auth.FacebookGraphMixin):
+            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".
 
index a3f15dcde99beee04faf3d5cd2639f4a54ce93f5..4b0a7804e826d928db3aa4d178b1828ee904ffcf 100644 (file)
@@ -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.
index f59f0d893a270722198999dd9e5999aa373e1622..3d87d7177c12ad16f1365bf1ce80c9a2ac04974f 100644 (file)
@@ -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
index 680dc4016a40f7bfea92a631ddfd8767aca5e6df..775bfba8c575c4ca950ea1435ea9bba028745576 100644 (file)
@@ -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 = tornado.ioloop.IOLoop.instance()
+            callback = functools.partial(connection_ready, sock)
+            io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
+            io_loop.start()
 
-        io_loop = 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
index 371a02d3e86e305403e3681b300f6bbb4a942762..6d039a4ad3406c0877e26e960d4430f1530a6424 100644 (file)
@@ -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
index c1614db71cba2f44186e0e4a208d65987b1837a3..c8802733a143dd223bc533f3af485ce9552d728c 100644 (file)
@@ -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``.
     """
index 7c7866369bc014be14a868a0dace1eff626d1e53..b6994942a8958c08492e2b1380803c495b365492 100644 (file)
@@ -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
diff --git a/tox.ini b/tox.ini
index 1055b234dbfa41818e99b3f426ac4a8885aa82c2..2e227c4759c7851aad936d250663c28ff5fa6955 100644 (file)
--- a/tox.ini
+++ b/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