-Asynchronous and non-Blocking
------------------------------
+Asynchronous and non-Blocking I/O
+---------------------------------
Real-time web features require a long-lived mostly-idle connection per
user. In a traditional synchronous web server, this implies devoting
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::
+
+ def main():
+ app = make_app()
+ app.listen(8888)
+ IOLoop.current().start()
+
+ if __name__ == '__main__':
+ main()
+
+Configure your operating system or process manager to run this program to
+start the server.
+
+Processes and ports
+~~~~~~~~~~~~~~~~~~~
+
+Due to the Python GIL (Global Interpreter Lock), it is necessary to run
+multiple Python processes to take full advantage of multi-CPU machines.
+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::
+
+ def main():
+ app = make_app()
+ server = tornado.httpserver.HTTPServer(app)
+ server.bind(8888)
+ server.start(0) # forks one process per cpu
+ IOLoop.current().start()
+
+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
+nothing touch the global IOLoop instance (even indirectly) before the
+fork. Second, it is difficult to do zero-downtime updates in this model.
+Finally, since all the processes share the same port it is more difficult
+to monitor them individually.
+
+For more sophisticated deployments, it is recommended to start the processes
+independently, and have each one listen on a different port.
+The "process groups" feature of `supervisord <http://www.supervisord.org>`_
+is one good way to arrange this. When each process uses a different port,
+an external load balancer such as HAProxy or nginx is usually needed
+to present a single address to outside visitors.
+
+
+Running behind a load balancer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When running behind a load balancer like nginx, it is recommended to
+pass ``xheaders=True`` to the `.HTTPServer` constructor. This will tell
+Tornado to use headers like ``X-Real-IP`` to get the user's IP address
+instead of attributing all traffic to the balancer's IP address.
+
+This is a barebones nginx config file that is structurally similar to
+the one we use at FriendFeed. It assumes nginx and the Tornado servers
+are running on the same machine, and the four Tornado servers are
+running on ports 8000 - 8003::
+
+ user nginx;
+ worker_processes 1;
+
+ error_log /var/log/nginx/error.log;
+ pid /var/run/nginx.pid;
+
+ events {
+ worker_connections 1024;
+ use epoll;
+ }
+
+ http {
+ # Enumerate all the Tornado servers here
+ upstream frontends {
+ server 127.0.0.1:8000;
+ server 127.0.0.1:8001;
+ server 127.0.0.1:8002;
+ server 127.0.0.1:8003;
+ }
+
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ access_log /var/log/nginx/access.log;
+
+ keepalive_timeout 65;
+ proxy_read_timeout 200;
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ gzip on;
+ gzip_min_length 1000;
+ gzip_proxied any;
+ gzip_types text/plain text/html text/css text/xml
+ application/x-javascript application/xml
+ application/atom+xml text/javascript;
+
+ # Only retry if there was a communication error, not a timeout
+ # on the Tornado server (to avoid propagating "queries of death"
+ # to all frontends)
+ proxy_next_upstream error;
+
+ server {
+ listen 80;
+
+ # Allow file uploads
+ client_max_body_size 50M;
+
+ location ^~ /static/ {
+ root /var/www;
+ if ($query_string) {
+ expires max;
+ }
+ }
+ location = /favicon.ico {
+ rewrite (.*) /static/favicon.ico;
+ }
+ location = /robots.txt {
+ rewrite (.*) /static/robots.txt;
+ }
+
+ location / {
+ proxy_pass_header Server;
+ proxy_set_header Host $http_host;
+ proxy_redirect off;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Scheme $scheme;
+ proxy_pass http://frontends;
+ }
+ }
+ }
+
Static files and aggressive file caching
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can serve static files from Tornado by specifying the
-``static_path`` setting in your application:
-
-::
+``static_path`` setting in your application::
settings = {
"static_path": os.path.join(os.path.dirname(__file__), "static"),
This setting will automatically make all requests that start with
``/static/`` serve from that static directory, e.g.,
-`http://localhost:8888/static/foo.png <http://localhost:8888/static/foo.png>`_
-will serve the file ``foo.png`` from the specified static directory. We
-also automatically serve ``/robots.txt`` and ``/favicon.ico`` from the
-static directory (even though they don't start with the ``/static/``
-prefix).
+``http://localhost:8888/static/foo.png`` will serve the file
+``foo.png`` from the specified static directory. We also automatically
+serve ``/robots.txt`` and ``/favicon.ico`` from the static directory
+(even though they don't start with the ``/static/`` prefix).
In the above settings, we have explicitly configured Tornado to serve
-``apple-touch-icon.png`` “from” the root with the ``StaticFileHandler``,
+``apple-touch-icon.png`` from the root with the `.StaticFileHandler`,
though it is physically in the static file directory. (The capturing
group in that regular expression is necessary to tell
-``StaticFileHandler`` the requested filename; capturing groups are
-passed to handlers as method arguments.) You could do the same thing to
-serve e.g. ``sitemap.xml`` from the site root. Of course, you can also
-avoid faking a root ``apple-touch-icon.png`` by using the appropriate
-``<link />`` tag in your HTML.
+`.StaticFileHandler` the requested filename; recall that capturing
+groups are passed to handlers as method arguments.) You could do the
+same thing to serve e.g. ``sitemap.xml`` from the site root. Of
+course, you can also avoid faking a root ``apple-touch-icon.png`` by
+using the appropriate ``<link />`` tag in your HTML.
To improve performance, it is generally a good idea for browsers to
cache static resources aggressively so browsers won't send unnecessary
rendering of the page. Tornado supports this out of the box with *static
content versioning*.
-To use this feature, use the ``static_url()`` method in your templates
-rather than typing the URL of the static file directly in your HTML:
-
-::
+To use this feature, use the `~.RequestHandler.static_url` method in
+your templates rather than typing the URL of the static file directly
+in your HTML::
<html>
<head>
significantly improving rendering performance.
In production, you probably want to serve static files from a more
-optimized static file server like `nginx <http://nginx.net/>`_. You can
-configure most any web server to support these caching semantics. Here
-is the nginx configuration we use at FriendFeed:
-
-::
+optimized static file server like `nginx <http://nginx.net/>`_. You
+can configure most any web server to recognize the version tags used
+by ``static_url()`` and set caching headers accordingly. Here is the
+relevant portion of the nginx configuration we use at FriendFeed::
location /static/ {
root /var/friendfeed/static;
* ``compiled_template_cache=False``: Templates will not be cached.
* ``static_hash_cache=False``: Static file hashes (used by the
``static_url`` function) will not be cached
-* ``serve_traceback=True``: When an exception in a ``RequestHandler``
+* ``serve_traceback=True``: When an exception in a `.RequestHandler`
is not caught, an error page including a stack trace will be
generated.
-Autoreload mode is not compatible with the multi-process mode of ``HTTPServer``.
-You must not give ``HTTPServer.start`` an argument other than 1 (or
+Autoreload mode is not compatible with the multi-process mode of `.HTTPServer`.
+You must not give `HTTPServer.start <.TCPServer.start>` an argument other than 1 (or
call `tornado.process.fork_processes`) if you are using autoreload mode.
The automatic reloading feature of debug mode is available as a
-standalone module in ``tornado.autoreload``. The two can be used in
+standalone module in `tornado.autoreload`. The two can be used in
combination to provide extra robustness against syntax errors: set
``autoreload=True`` within the app to detect changes while it is running,
and start it with ``python -m tornado.autoreload myserver.py`` to catch
any syntax errors or other errors at startup.
Reloading loses any Python interpreter command-line arguments (e.g. ``-u``)
-because it re-executes Python using ``sys.executable`` and ``sys.argv``.
+because it re-executes Python using `sys.executable` and `sys.argv`.
Additionally, modifying these variables will cause reloading to behave
incorrectly.
known to confuse some IDEs.
-Running Tornado in production
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+WSGI and Google App Engine
+~~~~~~~~~~~~~~~~~~~~~~~~~~
-At FriendFeed, we use `nginx <http://nginx.net/>`_ as a load balancer
-and static file server. We run multiple instances of the Tornado web
-server on multiple frontend machines. We typically run one Tornado
-frontend per core on the machine (sometimes more depending on
-utilization).
+Tornado is normally intended to be run on its own, without a WSGI
+container. However, in some environments (such as Google App Engine),
+only WSGI is allowed and applications cannot run their own servers.
+In this case Tornado supports a limited mode of operation that does
+not support asynchronous operation but allows a subset of Tornado's
+functionality in a WSGI-only environment. The features that are
+not allowed in WSGI mode include coroutines, the ``@asynchronous``
+decorator, `.AsyncHTTPClient`, the ``auth`` module, and WebSockets.
-When running behind a load balancer like nginx, it is recommended to
-pass ``xheaders=True`` to the ``HTTPServer`` constructor. This will tell
-Tornado to use headers like ``X-Real-IP`` to get the user's IP address
-instead of attributing all traffic to the balancer's IP address.
-
-This is a barebones nginx config file that is structurally similar to
-the one we use at FriendFeed. It assumes nginx and the Tornado servers
-are running on the same machine, and the four Tornado servers are
-running on ports 8000 - 8003:
-
-::
-
- user nginx;
- worker_processes 1;
-
- error_log /var/log/nginx/error.log;
- pid /var/run/nginx.pid;
-
- events {
- worker_connections 1024;
- use epoll;
- }
-
- http {
- # Enumerate all the Tornado servers here
- upstream frontends {
- server 127.0.0.1:8000;
- server 127.0.0.1:8001;
- server 127.0.0.1:8002;
- server 127.0.0.1:8003;
- }
-
- include /etc/nginx/mime.types;
- default_type application/octet-stream;
-
- access_log /var/log/nginx/access.log;
-
- keepalive_timeout 65;
- proxy_read_timeout 200;
- sendfile on;
- tcp_nopush on;
- tcp_nodelay on;
- gzip on;
- gzip_min_length 1000;
- gzip_proxied any;
- gzip_types text/plain text/html text/css text/xml
- application/x-javascript application/xml
- application/atom+xml text/javascript;
-
- # Only retry if there was a communication error, not a timeout
- # on the Tornado server (to avoid propagating "queries of death"
- # to all frontends)
- proxy_next_upstream error;
-
- server {
- listen 80;
-
- # Allow file uploads
- client_max_body_size 50M;
-
- location ^~ /static/ {
- root /var/www;
- if ($query_string) {
- expires max;
- }
- }
- location = /favicon.ico {
- rewrite (.*) /static/favicon.ico;
- }
- location = /robots.txt {
- rewrite (.*) /static/robots.txt;
- }
-
- location / {
- proxy_pass_header Server;
- proxy_set_header Host $http_host;
- proxy_redirect off;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Scheme $scheme;
- proxy_pass http://frontends;
- }
- }
- }
-
-WSGI and Google AppEngine
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Tornado comes with limited support for `WSGI <http://wsgi.org/>`_.
-However, since WSGI does not support non-blocking requests, you cannot
-use any of the asynchronous/non-blocking features of Tornado in your
-application if you choose to use WSGI instead of Tornado's HTTP server.
-Some of the features that are not available in WSGI applications:
-``@tornado.web.asynchronous``, the ``httpclient`` module, and the
-``auth`` module.
-
-You can create a valid WSGI application from your Tornado request
-handlers by using ``WSGIApplication`` in the ``wsgi`` module instead of
-using ``tornado.web.Application``. Here is an example that uses the
-built-in WSGI ``CGIHandler`` to make a valid `Google
-AppEngine <http://code.google.com/appengine/>`_ application:
-
-::
+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::
import tornado.web
import tornado.wsgi
- import wsgiref.handlers
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
- if __name__ == "__main__":
- application = tornado.wsgi.WSGIApplication([
- (r"/", MainHandler),
- ])
- wsgiref.handlers.CGIHandler().run(application)
+ tornado_app = tornado.web.Application([
+ (r"/", MainHandler),
+ ])
+ application = tornado.wsgi.WSGIAdapter(tornado_app)
See the `appengine example application
<https://github.com/tornadoweb/tornado/tree/stable/demos/appengine>`_ for a
~~~~~~~~~~~~~~~~~~~~~~~~~~
You can set cookies in the user's browser with the ``set_cookie``
-method:
-
-::
+method::
class MainHandler(tornado.web.RequestHandler):
def get(self):
else:
self.write("Your cookie was set!")
-Cookies are easily forged by malicious clients. If you need to set
-cookies to, e.g., save the user ID of the currently logged in user, you
-need to sign your cookies to prevent forgery. Tornado supports this out
-of the box with the ``set_secure_cookie`` and ``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:
-
-::
+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
+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::
application = tornado.web.Application([
(r"/", MainHandler),
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::
class MainHandler(tornado.web.RequestHandler):
def get(self):
else:
self.write("Your cookie was set!")
+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 --
+anyone who obtains the value of this key could produce their own signed
+cookies.
+
+By default, Tornado's secure cookies expire after 30 days. To change this,
+use the ``expires_days`` keyword argument to ``set_secure_cookie`` *and* the
+``max_age_days`` argument to ``set_secure_cookie``. These two values are
+passed separately so that you may e.g. have a cookie that is valid for 30 days
+for most purposes, but for certain sensitive actions (such as changing billing
+information) you use a smaller ``max_age_days`` when reading the cookie.
+
+.. _user-authentication:
+
User authentication
~~~~~~~~~~~~~~~~~~~
The currently authenticated user is available in every request handler
-as ``self.current_user``, and in every template as ``current_user``. By
-default, ``current_user`` is ``None``.
+as `self.current_user <.RequestHandler.current_user>`, and in every
+template as ``current_user``. By default, ``current_user`` is
+``None``.
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::
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
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
+`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::
class MainHandler(BaseHandler):
@tornado.web.authenticated
(r"/login", LoginHandler),
], **settings)
-If you decorate ``post()`` methods with the ``authenticated`` decorator,
-and the user is not logged in, the server will send a ``403`` response.
+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
+shorthand for ``if not self.current_user: self.redirect()`` and may
+not be appropriate for non-browser-based login schemes.
-Tornado comes with built-in support for third-party authentication
-schemes like Google OAuth. See the `tornado.auth`
-for more details. Check out the `Tornado Blog example application <https://github.com/tornadoweb/tornado/tree/stable/demos/blog>`_ for a
+Check out the `Tornado Blog example application
+<https://github.com/tornadoweb/tornado/tree/stable/demos/blog>`_ for a
complete example that uses authentication (and stores user data in a
MySQL database).
+Third party authentication
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The `tornado.auth` module implements the authentication and
+authorization protocols for a number of the most popular sites on the
+web, including Google/Gmail, Facebook, Twitter, and FriendFeed.
+The module includes methods to log users in via these sites and, where
+applicable, methods to authorize access to the service so you can, e.g.,
+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::
+
+ class GoogleHandler(tornado.web.RequestHandler, tornado.auth.GoogleMixin):
+ @tornado.web.asynchronous
+ def get(self):
+ if self.get_argument("openid.mode", None):
+ self.get_authenticated_user(self._on_auth)
+ return
+ self.authenticate_redirect()
+
+ def _on_auth(self, user):
+ if not user:
+ self.authenticate_redirect()
+ return
+ # Save the user with, e.g., set_secure_cookie()
+
+See the `tornado.auth` module documentation for more details.
+
.. _xsrf:
Cross-site request forgery protection
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``::
settings = {
"cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
``DELETE`` requests that do not contain a correct ``_xsrf`` value. If
you turn this setting on, you need to instrument all forms that submit
via ``POST`` to contain this field. You can do this with the special
-function ``xsrf_form_html()``, available in all templates:
-
-::
+`.UIModule` ``xsrf_form_html()``, available in all templates::
<form action="/new_message" method="post">
{% module xsrf_form_html() %}
your JavaScript to include the ``_xsrf`` value with each request. This
is the `jQuery <http://jquery.com/>`_ function we use at FriendFeed for
AJAX ``POST`` requests that automatically adds the ``_xsrf`` value to
-all requests:
-
-::
+all requests::
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
set the cookie as a side effect).
If you need to customize XSRF behavior on a per-handler basis, you can
-override ``RequestHandler.check_xsrf_cookie()``. For example, if you
+override `.RequestHandler.check_xsrf_cookie()`. For example, if you
have an API whose authentication does not use cookies, you may want to
disable XSRF protection by making ``check_xsrf_cookie()`` do nothing.
However, if you support both cookie and non-cookie-based authentication,
it is important that XSRF protection be used whenever the current
request is authenticated with a cookie.
-
-Third party authentication
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Tornado's ``auth`` module implements the authentication and
-authorization protocols for a number of the most popular sites on the
-web, including Google/Gmail, Facebook, Twitter, and FriendFeed.
-The module includes methods to log users in via these sites and, where
-applicable, methods to authorize access to the service so you can, e.g.,
-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:
-
-::
-
- class GoogleHandler(tornado.web.RequestHandler, tornado.auth.GoogleMixin):
- @tornado.web.asynchronous
- def get(self):
- if self.get_argument("openid.mode", None):
- self.get_authenticated_user(self._on_auth)
- return
- self.authenticate_redirect()
-
- def _on_auth(self, user):
- if not user:
- self.authenticate_redirect()
- return
- # Save the user with, e.g., set_secure_cookie()
-
-See the `tornado.auth` module documentation for more details.
Structure of a Tornado web application
======================================
-Request handlers and request arguments
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A Tornado web application generally consists of one or more
+`.RequestHandler` subclasses, an `.Application` object which
+routes incoming requests to handlers, and a ``main()`` function
+to start the server.
-A Tornado web application maps URLs or URL patterns to subclasses of
-`tornado.web.RequestHandler`. Those classes define ``get()`` or
-``post()`` methods to handle HTTP ``GET`` or ``POST`` requests to that
-URL.
+A minimal "hello world" example looks something like this::
-This code maps the root URL ``/`` to ``MainHandler`` and the URL pattern
-``/story/([0-9]+)`` to ``StoryHandler``. Regular expression groups are
-passed as arguments to the ``RequestHandler`` methods:
+ from tornado.ioloop import IOLoop
+ from tornado.web import RequestHandler, Application, url
+
+ class HelloHandler(RequestHandler):
+ def get(self):
+ self.write("Hello, world")
+
+ def make_app():
+ return Application([
+ url(r"/", HelloHandler),
+ ])
+
+ def main():
+ app = make_app()
+ app.listen(8888)
+ IOLoop.current().start()
+
+The ``Application`` object
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The `.Application` object is responsible for global configuration, including
+the routing table that maps requests to handlers.
+
+The routing table is a list of `.URLSpec` objects (or tuples), each of
+which contains (at least) a regular expression and a handler class.
+Order matters; the first matching rule is used. If the regular
+expression contains capturing groups, these groups are the *path
+arguments* and will be passed to the handler's HTTP method. If a
+dictionary is passed as the third element of the `.URLSpec`, it
+supplies the *initialization arguments* which will be passed to
+`.RequestHandler.initialize`. Finally, the `.URLSpec` may have a
+name, which will allow it to be used with
+`.RequestHandler.reverse_url`.
+
+For example, in this fragment the root URL ``/`` is mapped to
+``MainHandler`` and URLs of the form ``/story/`` followed by a number
+are mapped to ``StoryHandler``. That number is passed (as a string) to
+``StoryHandler.get``.
::
- class MainHandler(tornado.web.RequestHandler):
+ class MainHandler(RequestHandler):
def get(self):
- self.write("You requested the main page")
+ self.write('<a href="%s">link to story 1</a>' %
+ self.reverse_url("story", "1"))
+
+ class StoryHandler(RequestHandler):
+ def initialize(self, db):
+ self.db = db
- class StoryHandler(tornado.web.RequestHandler):
def get(self, story_id):
- self.write("You requested the story " + story_id)
+ self.write("this is story %s" % story_id)
+
+ app = Application([
+ url(r"/", MainHandler),
+ url(r"/story/([0-9]+)", StoryHandler, dict(db=db), name="story")
+ ])
- application = tornado.web.Application([
- (r"/", MainHandler),
- (r"/story/([0-9]+)", StoryHandler),
- ])
+The `.Application` constructor takes many keyword arguments that
+can be used to customize the behavior of the application and enable
+optional features; see `.Application.settings` for the complete list.
+
+Subclassing ``RequestHandler``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Most of the work of a Tornado web application is done in subclasses
+of `.RequestHandler`. The main entry point for a handler subclass
+is a method named after the HTTP method being handled: ``get()``,
+``post()``, etc. Each handler may define one or more of these methods
+to handle different HTTP actions. As described above, these methods
+will be called with arguments corresponding to the capturing groups
+of the routing rule that matched.
+
+Within a handler, call methods such as `.RequestHandler.render` or
+`.RequestHandler.write` to produce a response. ``render()`` loads a
+`.Template` by name and renders it with the given
+arguments. ``write()`` is used for non-template-based output; it
+accepts strings, bytes, and dictionaries (dicts will be encoded as
+JSON).
+
+Many methods in `.RequestHandler` are designed to be overridden in
+subclasses and be used throughout the application. It is common
+to define a ``BaseHandler`` class that overrides methods such as
+`~.RequestHandler.write_error` and `~.RequestHandler.get_current_user`
+and then subclass your own ``BaseHandler`` instead of `.RequestHandler`
+for all your specific handlers.
+
+Handling request input
+~~~~~~~~~~~~~~~~~~~~~~
-You can get query string arguments and parse ``POST`` bodies with the
-``get_argument()`` method:
+The request handler can access the object representing the current
+request with ``self.request``. See the class definition for
+`~tornado.httputil.HTTPServerRequest` for a complete list of
+attributes.
+
+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`.
::
- class MyFormHandler(tornado.web.RequestHandler):
+ class MyFormHandler(RequestHandler):
def get(self):
- self.write('<html><body><form action="/myform" method="post">'
+ self.write('<html><body><form action="/myform" method="POST">'
'<input type="text" name="message">'
'<input type="submit" value="Submit">'
'</form></body></html>')
def post(self):
self.set_header("Content-Type", "text/plain")
- self.write("You wrote " + self.get_argument("message"))
-
-Uploaded files are available in ``self.request.files``, which maps names
-(the name of the HTML ``<input type="file">`` element) to a list of
-files. Each file is a dictionary of the form
-``{"filename":..., "content_type":..., "body":...}``.
-
-If you want to send an error response to the client, e.g., 403
-Unauthorized, you can just raise a ``tornado.web.HTTPError`` exception:
-
-::
-
- if not self.user_is_logged_in():
- raise tornado.web.HTTPError(403)
-
-The request handler can access the object representing the current
-request with ``self.request``. The ``HTTPRequest`` object includes a
-number of useful attributes, including:
-
-- ``arguments`` - all of the ``GET`` and ``POST`` arguments
-- ``files`` - all of the uploaded files (via ``multipart/form-data``
- POST requests)
-- ``path`` - the request path (everything before the ``?``)
-- ``headers`` - the request headers
-
-See the class definition for `tornado.httputil.HTTPServerRequest` for a
-complete list of attributes.
+ self.write("You wrote " + self.get_body_argument("message"))
+
+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
+it expects a list. For lists, use
+`~.RequestHandler.get_query_arguments` and
+`~.RequestHandler.get_body_arguments` instead of their singular
+counterparts.
+
+Files uploaded via a form are available in ``self.request.files``,
+which maps names (the name of the HTML ``<input type="file">``
+element) to a list of files. Each file is a dictionary of the form
+``{"filename":..., "content_type":..., "body":...}``. The ``files``
+object is only present if the files were uploaded with a form wrapper
+(i.e. a ``multipart/form-data`` Content-Type); if this format was not used
+the raw uploaded data is available in ``self.request.body``.
+By default uploaded files are fully buffered in memory; if you need to
+handle files that are too large to comfortable keep in memory see the
+`.stream_request_body` class decorator.
+
+Due to the quirks of the HTML form encoding (e.g. the ambiguity around
+singular versus plural arguments), Tornado does not attempt to unify
+form arguments with other types of input. In particular, we do not
+parse JSON request bodies. Applications that wish to use JSON instead
+of form-encoding may override `~.RequestHandler.prepare` to parse their
+requests::
+
+ def prepare(self):
+ if self.request.headers["Content-Type"].startswith("application/json"):
+ self.json_args = json.loads(self.request.body)
+ else:
+ self.json_args = None
Overriding RequestHandler methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In addition to ``get()``/``post()``/etc, certain other methods in
-``RequestHandler`` are designed to be overridden by subclasses when
+`.RequestHandler` are designed to be overridden by subclasses when
necessary. On every request, the following sequence of calls takes
place:
-1. A new RequestHandler object is created on each request
-2. ``initialize()`` is called with keyword arguments from the
- ``Application`` configuration. (the ``initialize`` method is new in
- Tornado 1.1; in older versions subclasses would override ``__init__``
- instead). ``initialize`` should typically just save the arguments
- passed into member variables; it may not produce any output or call
- methods like ``send_error``.
-3. ``prepare()`` is called. This is most useful in a base class shared
- by all of your handler subclasses, as ``prepare`` is called no matter
- which HTTP method is used. ``prepare`` may produce output; if it
- calls ``finish`` (or ``send_error``, etc), processing stops here.
+1. A new `.RequestHandler` object is created on each request
+2. `~.RequestHandler.initialize()` is called with the initalization
+ arguments from the `.Application` configuration. ``initialize``
+ should typically just save the arguments passed into member
+ variables; it may not produce any output or call methods like
+ `~.RequestHandler.send_error`.
+3. `~.RequestHandler.prepare()` is called. This is most useful in a
+ base class shared by all of your handler subclasses, as ``prepare``
+ is called no matter which HTTP method is used. ``prepare`` may
+ produce output; if it calls `~.RequestHandler.finish` (or
+ ``redirect``, etc), processing stops here.
4. One of the HTTP methods is called: ``get()``, ``post()``, ``put()``,
etc. If the URL regular expression contains capturing groups, they
are passed as arguments to this method.
-5. When the request is finished, ``on_finish()`` is called. For synchronous
- handlers this is immediately after ``get()`` (etc) return; for
- asynchronous handlers it is after the call to ``finish()``.
-
-Here is an example demonstrating the ``initialize()`` method:
-
-::
-
- class ProfileHandler(RequestHandler):
- def initialize(self, database):
- self.database = database
-
- def get(self, username):
- ...
-
- app = Application([
- (r'/user/(.*)', ProfileHandler, dict(database=database)),
- ])
-
-Other methods designed for overriding include:
-
-- ``write_error(self, status_code, exc_info=None, **kwargs)`` -
- outputs HTML for use on error pages.
-- ``get_current_user(self)`` - see `User
- Authentication <#user-authentication>`_ below
-- ``get_user_locale(self)`` - returns ``locale`` object to use for the
- current user
-- ``get_login_url(self)`` - returns login url to be used by the
- ``@authenticated`` decorator (default is in ``Application`` settings)
-- ``get_template_path(self)`` - returns location of template files
- (default is in ``Application`` settings)
-- ``set_default_headers(self)`` - may be used to set additional headers
- on the response (such as a custom ``Server`` header)
+5. When the request is finished, `~.RequestHandler.on_finish()` is
+ called. For synchronous handlers this is immediately after
+ ``get()`` (etc) return; for asynchronous handlers it is after the
+ call to `~.RequestHandler.finish()`.
+
+All methods designed to be overridden are noted as such in the
+`.RequestHandler` documentation. Some of the most commonly
+overridden methods include:
+
+- `~.RequestHandler.write_error` -
+ outputs HTML for use on error pages.
+- `~.RequestHandler.on_connection_close` - called when the client
+ disconnects; applications may choose to detect this case and halt
+ further processing. Note that there is no guarantee that a closed
+ connection can be detected promptly.
+- `~.RequestHandler.get_current_user` - see :ref:`user-authentication`
+- `~.RequestHandler.get_user_locale` - returns `.Locale` object to use
+ for the current user
+- `~.RequestHandler.set_default_headers` - may be used to set
+ additional headers on the response (such as a custom ``Server``
+ header)
Error Handling
~~~~~~~~~~~~~~
-There are three ways to return an error from a `RequestHandler`:
-
-1. Manually call `~tornado.web.RequestHandler.set_status` and output the
- response body normally.
-2. Call `~RequestHandler.send_error`. This discards
- any pending unflushed output and calls `~RequestHandler.write_error` to
- generate an error page.
-3. Raise an exception. `tornado.web.HTTPError` can be used to generate
- a specified status code; all other exceptions return a 500 status.
- The exception handler uses `~RequestHandler.send_error` and
- `~RequestHandler.write_error` to generate the error page.
-
-The default error page includes a stack trace in debug mode and a one-line
-description of the error (e.g. "500: Internal Server Error") otherwise.
-To produce a custom error page, override `RequestHandler.write_error`.
-This method may produce output normally via methods such as
-`~RequestHandler.write` and `~RequestHandler.render`. If the error was
-caused by an exception, an ``exc_info`` triple will be passed as a keyword
-argument (note that this exception is not guaranteed to be the current
-exception in ``sys.exc_info``, so ``write_error`` must use e.g.
-`traceback.format_exception` instead of `traceback.format_exc`).
+If a handler raises an exception, Tornado will call
+`.RequestHandler.write_error` to generate an error page.
+`tornado.web.HTTPError` can be used to generate a specified status
+code; all other exceptions return a 500 status.
+
+The default error page includes a stack trace in debug mode and a
+one-line description of the error (e.g. "500: Internal Server Error")
+otherwise. To produce a custom error page, override
+`RequestHandler.write_error` (probably in a base class shared by all
+your handlers). This method may produce output normally via
+methods such as `~RequestHandler.write` and `~RequestHandler.render`.
+If the error was caused by an exception, an ``exc_info`` triple will
+be passed as a keyword argument (note that this exception is not
+guaranteed to be the current exception in `sys.exc_info`, so
+``write_error`` must use e.g. `traceback.format_exception` instead of
+`traceback.format_exc`).
+
+It is also possible to generate an error page from regular handler
+methods instead of ``write_error`` by calling
+`~.RequestHandler.set_status`, writing a response, and returning.
+The special exception `tornado.web.Finish` may be raised to terminate
+the handler without calling ``write_error`` in situations where simply
+returning is not convenient.
+
+For 404 errors, use the ``default_handler_class`` `Application setting
+<.Application.settings>`. This handler should override
+`~.RequestHandler.prepare` instead of a more specific method like
+``get()`` so it works with any HTTP method. It should produce its
+error page as described above: either by raising a ``HTTPError(404)``
+and overriding ``write_error``, or calling ``self.set_status(404)``
+and producing the response directly in ``prepare()``.
Redirection
~~~~~~~~~~~
There are two main ways you can redirect requests in Tornado:
-``self.redirect`` and with the ``RedirectHandler``.
-
-You can use ``self.redirect`` within a ``RequestHandler`` method (like
-``get``) to redirect users elsewhere. There is also an optional
-parameter ``permanent`` which you can use to indicate that the
-redirection is considered permanent.
-
-This triggers a ``301 Moved Permanently`` HTTP status, which is useful
-for e.g. redirecting to a canonical URL for a page in an SEO-friendly
+`.RequestHandler.redirect` and with the `.RedirectHandler`.
+
+You can use ``self.redirect()`` within a `.RequestHandler` method to
+redirect users elsewhere. There is also an optional parameter
+``permanent`` which you can use to indicate that the redirection is
+considered permanent. The default value of ``permanent`` is
+``False``, which generates a ``302 Found`` HTTP response code and is
+appropriate for things like redirecting users after successful
+``POST`` requests. If ``permanent`` is true, the ``301 Moved
+Permanently`` HTTP response code is used, which is useful for
+e.g. redirecting to a canonical URL for a page in an SEO-friendly
manner.
-The default value of ``permanent`` is ``False``, which is apt for things
-like redirecting users on successful POST requests.
-
-::
-
- self.redirect('/some-canonical-page', permanent=True)
-
-``RedirectHandler`` is available for your use when you initialize
-``Application``.
+`.RedirectHandler` lets you configure redirects directly in your
+`.Application` routing table. For example, to configure a single
+static redirect::
-For example, notice how we redirect to a longer download URL on this
-website:
-
-::
-
- application = tornado.wsgi.WSGIApplication([
- (r"/([a-z]*)", ContentHandler),
- (r"/static/tornado-0.2.tar.gz", tornado.web.RedirectHandler,
- dict(url="https://github.com/downloads/facebook/tornado/tornado-0.2.tar.gz")),
- ], **settings)
-
-The default ``RedirectHandler`` status code is
-``301 Moved Permanently``, but to use ``302 Found`` instead, set
-``permanent`` to ``False``.
-
-::
-
- application = tornado.wsgi.WSGIApplication([
- (r"/foo", tornado.web.RedirectHandler, {"url":"/bar", "permanent":False}),
- ], **settings)
-
-Note that the default value of ``permanent`` is different in
-``self.redirect`` than in ``RedirectHandler``. This should make some
-sense if you consider that ``self.redirect`` is used in your methods and
-is probably invoked by logic involving environment, authentication, or
-form submission, but ``RedirectHandler`` patterns are going to fire 100%
-of the time they match the request URL.
-
-Non-blocking, asynchronous requests
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When a request handler is executed, the request is automatically
-finished. Since Tornado uses a non-blocking I/O style, you can override
-this default behavior if you want a request to remain open after the
-main request handler method returns using the
-``tornado.web.asynchronous`` decorator.
+ app = tornado.web.Application([
+ url(r"/app", tornado.web.RedirectHandler,
+ dict(url="http://itunes.apple.com/my-app-id")),
+ ])
-When you use this decorator, it is your responsibility to call
-``self.finish()`` to finish the HTTP request, or the user's browser will
-simply hang:
+`.RedirectHandler` also supports regular expression substitutions.
+The following rule redirects all requests beginning with ``/pictures/``
+to the prefix ``/photos/`` instead::
-::
-
- class MainHandler(tornado.web.RequestHandler):
- @tornado.web.asynchronous
- def get(self):
- self.write("Hello, world")
- self.finish()
-
-Here is a real example that makes a call to the FriendFeed API using
-Tornado's built-in asynchronous HTTP client:
+ app = tornado.web.Application([
+ url(r"/photos/(.*)", MyPhotoHandler),
+ url(r"/pictures/(.*)", tornado.web.RedirectHandler,
+ dict(url=r"/photos/\1")),
+ ])
-::
+Unlike `.RequestHandler.redirect`, `.RedirectHandler` uses permanent
+redirects by default. This is because the routing table does not change
+at runtime and is presumed to be permanent, while redirects found in
+handlers are likely to be the result of other logic that may change.
+To send a temporary redirect with a `.RedirectHandler`, add
+``permanent=False`` to the `.RedirectHandler` initialization arguments.
+
+Asynchronous handlers
+~~~~~~~~~~~~~~~~~~~~~
+
+Tornado handlers are synchronous by default: when the
+``get()``/``post()`` method returns, the request is considered
+finished and the response is sent. Since all other requests are
+blocked while one handler is running, any long-running handler should
+be made asynchronous so it can call its slow operations in a
+non-blocking way. This topic is covered in more detail in
+:doc:`async`; this section is about the particulars of
+asynchronous techniques in `.RequestHandler` subclasses.
+
+The simplest way to make a handler asynchronous is to use the
+`.coroutine` decorator. This allows you to perform non-blocking I/O
+with the ``yield`` keyword, and no response will be sent until the
+coroutine has returned. See :doc:`coroutines` for more details.
+
+In some cases, coroutines may be less convenient thatn a
+callback-oriented style, in which case the `.tornado.web.asynchronous`
+decorator can be used instead. When this decorator is used the response
+is not automatically sent; instead the request will be kept open until
+some callback calls `.RequestHandler.finish`. It is up to the application
+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`::
class MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
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::
+
+ class MainHandler(tornado.web.RequestHandler):
+ @tornado.gen.coroutine
+ def get(self):
+ http = tornado.httpclient.AsyncHTTPClient()
+ yield http.fetch("http://friendfeed-api.com/v2/feed/bret")
+ json = tornado.escape.json_decode(response.body)
+ self.write("Fetched " + str(len(json["entries"])) + " entries "
+ "from the FriendFeed API")
+
For a more advanced asynchronous example, take a look at the `chat
example application
<https://github.com/tornadoweb/tornado/tree/stable/demos/chat>`_, which
of long polling may want to override ``on_connection_close()`` to
clean up after the client closes the connection (but see that method's
docstring for caveats).
-
-Asynchronous HTTP clients
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Tornado includes two non-blocking HTTP client implementations:
-``SimpleAsyncHTTPClient`` and ``CurlAsyncHTTPClient``. The simple client
-has no external dependencies because it is implemented directly on top
-of Tornado's ``IOLoop``. The Curl client requires that ``libcurl`` and
-``pycurl`` be installed (and a recent version of each is highly
-recommended to avoid bugs in older version's asynchronous interfaces),
-but is more likely to be compatible with sites that exercise little-used
-parts of the HTTP specification.
-
-Each of these clients is available in its own module
-(``tornado.simple_httpclient`` and ``tornado.curl_httpclient``), as well
-as via a configurable alias in ``tornado.httpclient``.
-``SimpleAsyncHTTPClient`` is the default, but to use a different
-implementation call the ``AsyncHTTPClient.configure`` method at startup:
-
-::
-
- AsyncHTTPClient.configure('tornado.curl_httpclient.CurlAsyncHTTPClient')
Templates and UI
================
+Tornado includes a simple, fast, and flexible templating language.
+This section describes that language as well as related issues
+such as internationalization.
-Templates
-~~~~~~~~~
+Tornado can also be used with any other Python template language,
+although there is no provision for integrating these systems into
+`.RequestHandler.render`. Simply render the template to a string
+and pass it to `.RequestHandler.write`
-You can use any template language supported by Python, but Tornado ships
-with its own templating language that is a lot faster and more flexible
-than many of the most popular templating systems out there. See the
-`tornado.template` module documentation for complete documentation.
+Configuring templates
+~~~~~~~~~~~~~~~~~~~~~
-A Tornado template is just HTML (or any other text-based format) with
-Python control sequences and expressions embedded within the markup:
+By default, Tornado looks for template files in the same directory as
+the ``.py`` files that refer to them. To put your template files in a
+different directory, use the ``template_path`` `Application setting
+<.Application.settings>` (or override `.RequestHandler.get_template_path`
+if you have different template paths for different handlers).
+
+To load templates from a non-filesystem location, subclass
+`tornado.template.BaseLoader` and pass an instance as the
+``template_loader`` application setting.
+
+Compiled templates are cached by default; to turn off this caching
+and reload templates so changes to the underlying files are always
+visible, use the application settings ``compiled_template_cache=False``
+or ``debug=True``.
-::
+
+Template syntax
+~~~~~~~~~~~~~~~
+
+A Tornado template is just HTML (or any other text-based format) with
+Python control sequences and expressions embedded within the markup::
<html>
<head>
Expressions can be any Python expression, including function calls.
Template code is executed in a namespace that includes the following
-objects and functions (Note that this list applies to templates rendered
-using ``RequestHandler.render`` and ``render_string``. If you're using
-the ``template`` module directly outside of a ``RequestHandler`` many of
-these entries are not present).
-
-- ``escape``: alias for ``tornado.escape.xhtml_escape``
-- ``xhtml_escape``: alias for ``tornado.escape.xhtml_escape``
-- ``url_escape``: alias for ``tornado.escape.url_escape``
-- ``json_encode``: alias for ``tornado.escape.json_encode``
-- ``squeeze``: alias for ``tornado.escape.squeeze``
-- ``linkify``: alias for ``tornado.escape.linkify``
-- ``datetime``: the Python ``datetime`` module
-- ``handler``: the current ``RequestHandler`` object
-- ``request``: alias for ``handler.request``
-- ``current_user``: alias for ``handler.current_user``
-- ``locale``: alias for ``handler.locale``
-- ``_``: alias for ``handler.locale.translate``
-- ``static_url``: alias for ``handler.static_url``
-- ``xsrf_form_html``: alias for ``handler.xsrf_form_html``
-- ``reverse_url``: alias for ``Application.reverse_url``
-- All entries from the ``ui_methods`` and ``ui_modules``
- ``Application`` settings
-- Any keyword arguments passed to ``render`` or ``render_string``
+objects and functions (Note that this list applies to templates
+rendered using `.RequestHandler.render` and
+`~.RequestHandler.render_string`. If you're using the
+`tornado.template` module directly outside of a `.RequestHandler` many
+of these entries are not present).
+
+- ``escape``: alias for `tornado.escape.xhtml_escape`
+- ``xhtml_escape``: alias for `tornado.escape.xhtml_escape`
+- ``url_escape``: alias for `tornado.escape.url_escape`
+- ``json_encode``: alias for `tornado.escape.json_encode`
+- ``squeeze``: alias for `tornado.escape.squeeze`
+- ``linkify``: alias for `tornado.escape.linkify`
+- ``datetime``: the Python `datetime` module
+- ``handler``: the current `.RequestHandler` object
+- ``request``: alias for `handler.request <.HTTPServerRequest>`
+- ``current_user``: alias for `handler.current_user
+ <.RequestHandler.current_user>`
+- ``locale``: alias for `handler.locale <.Locale>`
+- ``_``: alias for `handler.locale.translate <.Locale.translate>`
+- ``static_url``: alias for `handler.static_url <.RequestHandler.static_url>`
+- ``xsrf_form_html``: alias for `handler.xsrf_form_html
+ <.RequestHandler.xsrf_form_html>`
+- ``reverse_url``: alias for `.Application.reverse_url`
+- All entries from the ``ui_methods`` and ``ui_modules``
+ ``Application`` settings
+- Any keyword arguments passed to `~.RequestHandler.render` or
+ `~.RequestHandler.render_string`
When you are building a real application, you are going to want to use
all of the features of Tornado templates, especially template
inheritance. Read all about those features in the `tornado.template`
section (some features, including ``UIModules`` are implemented in the
-``web`` module)
+`tornado.web` module)
Under the hood, Tornado templates are translated directly to Python. The
expressions you include in your template are copied verbatim into a
template.
All template output is escaped by default, using the
-``tornado.escape.xhtml_escape`` function. This behavior can be changed
-globally by passing ``autoescape=None`` to the ``Application`` or
-``TemplateLoader`` constructors, for a template file with the
+`tornado.escape.xhtml_escape` function. This behavior can be changed
+globally by passing ``autoescape=None`` to the `.Application` or
+`.tornado.template.Loader` constructors, for a template file with the
``{% autoescape None %}`` directive, or for a single expression by
replacing ``{{ ... }}`` with ``{% raw ...%}``. Additionally, in each of
these places the name of an alternative escaping function may be used
XSS vulnerabilities, it is not sufficient in all cases. Expressions
that appear in certain locations, such as in Javascript or CSS, may need
additional escaping. Additionally, either care must be taken to always
-use double quotes and ``xhtml_escape`` in HTML attributes that may contain
+use double quotes and `.xhtml_escape` in HTML attributes that may contain
untrusted content, or a separate escaping function must be used for
attributes (see e.g. http://wonko.com/post/html-escaping)
-Localization
-~~~~~~~~~~~~
+Internationalization
+~~~~~~~~~~~~~~~~~~~~
The locale of the current user (whether they are logged in or not) is
always available as ``self.locale`` in the request handler and as
``locale`` in templates. The name of the locale (e.g., ``en_US``) is
available as ``locale.name``, and you can translate strings with the
-``locale.translate`` method. Templates also have the global function
+`.Locale.translate` method. Templates also have the global function
call ``_()`` available for string translation. The translate function
-has two forms:
-
-::
+has two forms::
_("Translate this string")
-which translates the string directly based on the current locale, and
-
-::
+which translates the string directly based on the current locale, and::
_("A person liked this", "%(num)d people liked this",
len(people)) % {"num": len(people)}
placeholders for variables (the ``%(num)d`` in the example above) since
placeholders can move around on translation.
-Here is a properly localized template:
-
-::
+Here is a properly internationalized template::
<html>
<head>
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 ``get_user_locale`` in your request handler:
-
-::
+by overriding `.RequestHandler.get_user_locale`::
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
If ``get_user_locale`` returns ``None``, we fall back on the
``Accept-Language`` header.
-You can load all the translations for your application using the
-``tornado.locale.load_translations`` method. It takes in the name of the
-directory which should contain CSV files named after the locales whose
-translations they contain, e.g., ``es_GT.csv`` or ``fr_CA.csv``. The
-method loads all the translations from those CSV files and infers the
-list of supported locales based on the presence of each CSV file. You
-typically call this method once in the ``main()`` method of your server:
-
-::
-
- def main():
- tornado.locale.load_translations(
- os.path.join(os.path.dirname(__file__), "translations"))
- start_server()
+The `tornado.locale` module supports loading translations in two
+formats: the ``.mo`` format used by `gettext` and related tools, and a
+simple ``.csv`` format. An application will generally call either
+`tornado.locale.load_translations` or
+`tornado.locale.load_gettext_translations` once at startup; see those
+methods for more details on the supported formats..
You can get the list of supported locales in your application with
-``tornado.locale.get_supported_locales()``. The user's locale is chosen
+`tornado.locale.get_supported_locales()`. The user's locale is chosen
to be the closest match based on the supported locales. For example, if
the user's locale is ``es_GT``, and the ``es`` locale is supported,
``self.locale`` will be ``es`` for that request. We fall back on
``en_US`` if no close match can be found.
-See the `tornado.locale`
-documentation for detailed information on the CSV format and other
-localization methods.
-
.. _ui-modules:
UI modules
Tornado supports *UI modules* to make it easy to support standard,
reusable UI widgets across your application. UI modules are like special
-functional calls to render components of your page, and they can come
+function calls to render components of your page, and they can come
packaged with their own CSS and JavaScript.
For example, if you are implementing a blog, and you want to have blog
entries appear on both the blog home page and on each blog entry page,
you can make an ``Entry`` module to render them on both pages. First,
-create a Python module for your UI modules, e.g., ``uimodules.py``:
-
-::
+create a Python module for your UI modules, e.g., ``uimodules.py``::
class Entry(tornado.web.UIModule):
def render(self, entry, show_comments=False):
"module-entry.html", entry=entry, show_comments=show_comments)
Tell Tornado to use ``uimodules.py`` using the ``ui_modules`` setting in
-your application:
+your application::
-::
+ from . import uimodules
class HomeHandler(tornado.web.RequestHandler):
def get(self):
(r"/entry/([0-9]+)", EntryHandler),
], **settings)
-Within ``home.html``, you reference the ``Entry`` module rather than
-printing the HTML directly:
-
-::
+Within a template, you can call a module with the ``{% module %}``
+statement. For example, you could call the ``Entry`` module from both
+``home.html``::
{% for entry in entries %}
{% module Entry(entry) %}
{% end %}
-Within ``entry.html``, you reference the ``Entry`` module with the
-``show_comments`` argument to show the expanded form of the entry:
-
-::
+and ``entry.html``::
{% module Entry(entry, show_comments=True) %}
Modules can include custom CSS and JavaScript functions by overriding
the ``embedded_css``, ``embedded_javascript``, ``javascript_files``, or
-``css_files`` methods:
-
-::
+``css_files`` methods::
class Entry(tornado.web.UIModule):
def embedded_css(self):
When additional Python code is not required, a template file itself may
be used as a module. For example, the preceding example could be
-rewritten to put the following in ``module-entry.html``:
-
-::
+rewritten to put the following in ``module-entry.html``::
{{ set_resources(embedded_css=".entry { margin-bottom: 1em; }") }}
<!-- more template html... -->
-This revised template module would be invoked with
-
-::
+This revised template module would be invoked with::
{% module Template("module-entry.html", show_comments=True) %}
.. automethod:: RequestHandler.check_xsrf_cookie
.. automethod:: RequestHandler.compute_etag
.. automethod:: RequestHandler.create_template_loader
+ .. autoattribute:: RequestHandler.current_user
.. automethod:: RequestHandler.get_browser_locale
.. automethod:: RequestHandler.get_current_user
.. automethod:: RequestHandler.get_login_url
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTCONNECTTIMEOUTMS
and comments in curl_httpclient.py).
+To select ``curl_httpclient``, call `AsyncHTTPClient.configure` at startup::
+
+ AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
"""
from __future__ import absolute_import, division, print_function, with_statement
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
-See the :doc:`Tornado overview <overview>` for more details and a good getting
-started guide.
+See the :doc:`guide` for additional information.
Thread-safety notes
-------------------
you use multiple threads it is important to use `.IOLoop.add_callback`
to transfer control back to the main thread before finishing the
request.
+
"""
from __future__ import absolute_import, division, print_function, with_statement