├── tornado ├── platform │ ├── __init__.py │ ├── auto.pyi │ ├── windows.py │ ├── epoll.py │ ├── auto.py │ ├── posix.py │ ├── interface.py │ ├── select.py │ ├── caresresolver.py │ └── kqueue.py ├── test │ ├── __init__.py │ ├── templates │ │ └── utf8.html │ ├── static │ │ ├── dir │ │ │ └── index.html │ │ ├── robots.txt │ │ ├── sample.xml.bz2 │ │ ├── sample.xml.gz │ │ └── sample.xml │ ├── csv_translations │ │ └── fr_FR.csv │ ├── options_test.cfg │ ├── static_foo.txt │ ├── gettext_translations │ │ ├── fr_FR │ │ │ └── LC_MESSAGES │ │ │ │ ├── tornado_test.mo │ │ │ │ └── tornado_test.po │ │ └── extract_me.py │ ├── __main__.py │ ├── resolve_test_helper.py │ ├── test.crt │ ├── windows_test.py │ ├── test.key │ ├── import_test.py │ ├── http1connection_test.py │ ├── tcpserver_test.py │ └── util.py ├── speedups.pyi ├── __init__.py └── speedups.c ├── demos ├── facebook │ ├── static │ │ ├── facebook.js │ │ └── facebook.css │ ├── README │ └── templates │ │ ├── stream.html │ │ └── modules │ │ └── post.html ├── blog │ ├── requirements.txt │ ├── templates │ │ ├── entry.html │ │ ├── home.html │ │ ├── create_author.html │ │ ├── login.html │ │ ├── modules │ │ │ └── entry.html │ │ ├── archive.html │ │ ├── base.html │ │ ├── feed.xml │ │ └── compose.html │ ├── docker-compose.yml │ ├── Dockerfile │ ├── schema.sql │ ├── README │ └── static │ │ └── blog.css ├── chat │ ├── templates │ │ ├── message.html │ │ └── index.html │ └── static │ │ └── chat.css ├── websocket │ ├── templates │ │ ├── message.html │ │ └── index.html │ ├── static │ │ ├── chat.css │ │ └── chat.js │ └── chatdemo.py ├── appengine │ ├── templates │ │ ├── entry.html │ │ ├── home.html │ │ ├── modules │ │ │ └── entry.html │ │ ├── archive.html │ │ ├── base.html │ │ ├── feed.xml │ │ └── compose.html │ ├── app.yaml │ ├── README │ └── static │ │ └── blog.css ├── twitter │ └── home.html ├── tcpecho │ ├── README.md │ ├── client.py │ └── server.py ├── benchmark │ ├── gen_benchmark.py │ ├── chunk_benchmark.py │ ├── template_benchmark.py │ ├── stack_context_benchmark.py │ └── benchmark.py ├── helloworld │ └── helloworld.py ├── file_upload │ └── file_receiver.py └── webspider │ └── webspider.py ├── docs ├── requirements.txt ├── favicon.ico ├── tornado.png ├── log.rst ├── utilities.rst ├── http.rst ├── webframework.rst ├── locale.rst ├── routing.rst ├── coroutine.rst ├── networking.rst ├── netutil.rst ├── tcpclient.rst ├── integration.rst ├── tcpserver.rst ├── asyncio.rst ├── http1connection.rst ├── autoreload.rst ├── util.rst ├── stack_context.rst ├── guide.rst ├── httputil.rst ├── httpserver.rst ├── process.rst ├── releases │ ├── v4.4.1.rst │ ├── v1.0.1.rst │ ├── v4.4.3.rst │ ├── v4.5.1.rst │ ├── v4.2.1.rst │ ├── v3.1.1.rst │ ├── v3.0.2.rst │ ├── v2.4.1.rst │ ├── v2.2.1.rst │ ├── v1.2.1.rst │ ├── v4.4.2.rst │ ├── v4.0.2.rst │ ├── v4.0.1.rst │ ├── v1.1.1.rst │ ├── v3.2.2.rst │ ├── v3.0.1.rst │ ├── v2.1.1.rst │ ├── v3.2.1.rst │ ├── v1.1.0.rst │ ├── v1.0.0.rst │ ├── v2.0.0.rst │ ├── v4.4.0.rst │ └── v2.4.0.rst ├── wsgi.rst ├── twisted.rst ├── template.rst ├── queues.rst ├── options.rst ├── caresresolver.rst ├── releases.rst ├── Makefile ├── concurrent.rst ├── testing.rst ├── locks.rst ├── escape.rst ├── websocket.rst ├── guide │ ├── queues.rst │ └── intro.rst ├── auth.rst ├── iostream.rst ├── httpclient.rst ├── ioloop.rst ├── gen.rst └── conf.py ├── maint ├── scripts │ ├── custom_fixers │ │ ├── __init__.py │ │ ├── fix_unicode_literal.py │ │ └── fix_future_imports.py │ ├── run_fixers.py │ ├── run_autopep8.sh │ └── test_resolvers.py ├── test │ ├── websocket │ │ ├── .gitignore │ │ ├── fuzzingserver.json │ │ ├── tox.ini │ │ ├── run-client.sh │ │ ├── fuzzingclient.json │ │ ├── server.py │ │ ├── run-server.sh │ │ └── client.py │ ├── appengine │ │ ├── py27 │ │ │ ├── tornado │ │ │ ├── runtests.py │ │ │ ├── cgi_runtests.py │ │ │ └── app.yaml │ │ ├── setup.py │ │ ├── README │ │ ├── tox.ini │ │ └── common │ │ │ ├── cgi_runtests.py │ │ │ └── runtests.py │ ├── cython │ │ ├── MANIFEST.in │ │ ├── .gitignore │ │ ├── pythonmodule.py │ │ ├── setup.py │ │ ├── tox.ini │ │ ├── cythonapp.pyx │ │ └── cythonapp_test.py │ ├── README │ ├── redbot │ │ ├── tox.ini │ │ └── README │ └── pyuv │ │ └── tox.ini ├── README ├── vm │ ├── ubuntu14.04 │ │ ├── Vagrantfile │ │ ├── setup.sh │ │ └── tox.ini │ ├── shared-setup.sh │ ├── ubuntu12.04 │ │ ├── Vagrantfile │ │ ├── tox.ini │ │ └── setup.sh │ ├── freebsd │ │ ├── setup.sh │ │ ├── tox.ini │ │ └── Vagrantfile │ ├── windows │ │ ├── tox.ini │ │ └── bootstrap.py │ └── README ├── requirements.in ├── requirements.txt └── circlerefs │ └── circlerefs.py ├── codecov.yml ├── .gitignore ├── .coveragerc ├── runtests.sh ├── MANIFEST.in ├── README.rst └── appveyor.yml /tornado/platform/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tornado/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demos/facebook/static/facebook.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | Twisted 2 | -------------------------------------------------------------------------------- /maint/scripts/custom_fixers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tornado/test/templates/utf8.html: -------------------------------------------------------------------------------- 1 | Héllo 2 | -------------------------------------------------------------------------------- /maint/test/websocket/.gitignore: -------------------------------------------------------------------------------- 1 | reports/ 2 | -------------------------------------------------------------------------------- /maint/test/appengine/py27/tornado: -------------------------------------------------------------------------------- 1 | ../../../../tornado -------------------------------------------------------------------------------- /maint/test/appengine/py27/runtests.py: -------------------------------------------------------------------------------- 1 | ../common/runtests.py -------------------------------------------------------------------------------- /maint/test/cython/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include cythonapp.pyx 2 | -------------------------------------------------------------------------------- /tornado/test/static/dir/index.html: -------------------------------------------------------------------------------- 1 | this is the index 2 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | coverage: 3 | status: off 4 | -------------------------------------------------------------------------------- /tornado/test/csv_translations/fr_FR.csv: -------------------------------------------------------------------------------- 1 | "school","école" 2 | -------------------------------------------------------------------------------- /maint/test/appengine/py27/cgi_runtests.py: -------------------------------------------------------------------------------- 1 | ../common/cgi_runtests.py -------------------------------------------------------------------------------- /tornado/test/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /maint/test/cython/.gitignore: -------------------------------------------------------------------------------- 1 | .eggs 2 | cythonapp.egg-info 3 | dist 4 | -------------------------------------------------------------------------------- /tornado/speedups.pyi: -------------------------------------------------------------------------------- 1 | def websocket_mask(mask: bytes, data: bytes) -> bytes: ... 2 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangchuchuan/tornado/master/docs/favicon.ico -------------------------------------------------------------------------------- /docs/tornado.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangchuchuan/tornado/master/docs/tornado.png -------------------------------------------------------------------------------- /demos/blog/requirements.txt: -------------------------------------------------------------------------------- 1 | bcrypt 2 | futures 3 | MySQL-python 4 | markdown 5 | tornado 6 | torndb 7 | -------------------------------------------------------------------------------- /tornado/test/options_test.cfg: -------------------------------------------------------------------------------- 1 | port=443 2 | port=443 3 | username='李康' 4 | 5 | foo_bar='a' 6 | 7 | my_path = __file__ 8 | -------------------------------------------------------------------------------- /demos/chat/templates/message.html: -------------------------------------------------------------------------------- 1 |
{% module linkify(message["body"]) %}
2 | -------------------------------------------------------------------------------- /tornado/test/static/sample.xml.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangchuchuan/tornado/master/tornado/test/static/sample.xml.bz2 -------------------------------------------------------------------------------- /tornado/test/static/sample.xml.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangchuchuan/tornado/master/tornado/test/static/sample.xml.gz -------------------------------------------------------------------------------- /demos/blog/templates/entry.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body %} 4 | {% module Entry(entry) %} 5 | {% end %} 6 | -------------------------------------------------------------------------------- /demos/websocket/templates/message.html: -------------------------------------------------------------------------------- 1 |
{% module linkify(message["body"]) %}
2 | -------------------------------------------------------------------------------- /tornado/test/static_foo.txt: -------------------------------------------------------------------------------- 1 | This file should not be served by StaticFileHandler even though 2 | its name starts with "static". 3 | -------------------------------------------------------------------------------- /demos/appengine/templates/entry.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body %} 4 | {% module Entry(entry) %} 5 | {% end %} 6 | -------------------------------------------------------------------------------- /docs/log.rst: -------------------------------------------------------------------------------- 1 | ``tornado.log`` --- Logging support 2 | =================================== 3 | 4 | .. automodule:: tornado.log 5 | :members: 6 | -------------------------------------------------------------------------------- /maint/test/cython/pythonmodule.py: -------------------------------------------------------------------------------- 1 | from tornado import gen 2 | 3 | @gen.coroutine 4 | def hello(): 5 | yield gen.sleep(0.001) 6 | raise gen.Return("hello") 7 | -------------------------------------------------------------------------------- /docs/utilities.rst: -------------------------------------------------------------------------------- 1 | Utilities 2 | ========= 3 | 4 | .. toctree:: 5 | 6 | autoreload 7 | log 8 | options 9 | stack_context 10 | testing 11 | util 12 | -------------------------------------------------------------------------------- /docs/http.rst: -------------------------------------------------------------------------------- 1 | HTTP servers and clients 2 | ======================== 3 | 4 | .. toctree:: 5 | 6 | httpserver 7 | httpclient 8 | httputil 9 | http1connection 10 | -------------------------------------------------------------------------------- /docs/webframework.rst: -------------------------------------------------------------------------------- 1 | Web framework 2 | ============= 3 | 4 | .. toctree:: 5 | 6 | web 7 | template 8 | routing 9 | escape 10 | locale 11 | websocket 12 | -------------------------------------------------------------------------------- /tornado/platform/auto.pyi: -------------------------------------------------------------------------------- 1 | # auto.py is full of patterns mypy doesn't like, so for type checking 2 | # purposes we replace it with interface.py. 3 | 4 | from .interface import * 5 | -------------------------------------------------------------------------------- /docs/locale.rst: -------------------------------------------------------------------------------- 1 | ``tornado.locale`` --- Internationalization support 2 | =================================================== 3 | 4 | .. automodule:: tornado.locale 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/routing.rst: -------------------------------------------------------------------------------- 1 | ``tornado.routing`` --- Basic routing implementation 2 | ==================================================== 3 | 4 | .. automodule:: tornado.routing 5 | :members: 6 | -------------------------------------------------------------------------------- /maint/README: -------------------------------------------------------------------------------- 1 | This directory contains tools and scripts that are used in the development 2 | and maintainance of Tornado itself, but are probably not of interest to 3 | Tornado users. 4 | -------------------------------------------------------------------------------- /docs/coroutine.rst: -------------------------------------------------------------------------------- 1 | Coroutines and concurrency 2 | ========================== 3 | 4 | .. toctree:: 5 | 6 | gen 7 | concurrent 8 | locks 9 | queues 10 | process 11 | -------------------------------------------------------------------------------- /docs/networking.rst: -------------------------------------------------------------------------------- 1 | Asynchronous networking 2 | ======================= 3 | 4 | .. toctree:: 5 | 6 | ioloop 7 | iostream 8 | netutil 9 | tcpclient 10 | tcpserver 11 | -------------------------------------------------------------------------------- /tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangchuchuan/tornado/master/tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.mo -------------------------------------------------------------------------------- /docs/netutil.rst: -------------------------------------------------------------------------------- 1 | ``tornado.netutil`` --- Miscellaneous network utilities 2 | ======================================================= 3 | 4 | .. automodule:: tornado.netutil 5 | :members: 6 | -------------------------------------------------------------------------------- /maint/test/appengine/setup.py: -------------------------------------------------------------------------------- 1 | # Dummy setup file to make tox happy. In the appengine world things aren't 2 | # installed through setup.py 3 | import distutils.core 4 | distutils.core.setup() 5 | -------------------------------------------------------------------------------- /docs/tcpclient.rst: -------------------------------------------------------------------------------- 1 | ``tornado.tcpclient`` --- `.IOStream` connection factory 2 | ======================================================== 3 | 4 | .. automodule:: tornado.tcpclient 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/integration.rst: -------------------------------------------------------------------------------- 1 | Integration with other services 2 | =============================== 3 | 4 | .. toctree:: 5 | 6 | auth 7 | wsgi 8 | asyncio 9 | caresresolver 10 | twisted 11 | -------------------------------------------------------------------------------- /docs/tcpserver.rst: -------------------------------------------------------------------------------- 1 | ``tornado.tcpserver`` --- Basic `.IOStream`-based TCP server 2 | ============================================================ 3 | 4 | .. automodule:: tornado.tcpserver 5 | :members: 6 | -------------------------------------------------------------------------------- /maint/test/appengine/py27/app.yaml: -------------------------------------------------------------------------------- 1 | application: tornado-tests-appengine27 2 | version: 1 3 | runtime: python27 4 | threadsafe: false 5 | api_version: 1 6 | 7 | handlers: 8 | - url: / 9 | script: cgi_runtests.py -------------------------------------------------------------------------------- /maint/test/README: -------------------------------------------------------------------------------- 1 | This directory contains additional tests that are not included in the main 2 | suite (because e.g. they have extra dependencies, run slowly, or produce 3 | more output than a simple pass/fail) 4 | -------------------------------------------------------------------------------- /maint/scripts/run_fixers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Usage is like 2to3: 3 | # $ maint/scripts/run_fixers.py -wn --no-diffs tornado 4 | 5 | import sys 6 | from lib2to3.main import main 7 | 8 | sys.exit(main("custom_fixers")) 9 | -------------------------------------------------------------------------------- /docs/asyncio.rst: -------------------------------------------------------------------------------- 1 | ``tornado.platform.asyncio`` --- Bridge between ``asyncio`` and Tornado 2 | ======================================================================= 3 | 4 | .. automodule:: tornado.platform.asyncio 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/http1connection.rst: -------------------------------------------------------------------------------- 1 | ``tornado.http1connection`` -- HTTP/1.x client/server implementation 2 | ==================================================================== 3 | 4 | .. automodule:: tornado.http1connection 5 | :members: 6 | -------------------------------------------------------------------------------- /demos/blog/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body %} 4 | {% for entry in entries %} 5 | {% module Entry(entry) %} 6 | {% end %} 7 |
{{ _("Archive") }}
8 | {% end %} 9 | -------------------------------------------------------------------------------- /docs/autoreload.rst: -------------------------------------------------------------------------------- 1 | ``tornado.autoreload`` --- Automatically detect code changes in development 2 | =========================================================================== 3 | 4 | .. automodule:: tornado.autoreload 5 | :members: 6 | -------------------------------------------------------------------------------- /demos/appengine/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body %} 4 | {% for entry in entries %} 5 | {% module Entry(entry) %} 6 | {% end %} 7 |
{{ _("Archive") }}
8 | {% end %} 9 | -------------------------------------------------------------------------------- /docs/util.rst: -------------------------------------------------------------------------------- 1 | ``tornado.util`` --- General-purpose utilities 2 | ============================================== 3 | 4 | .. testsetup:: 5 | 6 | from tornado.util import * 7 | 8 | .. automodule:: tornado.util 9 | :members: 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | *.so 4 | *.class 5 | *~ 6 | build/ 7 | /dist/ 8 | MANIFEST 9 | /tornado.egg-info/ 10 | .tox/ 11 | .vagrant 12 | /.coverage 13 | /htmlcov/ 14 | /env/ 15 | # Used in demo apps 16 | secrets.cfg 17 | .mypy_cache/ 18 | -------------------------------------------------------------------------------- /docs/stack_context.rst: -------------------------------------------------------------------------------- 1 | ``tornado.stack_context`` --- Exception handling across asynchronous callbacks 2 | ============================================================================== 3 | 4 | .. automodule:: tornado.stack_context 5 | :members: 6 | -------------------------------------------------------------------------------- /demos/appengine/app.yaml: -------------------------------------------------------------------------------- 1 | application: tornado-appengine 2 | version: 2 3 | runtime: python27 4 | api_version: 1 5 | threadsafe: yes 6 | 7 | handlers: 8 | - url: /static/ 9 | static_dir: static 10 | 11 | - url: /.* 12 | script: blog.application 13 | -------------------------------------------------------------------------------- /docs/guide.rst: -------------------------------------------------------------------------------- 1 | User's guide 2 | ============ 3 | 4 | .. toctree:: 5 | 6 | guide/intro 7 | guide/async 8 | guide/coroutines 9 | guide/queues 10 | guide/structure 11 | guide/templates 12 | guide/security 13 | guide/running 14 | -------------------------------------------------------------------------------- /docs/httputil.rst: -------------------------------------------------------------------------------- 1 | ``tornado.httputil`` --- Manipulate HTTP headers and URLs 2 | ========================================================= 3 | 4 | .. testsetup:: 5 | 6 | from tornado.httputil import * 7 | 8 | .. automodule:: tornado.httputil 9 | :members: 10 | -------------------------------------------------------------------------------- /docs/httpserver.rst: -------------------------------------------------------------------------------- 1 | ``tornado.httpserver`` --- Non-blocking HTTP server 2 | =================================================== 3 | 4 | .. automodule:: tornado.httpserver 5 | 6 | HTTP Server 7 | ----------- 8 | .. autoclass:: HTTPServer 9 | :members: 10 | -------------------------------------------------------------------------------- /maint/test/redbot/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27 3 | setupdir=../../.. 4 | 5 | [testenv] 6 | commands = python red_test.py 7 | deps = 8 | # Newer versions of thor have a bug with redbot (5/18/13) 9 | thor==0.2.0 10 | git+https://github.com/mnot/redbot.git 11 | -------------------------------------------------------------------------------- /maint/vm/ubuntu14.04/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant::Config.run do |config| 2 | config.vm.box = "ubuntu/trusty64" 3 | 4 | config.vm.network :hostonly, "172.19.1.8" 5 | config.vm.share_folder("tornado", "/tornado", "../../..", :nfs=> true) 6 | 7 | config.vm.provision :shell, :path => "setup.sh" 8 | end -------------------------------------------------------------------------------- /docs/process.rst: -------------------------------------------------------------------------------- 1 | ``tornado.process`` --- Utilities for multiple processes 2 | ======================================================== 3 | 4 | .. automodule:: tornado.process 5 | :members: 6 | 7 | .. exception:: CalledProcessError 8 | 9 | An alias for `subprocess.CalledProcessError`. 10 | -------------------------------------------------------------------------------- /demos/twitter/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tornado Twitter Demo 4 | 5 | 6 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/releases/v4.4.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 4.4.1 2 | =========================== 3 | 4 | Jul 23, 2016 5 | ------------ 6 | 7 | `tornado.web` 8 | ~~~~~~~~~~~~~ 9 | 10 | * Fixed a regression in Tornado 4.4 which caused URL regexes 11 | containing backslash escapes outside capturing groups to be 12 | rejected. 13 | -------------------------------------------------------------------------------- /maint/test/pyuv/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27 3 | setupdir = ../../.. 4 | 5 | [testenv] 6 | commands = 7 | python -m tornado.test.runtests --ioloop=tornaduv.UVLoop {posargs:} 8 | # twisted tests don't work on pyuv IOLoop currently. 9 | deps = 10 | pyuv 11 | tornaduv 12 | futures 13 | mock 14 | -------------------------------------------------------------------------------- /docs/releases/v1.0.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 1.0.1 2 | =========================== 3 | 4 | Aug 13, 2010 5 | ------------ 6 | 7 | :: 8 | 9 | This release fixes a bug with RequestHandler.get_secure_cookie, which would 10 | in some circumstances allow an attacker to tamper with data stored in the 11 | cookie. 12 | -------------------------------------------------------------------------------- /maint/vm/shared-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run at the end of each vm's provisioning script 3 | 4 | set -e 5 | 6 | # Link tox.ini into the home directory so you can run tox immediately 7 | # after ssh'ing in without cd'ing to /vagrant (since cd'ing to /tornado 8 | # gets the wrong config) 9 | ln -sf /vagrant/tox.ini ~vagrant/tox.ini 10 | -------------------------------------------------------------------------------- /demos/blog/docker-compose.yml: -------------------------------------------------------------------------------- 1 | mysql: 2 | image: mysql:5.6 3 | environment: 4 | MYSQL_ROOT_PASSWORD: its_a_secret_to_everybody 5 | MYSQL_USER: blog 6 | MYSQL_PASSWORD: blog 7 | MYSQL_DATABASE: blog 8 | ports: 9 | - "3306" 10 | blog: 11 | build: . 12 | links: 13 | - mysql 14 | ports: 15 | - "8888:8888" 16 | -------------------------------------------------------------------------------- /docs/releases/v4.4.3.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 4.4.3 2 | =========================== 3 | 4 | Mar 30, 2017 5 | ------------ 6 | 7 | Bug fixes 8 | ~~~~~~~~~ 9 | 10 | * The `tornado.auth` module has been updated for compatibility with `a 11 | change to Facebook's access_token endpoint. 12 | `_ 13 | -------------------------------------------------------------------------------- /maint/test/websocket/fuzzingserver.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "url": "ws://localhost:9001", 4 | 5 | "options": {"failByDrop": false}, 6 | "outdir": "./reports/clients", 7 | "webport": 8080, 8 | 9 | "cases": ["*"], 10 | "exclude-cases": ["9.*", "12.*.1","12.2.*", "12.3.*", "12.4.*", "12.5.*", "13.*.1"], 11 | "exclude-agent-cases": {} 12 | } 13 | -------------------------------------------------------------------------------- /maint/vm/ubuntu12.04/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant::Config.run do |config| 2 | config.vm.box = "precise64" 3 | config.vm.box_url = "http://files.vagrantup.com/precise64.box" 4 | 5 | config.vm.network :hostonly, "172.19.1.5" 6 | config.vm.share_folder("tornado", "/tornado", "../../..", :nfs=> true) 7 | 8 | config.vm.provision :shell, :path => "setup.sh" 9 | end -------------------------------------------------------------------------------- /maint/test/redbot/README: -------------------------------------------------------------------------------- 1 | Redbot is an HTTP validator that checks for common problems, especially 2 | related to cacheability. These tests ensure that Tornado's default behavior 3 | is correct (but note that this guarantee does not automatically extend 4 | to applications built on Tornado since application behavior can impact 5 | cacheability. 6 | 7 | http://redbot.org/about -------------------------------------------------------------------------------- /maint/vm/freebsd/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | chsh -s bash vagrant 4 | 5 | PACKAGES=" 6 | curl 7 | python 8 | python34 9 | py27-pip 10 | py27-virtualenv 11 | " 12 | 13 | PIP_PACKAGES=" 14 | futures 15 | pycurl 16 | tox 17 | " 18 | 19 | ASSUME_ALWAYS_YES=true pkg install $PACKAGES 20 | 21 | pip install $PIP_PACKAGES 22 | 23 | /tornado/maint/vm/shared-setup.sh 24 | -------------------------------------------------------------------------------- /maint/test/websocket/tox.ini: -------------------------------------------------------------------------------- 1 | # We don't actually use tox to run this test, but it's the easiest way 2 | # to install autobahn and build the speedups module. 3 | # See run.sh for the real test runner. 4 | [tox] 5 | envlist = py27, py35, pypy 6 | setupdir=../../.. 7 | 8 | [testenv] 9 | commands = python -c pass 10 | 11 | [testenv:py27] 12 | deps = autobahntestsuite 13 | -------------------------------------------------------------------------------- /docs/releases/v4.5.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 4.5.1 2 | =========================== 3 | 4 | Apr 20, 2017 5 | ------------ 6 | 7 | `tornado.log` 8 | ~~~~~~~~~~~~~ 9 | 10 | - Improved detection of libraries for colorized logging. 11 | 12 | `tornado.httputil` 13 | ~~~~~~~~~~~~~~~~~~ 14 | 15 | - `.url_concat` once again treats None as equivalent to an empty sequence. 16 | -------------------------------------------------------------------------------- /demos/blog/templates/create_author.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body %} 4 |
5 | Email:
6 | Name:
7 | Password:
8 | {% module xsrf_form_html() %} 9 | 10 |
11 | {% end %} 12 | -------------------------------------------------------------------------------- /docs/releases/v4.2.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 4.2.1 2 | =========================== 3 | 4 | Jul 17, 2015 5 | ------------ 6 | 7 | Security fix 8 | ~~~~~~~~~~~~ 9 | 10 | * This release fixes a path traversal vulnerability in `.StaticFileHandler`, 11 | in which files whose names *started with* the ``static_path`` directory 12 | but were not actually *in* that directory could be accessed. 13 | -------------------------------------------------------------------------------- /maint/vm/freebsd/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist=py27-full, py27, py34 3 | setupdir=/tornado 4 | # /home is a symlink to /usr/home, but tox doesn't like symlinks here 5 | toxworkdir=/usr/home/vagrant/tox-tornado 6 | 7 | [testenv] 8 | commands = python -m tornado.test.runtests {posargs:} 9 | 10 | [testenv:py27-full] 11 | # twisted's tests fail on freebsd 12 | deps = 13 | futures 14 | pycurl 15 | -------------------------------------------------------------------------------- /docs/releases/v3.1.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 3.1.1 2 | =========================== 3 | 4 | Sep 1, 2013 5 | ----------- 6 | 7 | * `.StaticFileHandler` no longer fails if the client requests a ``Range`` that 8 | is larger than the entire file (Facebook has a crawler that does this). 9 | * `.RequestHandler.on_connection_close` now works correctly on subsequent 10 | requests of a keep-alive connection. 11 | -------------------------------------------------------------------------------- /demos/blog/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body %} 4 | {% if error %} 5 | Error: {{ error }}

6 | {% end %} 7 | 8 |

9 | Email:
10 | Password:
11 | {% module xsrf_form_html() %} 12 | 13 |
14 | {% end %} 15 | -------------------------------------------------------------------------------- /demos/blog/templates/modules/entry.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ entry.title }}

3 |
{{ locale.format_date(entry.published, full_format=True, shorter=True) }}
4 |
{% raw entry.html %}
5 | {% if current_user %} 6 | 7 | {% end %} 8 |
9 | -------------------------------------------------------------------------------- /maint/test/cython/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | try: 4 | import Cython.Build 5 | except: 6 | Cython = None 7 | 8 | if Cython is None: 9 | ext_modules = None 10 | else: 11 | ext_modules=Cython.Build.cythonize('cythonapp.pyx') 12 | 13 | setup( 14 | name='cythonapp', 15 | py_modules=['cythonapp_test', 'pythonmodule'], 16 | ext_modules=ext_modules, 17 | setup_requires='Cython>=0.23.1', 18 | ) 19 | -------------------------------------------------------------------------------- /demos/facebook/README: -------------------------------------------------------------------------------- 1 | Running the Tornado Facebook example 2 | ==================================== 3 | 4 | To run this example, you must register a Facebook application with a 5 | Connect URL set to the domain the this demo will be running on 6 | (i.e. http://localhost:8888/ by default). The API key and secret 7 | for this application must be passed on the command line: 8 | 9 | python facebook.py --facebook_api_key=ABC --facebook_secret=XYZ 10 | -------------------------------------------------------------------------------- /demos/blog/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:2.7 2 | 3 | EXPOSE 8888 4 | 5 | RUN apt-get update && apt-get install -y mysql-client 6 | 7 | # based on python:2.7-onbuild, but if we use that image directly 8 | # the above apt-get line runs too late. 9 | RUN mkdir -p /usr/src/app 10 | WORKDIR /usr/src/app 11 | 12 | COPY requirements.txt /usr/src/app/ 13 | RUN pip install -r requirements.txt 14 | 15 | COPY . /usr/src/app 16 | 17 | CMD python blog.py --mysql_host=mysql 18 | -------------------------------------------------------------------------------- /maint/test/websocket/run-client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | tox 6 | 7 | .tox/py27/bin/wstest -m fuzzingserver & 8 | FUZZING_SERVER_PID=$! 9 | 10 | sleep 1 11 | 12 | .tox/py27/bin/python client.py --name='Tornado/py27' 13 | .tox/py35/bin/python client.py --name='Tornado/py35' 14 | .tox/pypy/bin/python client.py --name='Tornado/pypy' 15 | 16 | kill $FUZZING_SERVER_PID 17 | wait 18 | 19 | echo "Tests complete. Output is in ./reports/clients/index.html" 20 | -------------------------------------------------------------------------------- /demos/appengine/templates/modules/entry.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ entry.title }}

3 |
{{ locale.format_date(entry.published, full_format=True, shorter=True) }}
4 |
{% raw entry.html %}
5 | {% if current_user and current_user.administrator %} 6 | 7 | {% end %} 8 |
9 | -------------------------------------------------------------------------------- /tornado/test/__main__.py: -------------------------------------------------------------------------------- 1 | """Shim to allow python -m tornado.test. 2 | 3 | This only works in python 2.7+. 4 | """ 5 | from __future__ import absolute_import, division, print_function 6 | 7 | from tornado.test.runtests import all, main 8 | 9 | # tornado.testing.main autodiscovery relies on 'all' being present in 10 | # the main module, so import it here even though it is not used directly. 11 | # The following line prevents a pyflakes warning. 12 | all = all 13 | 14 | main() 15 | -------------------------------------------------------------------------------- /docs/releases/v3.0.2.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 3.0.2 2 | =========================== 3 | 4 | Jun 2, 2013 5 | ----------- 6 | 7 | * `tornado.auth.TwitterMixin` now defaults to version 1.1 of the 8 | Twitter API, instead of version 1.0 which is being `discontinued on 9 | June 11 `_. It also now uses HTTPS 10 | when talking to Twitter. 11 | * Fixed a potential memory leak with a long chain of `.gen.coroutine` 12 | or `.gen.engine` functions. 13 | -------------------------------------------------------------------------------- /maint/test/appengine/README: -------------------------------------------------------------------------------- 1 | Unit test support for app engine. Currently very limited as most of 2 | our tests depend on direct network access, but these tests ensure that the 3 | modules that are supposed to work on app engine don't depend on any 4 | forbidden modules. 5 | 6 | The code lives in maint/appengine/common, but should be run from the py25 7 | or py27 subdirectories (which contain an app.yaml and a bunch of symlinks). 8 | runtests.py is the entry point; cgi_runtests.py is used internally. 9 | -------------------------------------------------------------------------------- /maint/scripts/custom_fixers/fix_unicode_literal.py: -------------------------------------------------------------------------------- 1 | from lib2to3 import fixer_base 2 | from lib2to3.fixer_util import String 3 | 4 | 5 | class FixUnicodeLiteral(fixer_base.BaseFix): 6 | BM_compatible = True 7 | PATTERN = """ 8 | power< 'u' 9 | trailer< 10 | '(' 11 | arg=any 12 | ')' 13 | > 14 | > 15 | """ 16 | 17 | def transform(self, node, results): 18 | arg = results["arg"] 19 | node.replace(String('u'+arg.value, prefix=node.prefix)) 20 | -------------------------------------------------------------------------------- /maint/test/appengine/tox.ini: -------------------------------------------------------------------------------- 1 | # App Engine tests require the SDK to be installed separately. 2 | # Version 1.6.1 or newer is required (older versions don't work when 3 | # python is run from a virtualenv) 4 | # 5 | # These are currently excluded from the main tox.ini because their 6 | # logs are spammy and they're a little flaky. 7 | [tox] 8 | envlist = py27-appengine 9 | 10 | [testenv] 11 | changedir = {toxworkdir} 12 | 13 | [testenv:py27-appengine] 14 | basepython = python2.7 15 | commands = python {toxinidir}/py27/runtests.py {posargs:} 16 | -------------------------------------------------------------------------------- /docs/releases/v2.4.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 2.4.1 2 | =========================== 3 | 4 | Nov 24, 2012 5 | ------------ 6 | 7 | Bug fixes 8 | ~~~~~~~~~ 9 | 10 | * Fixed a memory leak in `tornado.stack_context` that was especially likely 11 | with long-running ``@gen.engine`` functions. 12 | * `tornado.auth.TwitterMixin` now works on Python 3. 13 | * Fixed a bug in which ``IOStream.read_until_close`` with a streaming callback 14 | would sometimes pass the last chunk of data to the final callback instead 15 | of the streaming callback. 16 | -------------------------------------------------------------------------------- /tornado/test/resolve_test_helper.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, division, print_function 2 | from tornado.ioloop import IOLoop 3 | from tornado.netutil import ThreadedResolver 4 | 5 | # When this module is imported, it runs getaddrinfo on a thread. Since 6 | # the hostname is unicode, getaddrinfo attempts to import encodings.idna 7 | # but blocks on the import lock. Verify that ThreadedResolver avoids 8 | # this deadlock. 9 | 10 | resolver = ThreadedResolver() 11 | IOLoop.current().run_sync(lambda: resolver.resolve(u'localhost', 80)) 12 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | # Test coverage configuration. 2 | # Usage: 3 | # pip install coverage 4 | # coverage erase # clears previous data if any 5 | # coverage run -m tornado.test.runtests 6 | # coverage report # prints to stdout 7 | # coverage html # creates ./htmlcov/*.html including annotated source 8 | [run] 9 | branch = true 10 | source = tornado 11 | omit = 12 | tornado/platform/* 13 | tornado/test/* 14 | */_auto2to3* 15 | 16 | [report] 17 | # Ignore missing source files, i.e. fake template-generated "files" 18 | ignore_errors = true 19 | -------------------------------------------------------------------------------- /maint/test/cython/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | # This currently segfaults on pypy. 3 | envlist = py27,py33,py34,py35,py36 4 | 5 | [testenv] 6 | deps = 7 | ../../.. 8 | Cython>=0.23.3 9 | backports_abc>=0.4 10 | singledispatch 11 | commands = python -m unittest cythonapp_test 12 | # Most of these are defaults, but if you specify any you can't fall back 13 | # defaults for the others. 14 | basepython = 15 | py27: python2.7 16 | py33: python3.3 17 | py34: python3.4 18 | py35: python3.5 19 | py36: python3.6 20 | -------------------------------------------------------------------------------- /runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run the Tornado test suite. 3 | # 4 | # Also consider using tox, which uses virtualenv to run the test suite 5 | # under multiple versions of python. 6 | 7 | cd $(dirname $0) 8 | 9 | # "python -m" differs from "python tornado/test/runtests.py" in how it sets 10 | # up the default python path. "python -m" uses the current directory, 11 | # while "python file.py" uses the directory containing "file.py" (which is 12 | # not what you want if file.py appears within a package you want to import 13 | # from) 14 | python -m tornado.test.runtests "$@" 15 | -------------------------------------------------------------------------------- /docs/wsgi.rst: -------------------------------------------------------------------------------- 1 | ``tornado.wsgi`` --- Interoperability with other Python frameworks and servers 2 | ============================================================================== 3 | 4 | .. automodule:: tornado.wsgi 5 | 6 | Running Tornado apps on WSGI servers 7 | ------------------------------------ 8 | 9 | .. autoclass:: WSGIAdapter 10 | :members: 11 | 12 | .. autoclass:: WSGIApplication 13 | :members: 14 | 15 | Running WSGI apps on Tornado servers 16 | ------------------------------------ 17 | 18 | .. autoclass:: WSGIContainer 19 | :members: 20 | -------------------------------------------------------------------------------- /maint/scripts/run_autopep8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Runs autopep8 in the configuration used for tornado. 4 | # 5 | # W602 is "deprecated form of raising exception", but the fix is incorrect 6 | # (and I'm not sure if the three-argument form of raise is really deprecated 7 | # in the first place) 8 | # E501 is "line longer than 80 chars" but the automated fix is ugly. 9 | # E301 adds a blank line between docstring and first method 10 | # E309 adds a blank line between class declaration and docstring (?) 11 | autopep8 --ignore=W602,E501,E301,E309 -i tornado/*.py tornado/platform/*.py tornado/test/*.py 12 | -------------------------------------------------------------------------------- /docs/twisted.rst: -------------------------------------------------------------------------------- 1 | ``tornado.platform.twisted`` --- Bridges between Twisted and Tornado 2 | ======================================================================== 3 | 4 | .. automodule:: tornado.platform.twisted 5 | 6 | Twisted on Tornado 7 | ------------------ 8 | 9 | .. autoclass:: TornadoReactor 10 | :members: 11 | 12 | .. autofunction:: install 13 | 14 | Tornado on Twisted 15 | ------------------ 16 | 17 | .. autoclass:: TwistedIOLoop 18 | :members: 19 | 20 | Twisted DNS resolver 21 | -------------------- 22 | 23 | .. autoclass:: TwistedResolver 24 | :members: 25 | -------------------------------------------------------------------------------- /maint/test/websocket/fuzzingclient.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": {"failByDrop": false}, 3 | "outdir": "./reports/servers", 4 | 5 | "servers": [ 6 | {"agent": "Tornado/py27", "url": "ws://localhost:9001", 7 | "options": {"version": 18}}, 8 | {"agent": "Tornado/py35", "url": "ws://localhost:9002", 9 | "options": {"version": 18}}, 10 | {"agent": "Tornado/pypy", "url": "ws://localhost:9003", 11 | "options": {"version": 18}} 12 | ], 13 | 14 | "cases": ["*"], 15 | "exclude-cases": ["9.*", "12.*.1","12.2.*", "12.3.*", "12.4.*", "12.5.*", "13.*.1"], 16 | "exclude-agent-cases": {} 17 | } 18 | -------------------------------------------------------------------------------- /docs/template.rst: -------------------------------------------------------------------------------- 1 | ``tornado.template`` --- Flexible output generation 2 | =================================================== 3 | 4 | .. automodule:: tornado.template 5 | 6 | Class reference 7 | --------------- 8 | 9 | .. autoclass:: Template(template_string, name="", loader=None, compress_whitespace=None, autoescape="xhtml_escape", whitespace=None) 10 | :members: 11 | 12 | .. autoclass:: BaseLoader 13 | :members: 14 | 15 | .. autoclass:: Loader 16 | :members: 17 | 18 | .. autoclass:: DictLoader 19 | :members: 20 | 21 | .. autoexception:: ParseError 22 | 23 | .. autofunction:: filter_whitespace 24 | -------------------------------------------------------------------------------- /docs/releases/v2.2.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 2.2.1 2 | =========================== 3 | 4 | Apr 23, 2012 5 | ------------ 6 | 7 | Security fixes 8 | ~~~~~~~~~~~~~~ 9 | 10 | * `tornado.web.RequestHandler.set_header` now properly sanitizes input 11 | values to protect against header injection, response splitting, etc. 12 | (it has always attempted to do this, but the check was incorrect). 13 | Note that redirects, the most likely source of such bugs, are protected 14 | by a separate check in `.RequestHandler.redirect`. 15 | 16 | Bug fixes 17 | ~~~~~~~~~ 18 | 19 | * Colored logging configuration in `tornado.options` is compatible with 20 | Python 3.2.3 (and 3.3). 21 | -------------------------------------------------------------------------------- /docs/queues.rst: -------------------------------------------------------------------------------- 1 | ``tornado.queues`` -- Queues for coroutines 2 | =========================================== 3 | 4 | .. versionadded:: 4.2 5 | 6 | .. automodule:: tornado.queues 7 | 8 | Classes 9 | ------- 10 | 11 | Queue 12 | ^^^^^ 13 | .. autoclass:: Queue 14 | :members: 15 | 16 | PriorityQueue 17 | ^^^^^^^^^^^^^ 18 | .. autoclass:: PriorityQueue 19 | :members: 20 | 21 | LifoQueue 22 | ^^^^^^^^^ 23 | .. autoclass:: LifoQueue 24 | :members: 25 | 26 | Exceptions 27 | ---------- 28 | 29 | QueueEmpty 30 | ^^^^^^^^^^ 31 | .. autoexception:: QueueEmpty 32 | 33 | QueueFull 34 | ^^^^^^^^^ 35 | .. autoexception:: QueueFull 36 | -------------------------------------------------------------------------------- /maint/vm/ubuntu14.04/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | apt-get update 6 | 7 | # libcurl4-gnutls-dev is the default if you ask for libcurl4-dev, but it 8 | # has bugs that make our tests deadlock (the relevant tests detect this and 9 | # disable themselves, but it means that to get full coverage we have to use 10 | # the openssl version). 11 | APT_PACKAGES=" 12 | python-pip 13 | python-dev 14 | python3-pycurl 15 | libcurl4-openssl-dev 16 | " 17 | 18 | apt-get -y install $APT_PACKAGES 19 | 20 | # Ubuntu 14.04 includes python 2.7 and 3.4. 21 | 22 | PIP_PACKAGES=" 23 | futures 24 | pycurl 25 | tox 26 | twisted 27 | virtualenv 28 | " 29 | 30 | pip install $PIP_PACKAGES 31 | 32 | /tornado/maint/vm/shared-setup.sh 33 | -------------------------------------------------------------------------------- /maint/requirements.in: -------------------------------------------------------------------------------- 1 | # Requirements for tools used in the development of tornado. 2 | # This list is for python 3.5; for 2.7 add: 3 | # - backports.ssl-match-hostname 4 | # - futures 5 | # - mock 6 | # - certifi 7 | # 8 | # Use virtualenv instead of venv; tox seems to get confused otherwise. 9 | # 10 | # maint/requirements.txt contains the pinned versions of all direct and 11 | # indirect dependencies; this file only contains direct dependencies 12 | # and is useful for upgrading. 13 | 14 | # Tornado's optional dependencies 15 | Twisted 16 | pycares 17 | pycurl 18 | 19 | # Other useful tools 20 | Sphinx 21 | autopep8 22 | coverage 23 | flake8 24 | pep8 25 | pyflakes 26 | sphinx-rtd-theme 27 | tox 28 | twine 29 | virtualenv 30 | -------------------------------------------------------------------------------- /docs/options.rst: -------------------------------------------------------------------------------- 1 | ``tornado.options`` --- Command-line parsing 2 | ============================================ 3 | 4 | .. automodule:: tornado.options 5 | 6 | 7 | 8 | Global functions 9 | ---------------- 10 | 11 | .. autofunction:: define 12 | 13 | .. py:data:: options 14 | 15 | Global options object. All defined options are available as attributes 16 | on this object. 17 | 18 | .. autofunction:: parse_command_line 19 | .. autofunction:: parse_config_file 20 | .. autofunction:: print_help(file=sys.stderr) 21 | .. autofunction:: add_parse_callback 22 | .. autoexception:: Error 23 | 24 | OptionParser class 25 | ------------------ 26 | 27 | .. autoclass:: OptionParser 28 | :members: 29 | -------------------------------------------------------------------------------- /demos/facebook/templates/stream.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tornado Facebook Stream Demo 6 | 7 | 8 | 9 |
10 |
11 | {{ escape(current_user["name"]) }} - 12 | {{ _("Sign out") }} 13 |
14 |
{{ _("Refresh stream") }}
15 |
16 | {% for post in stream["data"] %} 17 | {{ modules.Post(post) }} 18 | {% end %} 19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /demos/tcpecho/README.md: -------------------------------------------------------------------------------- 1 | TCP echo demo 2 | ============= 3 | 4 | This demo shows how to use Tornado's asynchronous TCP client and 5 | server by implementing `handle_stream` as a coroutine. 6 | 7 | To run the server: 8 | 9 | ``` 10 | $ python server.py 11 | ``` 12 | 13 | The client will send the message given with the `--message` option 14 | (which defaults to "ping"), wait for a response, then quit. To run: 15 | 16 | ``` 17 | $ python client.py --message="your message here" 18 | ``` 19 | 20 | Alternatively, you can interactively send messages to the echo server 21 | with a telnet client. For example: 22 | 23 | ``` 24 | $ telnet localhost 9888 25 | Trying ::1... 26 | Connected to localhost. 27 | Escape character is '^]'. 28 | ping 29 | ping 30 | ``` 31 | -------------------------------------------------------------------------------- /maint/test/websocket/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from tornado.ioloop import IOLoop 4 | from tornado.options import define, options, parse_command_line 5 | from tornado.websocket import WebSocketHandler 6 | from tornado.web import Application 7 | 8 | define('port', default=9000) 9 | 10 | class EchoHandler(WebSocketHandler): 11 | def on_message(self, message): 12 | self.write_message(message, binary=isinstance(message, bytes)) 13 | 14 | def get_compression_options(self): 15 | return {} 16 | 17 | if __name__ == '__main__': 18 | parse_command_line() 19 | app = Application([ 20 | ('/', EchoHandler), 21 | ]) 22 | app.listen(options.port, address='127.0.0.1') 23 | IOLoop.instance().start() 24 | -------------------------------------------------------------------------------- /maint/vm/windows/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27-full, py27, py33, py27-opt, py33-monotonic 3 | setupdir = e:\ 4 | toxworkdir = c:\tox-tornado 5 | 6 | [testenv] 7 | commands = python -m tornado.test.runtests {posargs:} 8 | 9 | [testenv:py27-full] 10 | basepython = python2.7 11 | deps = 12 | futures 13 | mock 14 | 15 | [testenv:py33] 16 | # tox's path mappings haven't been updated for py33 yet. 17 | basepython = c:\python33\python.exe 18 | 19 | [testenv:py33-monotonic] 20 | basepython = c:\python33\python.exe 21 | commands = python -m tornado.test.runtests --ioloop_time_monotonic {posargs:} 22 | 23 | [testenv:py27-opt] 24 | basepython = python2.7 25 | deps = 26 | futures 27 | mock 28 | commands = python -O -m tornado.test.runtests {posargs:} 29 | -------------------------------------------------------------------------------- /tornado/test/static/sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 2008 6 | 141100 7 | 8 | 9 | 10 | 11 | 4 12 | 2011 13 | 59900 14 | 15 | 16 | 17 | 68 18 | 2011 19 | 13600 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tornado/platform/windows.py: -------------------------------------------------------------------------------- 1 | # NOTE: win32 support is currently experimental, and not recommended 2 | # for production use. 3 | 4 | 5 | from __future__ import absolute_import, division, print_function 6 | import ctypes # type: ignore 7 | import ctypes.wintypes # type: ignore 8 | 9 | # See: http://msdn.microsoft.com/en-us/library/ms724935(VS.85).aspx 10 | SetHandleInformation = ctypes.windll.kernel32.SetHandleInformation 11 | SetHandleInformation.argtypes = (ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD) 12 | SetHandleInformation.restype = ctypes.wintypes.BOOL 13 | 14 | HANDLE_FLAG_INHERIT = 0x00000001 15 | 16 | 17 | def set_close_exec(fd): 18 | success = SetHandleInformation(fd, HANDLE_FLAG_INHERIT, 0) 19 | if not success: 20 | raise ctypes.WinError() 21 | -------------------------------------------------------------------------------- /tornado/test/gettext_translations/extract_me.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Dummy source file to allow creation of the initial .po file in the 3 | # same way as a real project. I'm not entirely sure about the real 4 | # workflow here, but this seems to work. 5 | # 6 | # 1) xgettext --language=Python --keyword=_:1,2 --keyword=pgettext:1c,2 --keyword=pgettext:1c,2,3 extract_me.py -o tornado_test.po 7 | # 2) Edit tornado_test.po, setting CHARSET, Plural-Forms and setting msgstr 8 | # 3) msgfmt tornado_test.po -o tornado_test.mo 9 | # 4) Put the file in the proper location: $LANG/LC_MESSAGES 10 | 11 | from __future__ import absolute_import, division, print_function 12 | _("school") 13 | pgettext("law", "right") 14 | pgettext("good", "right") 15 | pgettext("organization", "club", "clubs", 1) 16 | pgettext("stick", "club", "clubs", 1) 17 | -------------------------------------------------------------------------------- /maint/vm/ubuntu12.04/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27-full, py27, py27-select, py27-twisted 3 | setupdir=/tornado 4 | toxworkdir=/home/vagrant/tox-tornado 5 | 6 | [testenv] 7 | commands = python -m tornado.test.runtests {posargs:} 8 | 9 | [testenv:py27-full] 10 | basepython = python2.7 11 | deps = 12 | futures 13 | pycurl 14 | twisted==12.2.0 15 | 16 | [testenv:py27-select] 17 | basepython = python2.7 18 | deps = 19 | futures 20 | pycurl 21 | twisted==12.2.0 22 | commands = python -m tornado.test.runtests --ioloop=tornado.platform.select.SelectIOLoop {posargs:} 23 | 24 | [testenv:py27-twisted] 25 | basepython = python2.7 26 | deps = 27 | futures 28 | pycurl 29 | twisted==12.2.0 30 | commands = python -m tornado.test.runtests --ioloop=tornado.platform.twisted.TwistedIOLoop {posargs:} 31 | -------------------------------------------------------------------------------- /demos/blog/templates/archive.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block head %} 4 | 20 | {% end %} 21 | 22 | {% block body %} 23 |
    24 | {% for entry in entries %} 25 |
  • 26 | 27 |
    {{ locale.format_date(entry.published, full_format=True, shorter=True) }}
    28 |
  • 29 | {% end %} 30 |
31 | {% end %} 32 | -------------------------------------------------------------------------------- /maint/requirements.txt: -------------------------------------------------------------------------------- 1 | alabaster==0.7.10 2 | appdirs==1.4.3 3 | args==0.1.0 4 | attrs==16.3.0 5 | Automat==0.5.0 6 | autopep8==1.3.1 7 | Babel==2.4.0 8 | clint==0.5.1 9 | constantly==15.1.0 10 | coverage==4.3.4 11 | docutils==0.13.1 12 | flake8==3.3.0 13 | imagesize==0.7.1 14 | incremental==16.10.1 15 | Jinja2==2.9.6 16 | MarkupSafe==1.0 17 | mccabe==0.6.1 18 | packaging==16.8 19 | pep8==1.7.0 20 | pkginfo==1.4.1 21 | pluggy==0.4.0 22 | py==1.4.33 23 | pycares==2.1.1 24 | pycodestyle==2.3.1 25 | pycurl==7.43.0 26 | pyflakes==1.5.0 27 | Pygments==2.2.0 28 | pyparsing==2.2.0 29 | pytz==2017.2 30 | requests==2.13.0 31 | requests-toolbelt==0.7.1 32 | six==1.10.0 33 | snowballstemmer==1.2.1 34 | Sphinx==1.5.5 35 | sphinx-rtd-theme==0.2.4 36 | tox==2.7.0 37 | twine==1.8.1 38 | Twisted==17.1.0 39 | virtualenv==15.1.0 40 | zope.interface==4.3.3 41 | -------------------------------------------------------------------------------- /demos/appengine/templates/archive.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block head %} 4 | 20 | {% end %} 21 | 22 | {% block body %} 23 |
    24 | {% for entry in entries %} 25 |
  • 26 | 27 |
    {{ locale.format_date(entry.published, full_format=True, shorter=True) }}
    28 |
  • 29 | {% end %} 30 |
31 | {% end %} 32 | -------------------------------------------------------------------------------- /docs/releases/v1.2.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 1.2.1 2 | =========================== 3 | 4 | Mar 3, 2011 5 | ----------- 6 | 7 | :: 8 | 9 | We are pleased to announce the release of Tornado 1.2.1, available from 10 | https://github.com/downloads/facebook/tornado/tornado-1.2.1.tar.gz 11 | 12 | This release contains only two small changes relative to version 1.2: 13 | * FacebookGraphMixin has been updated to work with a recent change to the 14 | Facebook API. 15 | * Running "setup.py install" will no longer attempt to automatically 16 | install pycurl. This wasn't working well on platforms where the best way 17 | to install pycurl is via something like apt-get instead of easy_install. 18 | 19 | This is an important upgrade if you are using FacebookGraphMixin, but 20 | otherwise it can be safely ignored. 21 | -------------------------------------------------------------------------------- /docs/releases/v4.4.2.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 4.4.2 2 | =========================== 3 | 4 | Oct 1, 2016 5 | ------------ 6 | 7 | Security fixes 8 | ~~~~~~~~~~~~~~ 9 | 10 | * A difference in cookie parsing between Tornado and web browsers 11 | (especially when combined with Google Analytics) could allow an 12 | attacker to set arbitrary cookies and bypass XSRF protection. The 13 | cookie parser has been rewritten to fix this attack. 14 | 15 | Backwards-compatibility notes 16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 17 | 18 | * Cookies containing certain special characters (in particular semicolon 19 | and square brackets) are now parsed differently. 20 | * If the cookie header contains a combination of valid and invalid cookies, 21 | the valid ones will be returned (older versions of Tornado would reject the 22 | entire header for a single invalid cookie). 23 | -------------------------------------------------------------------------------- /demos/facebook/templates/modules/post.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {% set author_url="http://www.facebook.com/profile.php?id=" + escape(post["from"]["id"]) %} 4 | 5 |
6 |
7 | {{ escape(post["from"]["name"]) }} 8 | {% if "message" in post %} 9 | {{ escape(post["message"]) }} 10 | {% end %} 11 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /maint/vm/ubuntu14.04/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27-full, py34, py27, py27-select, py27-twisted 3 | setupdir=/tornado 4 | toxworkdir=/home/vagrant/tox-tornado 5 | 6 | [testenv] 7 | commands = python -m tornado.test.runtests {posargs:} 8 | 9 | [testenv:py27-full] 10 | basepython = python2.7 11 | deps = 12 | futures 13 | mock 14 | pycurl 15 | twisted==14.0.0 16 | 17 | [testenv:py27-select] 18 | basepython = python2.7 19 | deps = 20 | futures 21 | mock 22 | pycurl 23 | twisted==14.0.0 24 | commands = python -m tornado.test.runtests --ioloop=tornado.platform.select.SelectIOLoop {posargs:} 25 | 26 | [testenv:py27-twisted] 27 | basepython = python2.7 28 | deps = 29 | futures 30 | mock 31 | pycurl 32 | twisted==14.0.0 33 | commands = python -m tornado.test.runtests --ioloop=tornado.platform.twisted.TwistedIOLoop {posargs:} 34 | -------------------------------------------------------------------------------- /demos/tcpecho/client.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from tornado.ioloop import IOLoop 3 | from tornado import gen 4 | from tornado.tcpclient import TCPClient 5 | from tornado.options import options, define 6 | 7 | define("host", default="localhost", help="TCP server host") 8 | define("port", default=9888, help="TCP port to connect to") 9 | define("message", default="ping", help="Message to send") 10 | 11 | 12 | @gen.coroutine 13 | def send_message(): 14 | stream = yield TCPClient().connect(options.host, options.port) 15 | yield stream.write((options.message + "\n").encode()) 16 | print("Sent to server:", options.message) 17 | reply = yield stream.read_until(b"\n") 18 | print("Response from server:", reply.decode().strip()) 19 | 20 | 21 | if __name__ == "__main__": 22 | options.parse_command_line() 23 | IOLoop.current().run_sync(send_message) 24 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include demos *.py *.yaml *.html *.css *.js *.xml *.sql README 2 | recursive-include docs * 3 | prune docs/build 4 | include tornado/speedups.c 5 | include tornado/test/README 6 | include tornado/test/csv_translations/fr_FR.csv 7 | include tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.mo 8 | include tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.po 9 | include tornado/test/options_test.cfg 10 | include tornado/test/static/robots.txt 11 | include tornado/test/static/sample.xml 12 | include tornado/test/static/sample.xml.gz 13 | include tornado/test/static/sample.xml.bz2 14 | include tornado/test/static/dir/index.html 15 | include tornado/test/static_foo.txt 16 | include tornado/test/templates/utf8.html 17 | include tornado/test/test.crt 18 | include tornado/test/test.key 19 | include LICENSE 20 | include README.rst 21 | include runtests.sh 22 | -------------------------------------------------------------------------------- /docs/caresresolver.rst: -------------------------------------------------------------------------------- 1 | ``tornado.platform.caresresolver`` --- Asynchronous DNS Resolver using C-Ares 2 | ============================================================================= 3 | 4 | .. module:: tornado.platform.caresresolver 5 | 6 | This module contains a DNS resolver using the c-ares library (and its 7 | wrapper ``pycares``). 8 | 9 | .. py:class:: CaresResolver 10 | 11 | Name resolver based on the c-ares library. 12 | 13 | This is a non-blocking and non-threaded resolver. It may not produce 14 | the same results as the system resolver, but can be used for non-blocking 15 | resolution when threads cannot be used. 16 | 17 | c-ares fails to resolve some names when ``family`` is ``AF_UNSPEC``, 18 | so it is only recommended for use in ``AF_INET`` (i.e. IPv4). This is 19 | the default for ``tornado.simple_httpclient``, but other libraries 20 | may default to ``AF_UNSPEC``. 21 | -------------------------------------------------------------------------------- /docs/releases.rst: -------------------------------------------------------------------------------- 1 | Release notes 2 | ============= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | releases/v4.5.1 8 | releases/v4.5.0 9 | releases/v4.4.3 10 | releases/v4.4.2 11 | releases/v4.4.1 12 | releases/v4.4.0 13 | releases/v4.3.0 14 | releases/v4.2.1 15 | releases/v4.2.0 16 | releases/v4.1.0 17 | releases/v4.0.2 18 | releases/v4.0.1 19 | releases/v4.0.0 20 | releases/v3.2.2 21 | releases/v3.2.1 22 | releases/v3.2.0 23 | releases/v3.1.1 24 | releases/v3.1.0 25 | releases/v3.0.2 26 | releases/v3.0.1 27 | releases/v3.0.0 28 | releases/v2.4.1 29 | releases/v2.4.0 30 | releases/v2.3.0 31 | releases/v2.2.1 32 | releases/v2.2.0 33 | releases/v2.1.1 34 | releases/v2.1.0 35 | releases/v2.0.0 36 | releases/v1.2.1 37 | releases/v1.2.0 38 | releases/v1.1.1 39 | releases/v1.1.0 40 | releases/v1.0.1 41 | releases/v1.0.0 42 | -------------------------------------------------------------------------------- /tornado/test/test.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICSDCCAbGgAwIBAgIJAN1oTowzMbkzMA0GCSqGSIb3DQEBBQUAMD0xCzAJBgNV 3 | BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRkwFwYDVQQKDBBUb3JuYWRvIFdl 4 | YiBUZXN0MB4XDTEwMDgyNTE4MjQ0NFoXDTIwMDgyMjE4MjQ0NFowPTELMAkGA1UE 5 | BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExGTAXBgNVBAoMEFRvcm5hZG8gV2Vi 6 | IFRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALirW3mX4jbdFse2aZwW 7 | zszCJ1IsRDrzALpbvMYLLbIZqo+Z8v5aERKTRQpXFqGaZyY+tdwYy7X7YXcLtKqv 8 | jnw/MSeIaqkw5pROKz5aR0nkPLvcTmhJVLVPCLc8dFnIlu8aC9TrDhr90P+PzU39 9 | UG7zLweA9zXKBuW3Tjo5dMP3AgMBAAGjUDBOMB0GA1UdDgQWBBRhJjMBYrzddCFr 10 | /0vvPyHMeqgo0TAfBgNVHSMEGDAWgBRhJjMBYrzddCFr/0vvPyHMeqgo0TAMBgNV 11 | HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGP6GaxSfb21bikcqaK3ZKCC1sRJ 12 | tiCuvJZbBUFUCAzl05dYUfJZim/oWK+GqyUkUB8ciYivUNnn9OtS7DnlTgT2ws2e 13 | lNgn5cuFXoAGcHXzVlHG3yoywYBf3y0Dn20uzrlLXUWJAzoSLOt2LTaXvwlgm7hF 14 | W1q8SQ6UBshRw2X0 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /maint/test/cython/cythonapp.pyx: -------------------------------------------------------------------------------- 1 | import cython 2 | from tornado import gen 3 | import pythonmodule 4 | 5 | async def native_coroutine(): 6 | x = await pythonmodule.hello() 7 | if x != "hello": 8 | raise ValueError("expected hello, got %r" % x) 9 | return "goodbye" 10 | 11 | @gen.coroutine 12 | def decorated_coroutine(): 13 | x = yield pythonmodule.hello() 14 | if x != "hello": 15 | raise ValueError("expected hello, got %r" % x) 16 | return "goodbye" 17 | 18 | # The binding directive is necessary for compatibility with 19 | # ArgReplacer (and therefore return_future), but only because 20 | # this is a static function. 21 | @cython.binding(True) 22 | def function_with_args(one, two, three): 23 | return (one, two, three) 24 | 25 | 26 | class AClass: 27 | # methods don't need the binding directive. 28 | def method_with_args(one, two, three): 29 | return (one, two, three) 30 | -------------------------------------------------------------------------------- /docs/releases/v4.0.2.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 4.0.2 2 | =========================== 3 | 4 | Sept 10, 2014 5 | ------------- 6 | 7 | Bug fixes 8 | ~~~~~~~~~ 9 | 10 | * Fixed a bug that could sometimes cause a timeout to fire after being 11 | cancelled. 12 | * `.AsyncTestCase` once again passes along arguments to test methods, 13 | making it compatible with extensions such as Nose's test generators. 14 | * `.StaticFileHandler` can again compress its responses when gzip is enabled. 15 | * ``simple_httpclient`` passes its ``max_buffer_size`` argument to the 16 | underlying stream. 17 | * Fixed a reference cycle that can lead to increased memory consumption. 18 | * `.add_accept_handler` will now limit the number of times it will call 19 | `~socket.socket.accept` per `.IOLoop` iteration, addressing a potential 20 | starvation issue. 21 | * Improved error handling in `.IOStream.connect` (primarily for FreeBSD 22 | systems) 23 | -------------------------------------------------------------------------------- /docs/releases/v4.0.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 4.0.1 2 | =========================== 3 | 4 | Aug 12, 2014 5 | ------------ 6 | 7 | * The build will now fall back to pure-python mode if the C extension 8 | fails to build for any reason (previously it would fall back for some 9 | errors but not others). 10 | * `.IOLoop.call_at` and `.IOLoop.call_later` now always return 11 | a timeout handle for use with `.IOLoop.remove_timeout`. 12 | * If any callback of a `.PeriodicCallback` or `.IOStream` returns a 13 | `.Future`, any error raised in that future will now be logged 14 | (similar to the behavior of `.IOLoop.add_callback`). 15 | * Fixed an exception in client-side websocket connections when the 16 | connection is closed. 17 | * ``simple_httpclient`` once again correctly handles 204 status 18 | codes with no content-length header. 19 | * Fixed a regression in ``simple_httpclient`` that would result in 20 | timeouts for certain kinds of errors. 21 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: sphinx 3 | 4 | # No -W for doctests because that disallows tests with empty output. 5 | SPHINX_DOCTEST_OPTS=-n -d build/doctress . 6 | SPHINXOPTS=-n -W -d build/doctrees . 7 | 8 | .PHONY: sphinx 9 | sphinx: 10 | sphinx-build -b html $(SPHINXOPTS) build/html 11 | 12 | .PHONY: coverage 13 | coverage: 14 | sphinx-build -b coverage ${SPHINXOPTS} build/coverage 15 | cat build/coverage/python.txt 16 | 17 | .PHONY: latex 18 | latex: 19 | sphinx-build -b latex $(SPHINXOPTS) build/latex 20 | 21 | # Building a pdf requires a latex installation. For macports, the needed 22 | # packages are texlive-latex-extra and texlive-fonts-recommended. 23 | # The output is in build/latex/tornado.pdf 24 | .PHONY: pdf 25 | pdf: latex 26 | cd build/latex && pdflatex -interaction=nonstopmode tornado.tex 27 | 28 | .PHONY: doctest 29 | doctest: 30 | sphinx-build -b doctest $(SPHINX_DOCTEST_OPTS) build/doctest 31 | 32 | clean: 33 | rm -rf build 34 | -------------------------------------------------------------------------------- /maint/test/websocket/run-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Runs the autobahn websocket conformance test against tornado in both 4 | # python2 and python3. Output goes in ./reports/servers/index.html. 5 | # 6 | # The --cases and --exclude arguments can be used to run only part of 7 | # the suite. The default is --exclude="9.*" to skip the relatively slow 8 | # performance tests; pass --exclude="" to override and include them. 9 | 10 | set -e 11 | 12 | # build/update the virtualenvs 13 | tox 14 | 15 | .tox/py27/bin/python server.py --port=9001 & 16 | PY27_SERVER_PID=$! 17 | 18 | .tox/py35/bin/python server.py --port=9002 & 19 | PY35_SERVER_PID=$! 20 | 21 | .tox/pypy/bin/python server.py --port=9003 & 22 | PYPY_SERVER_PID=$! 23 | 24 | sleep 1 25 | 26 | .tox/py27/bin/wstest -m fuzzingclient 27 | 28 | kill $PY27_SERVER_PID 29 | kill $PY35_SERVER_PID 30 | kill $PYPY_SERVER_PID 31 | wait 32 | 33 | echo "Tests complete. Output is in ./reports/servers/index.html" 34 | -------------------------------------------------------------------------------- /docs/concurrent.rst: -------------------------------------------------------------------------------- 1 | ``tornado.concurrent`` --- Work with threads and futures 2 | ======================================================== 3 | 4 | .. testsetup:: 5 | 6 | from tornado.concurrent import * 7 | from tornado import gen 8 | 9 | .. automodule:: tornado.concurrent 10 | :members: 11 | :exclude-members: Future, TracebackFuture 12 | 13 | .. autoclass:: Future 14 | 15 | Consumer methods 16 | ^^^^^^^^^^^^^^^^ 17 | 18 | .. automethod:: Future.result 19 | .. automethod:: Future.exception 20 | .. automethod:: Future.exc_info 21 | .. automethod:: Future.add_done_callback 22 | .. automethod:: Future.done 23 | .. automethod:: Future.running 24 | .. automethod:: Future.cancel 25 | .. automethod:: Future.cancelled 26 | 27 | Producer methods 28 | ^^^^^^^^^^^^^^^^ 29 | 30 | .. automethod:: Future.set_result 31 | .. automethod:: Future.set_exception 32 | .. automethod:: Future.set_exc_info 33 | -------------------------------------------------------------------------------- /docs/testing.rst: -------------------------------------------------------------------------------- 1 | ``tornado.testing`` --- Unit testing support for asynchronous code 2 | ================================================================== 3 | 4 | .. automodule:: tornado.testing 5 | 6 | Asynchronous test cases 7 | ----------------------- 8 | 9 | .. autoclass:: AsyncTestCase 10 | :members: 11 | 12 | .. autoclass:: AsyncHTTPTestCase 13 | :members: 14 | 15 | .. autoclass:: AsyncHTTPSTestCase 16 | :members: 17 | 18 | .. autofunction:: gen_test 19 | 20 | Controlling log output 21 | ---------------------- 22 | 23 | .. autoclass:: ExpectLog 24 | :members: 25 | 26 | .. autoclass:: LogTrapTestCase 27 | :members: 28 | 29 | Test runner 30 | ----------- 31 | 32 | .. autofunction:: main 33 | 34 | Helper functions 35 | ---------------- 36 | 37 | .. autofunction:: bind_unused_port 38 | 39 | .. autofunction:: get_unused_port 40 | 41 | .. autofunction:: get_async_test_timeout 42 | -------------------------------------------------------------------------------- /docs/releases/v1.1.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 1.1.1 2 | =========================== 3 | 4 | Feb 8, 2011 5 | ----------- 6 | 7 | :: 8 | 9 | Tornado 1.1.1 is a BACKWARDS-INCOMPATIBLE security update that fixes an 10 | XSRF vulnerability. It is available at 11 | https://github.com/downloads/facebook/tornado/tornado-1.1.1.tar.gz 12 | 13 | This is a backwards-incompatible change. Applications that previously 14 | relied on a blanket exception for XMLHTTPRequest may need to be modified 15 | to explicitly include the XSRF token when making ajax requests. 16 | 17 | The tornado chat demo application demonstrates one way of adding this 18 | token (specifically the function postJSON in demos/chat/static/chat.js). 19 | 20 | More information about this change and its justification can be found at 21 | http://www.djangoproject.com/weblog/2011/feb/08/security/ 22 | http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails 23 | -------------------------------------------------------------------------------- /tornado/test/windows_test.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, division, print_function 2 | import functools 3 | import os 4 | import socket 5 | import unittest 6 | 7 | from tornado.platform.auto import set_close_exec 8 | 9 | skipIfNonWindows = unittest.skipIf(os.name != 'nt', 'non-windows platform') 10 | 11 | 12 | @skipIfNonWindows 13 | class WindowsTest(unittest.TestCase): 14 | def test_set_close_exec(self): 15 | # set_close_exec works with sockets. 16 | s = socket.socket() 17 | self.addCleanup(s.close) 18 | set_close_exec(s.fileno()) 19 | 20 | # But it doesn't work with pipes. 21 | r, w = os.pipe() 22 | self.addCleanup(functools.partial(os.close, r)) 23 | self.addCleanup(functools.partial(os.close, w)) 24 | with self.assertRaises(WindowsError) as cm: 25 | set_close_exec(r) 26 | ERROR_INVALID_HANDLE = 6 27 | self.assertEqual(cm.exception.winerror, ERROR_INVALID_HANDLE) 28 | -------------------------------------------------------------------------------- /tornado/test/test.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALirW3mX4jbdFse2 3 | aZwWzszCJ1IsRDrzALpbvMYLLbIZqo+Z8v5aERKTRQpXFqGaZyY+tdwYy7X7YXcL 4 | tKqvjnw/MSeIaqkw5pROKz5aR0nkPLvcTmhJVLVPCLc8dFnIlu8aC9TrDhr90P+P 5 | zU39UG7zLweA9zXKBuW3Tjo5dMP3AgMBAAECgYEAiygNaWYrf95AcUQi9w00zpUr 6 | nj9fNvCwxr2kVbRMvd2balS/CC4EmXPCXdVcZ3B7dBVjYzSIJV0Fh/iZLtnVysD9 7 | fcNMZ+Cz71b/T0ItsNYOsJk0qUVyP52uqsqkNppIPJsD19C+ZeMLZj6iEiylZyl8 8 | 2U16c/kVIjER63mUEGkCQQDayQOTGPJrKHqPAkUqzeJkfvHH2yCf+cySU+w6ezyr 9 | j9yxcq8aZoLusCebDVT+kz7RqnD5JePFvB38cMuepYBLAkEA2BTFdZx30f4moPNv 10 | JlXlPNJMUTUzsXG7n4vNc+18O5ous0NGQII8jZWrIcTrP8wiP9fF3JwUsKrJhcBn 11 | xRs3hQJBAIDUgz1YIE+HW3vgi1gkOh6RPdBAsVpiXtr/fggFz3j60qrO7FswaAMj 12 | SX8c/6KUlBYkNjgP3qruFf4zcUNvEzcCQQCaioCPFVE9ByBpjLG6IUTKsz2R9xL5 13 | nfYqrbpLZ1aq6iLsYvkjugHE4X57sHLwNfdo4dHJbnf9wqhO2MVe25BhAkBdKYpY 14 | 7OKc/2mmMbJDhVBgoixz/muN/5VjdfbvVY48naZkJF1p1tmogqPC5F1jPCS4rM+S 15 | FfPJIHRNEn2oktw5 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /maint/vm/freebsd/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | config.vm.box = "chef/freebsd-10.0" 9 | 10 | config.vm.network "private_network", type: "dhcp" 11 | 12 | # Share an additional folder to the guest VM. The first argument is 13 | # the path on the host to the actual folder. The second argument is 14 | # the path on the guest to mount the folder. And the optional third 15 | # argument is a set of non-required options. 16 | config.vm.synced_folder "../../..", "/tornado", type: "nfs" 17 | 18 | # Override the default /vagrant mapping to use nfs, since freebsd doesn't 19 | # support other folder types. 20 | config.vm.synced_folder ".", "/vagrant", type: "nfs" 21 | 22 | config.ssh.shell = "/bin/sh" 23 | 24 | config.vm.provision :shell, :path => "setup.sh" 25 | end 26 | -------------------------------------------------------------------------------- /maint/vm/ubuntu12.04/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | apt-get update 6 | 7 | # libcurl4-gnutls-dev is the default if you ask for libcurl4-dev, but it 8 | # has bugs that make our tests deadlock (the relevant tests detect this and 9 | # disable themselves, but it means that to get full coverage we have to use 10 | # the openssl version). 11 | # The oddly-named python-software-properties includes add-apt-repository. 12 | APT_PACKAGES=" 13 | python-pip 14 | python-dev 15 | libcurl4-openssl-dev 16 | python-software-properties 17 | " 18 | 19 | apt-get -y install $APT_PACKAGES 20 | 21 | 22 | # Ubuntu 12.04 has python 2.7 as default; install more from here. 23 | add-apt-repository ppa:fkrull/deadsnakes 24 | apt-get update 25 | 26 | DEADSNAKES_PACKAGES=" 27 | python3.5 28 | python3.5-dev 29 | " 30 | apt-get -y install $DEADSNAKES_PACKAGES 31 | 32 | 33 | PIP_PACKAGES=" 34 | futures 35 | pycurl 36 | tox 37 | twisted 38 | virtualenv 39 | " 40 | 41 | pip install $PIP_PACKAGES 42 | 43 | /tornado/maint/vm/shared-setup.sh 44 | -------------------------------------------------------------------------------- /maint/vm/README: -------------------------------------------------------------------------------- 1 | This directory contains virtual machine setup scripts for testing Tornado. 2 | 3 | Requirements: 4 | 5 | Vagrant (http://vagrantup.com) and VirtualBox (http://virtualbox.org). 6 | Vagrant provides an easy download for Ubuntu images, base images for 7 | other platforms are harder to find and can be built with 8 | VeeWee (https://github.com/jedi4ever/veewee). 9 | 10 | Usage: 11 | 12 | cd to the appropriate directory and run `vagrant up`, then `vagrant ssh`. 13 | From there, simply run `tox` to run the full test suite, or cd to /tornado 14 | and test manually. Afterwards, use `vagrant suspend` or `vagrant destroy` 15 | to clean up. 16 | 17 | Notes: 18 | 19 | Python distutils (and therefore tox) assume that if the platform 20 | supports hard links, they can be used in the Tornado source directory. 21 | VirtualBox's shared folder filesystem does not support hard links (or 22 | symlinks), so we have to use NFS shared folders instead. (which has 23 | the unfortunate side effect of requiring sudo on the host machine) 24 | -------------------------------------------------------------------------------- /tornado/platform/epoll.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2012 Facebook 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | """EPoll-based IOLoop implementation for Linux systems.""" 17 | from __future__ import absolute_import, division, print_function 18 | 19 | import select 20 | 21 | from tornado.ioloop import PollIOLoop 22 | 23 | 24 | class EPollIOLoop(PollIOLoop): 25 | def initialize(self, **kwargs): 26 | super(EPollIOLoop, self).initialize(impl=select.epoll(), **kwargs) 27 | -------------------------------------------------------------------------------- /docs/releases/v3.2.2.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 3.2.2 2 | =========================== 3 | 4 | June 3, 2014 5 | ------------ 6 | 7 | Security fixes 8 | ~~~~~~~~~~~~~~ 9 | 10 | * The XSRF token is now encoded with a random mask on each request. 11 | This makes it safe to include in compressed pages without being 12 | vulnerable to the `BREACH attack `_. 13 | This applies to most applications that use both the ``xsrf_cookies`` 14 | and ``gzip`` options (or have gzip applied by a proxy). 15 | 16 | Backwards-compatibility notes 17 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | 19 | * If Tornado 3.2.2 is run at the same time as older versions on the same 20 | domain, there is some potential for issues with the differing cookie 21 | versions. The `.Application` setting ``xsrf_cookie_version=1`` can 22 | be used for a transitional period to generate the older cookie format 23 | on newer servers. 24 | 25 | Other changes 26 | ~~~~~~~~~~~~~ 27 | 28 | * ``tornado.platform.asyncio`` is now compatible with ``trollius`` version 0.3. 29 | -------------------------------------------------------------------------------- /docs/releases/v3.0.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 3.0.1 2 | =========================== 3 | 4 | Apr 8, 2013 5 | ----------- 6 | 7 | * The interface of `tornado.auth.FacebookGraphMixin` is now consistent 8 | with its documentation and the rest of the module. The 9 | ``get_authenticated_user`` and ``facebook_request`` methods return a 10 | ``Future`` and the ``callback`` argument is optional. 11 | * The `tornado.testing.gen_test` decorator will no longer be recognized 12 | as a (broken) test by ``nose``. 13 | * Work around a bug in Ubuntu 13.04 betas involving an incomplete backport 14 | of the `ssl.match_hostname` function. 15 | * `tornado.websocket.websocket_connect` now fails cleanly when it attempts 16 | to connect to a non-websocket url. 17 | * `tornado.testing.LogTrapTestCase` once again works with byte strings 18 | on Python 2. 19 | * The ``request`` attribute of `tornado.httpclient.HTTPResponse` is 20 | now always an `~tornado.httpclient.HTTPRequest`, never a ``_RequestProxy``. 21 | * Exceptions raised by the `tornado.gen` module now have better messages 22 | when tuples are used as callback keys. 23 | -------------------------------------------------------------------------------- /docs/locks.rst: -------------------------------------------------------------------------------- 1 | ``tornado.locks`` -- Synchronization primitives 2 | =============================================== 3 | 4 | .. versionadded:: 4.2 5 | 6 | Coordinate coroutines with synchronization primitives analogous to those the 7 | standard library provides to threads. 8 | 9 | .. warning:: 10 | 11 | Note that these primitives are not actually thread-safe and cannot be used in 12 | place of those from the standard library--they are meant to coordinate Tornado 13 | coroutines in a single-threaded app, not to protect shared objects in a 14 | multithreaded app. 15 | 16 | .. automodule:: tornado.locks 17 | 18 | Condition 19 | --------- 20 | .. autoclass:: Condition 21 | :members: 22 | 23 | Event 24 | ----- 25 | .. autoclass:: Event 26 | :members: 27 | 28 | Semaphore 29 | --------- 30 | .. autoclass:: Semaphore 31 | :members: 32 | 33 | BoundedSemaphore 34 | ---------------- 35 | .. autoclass:: BoundedSemaphore 36 | :members: 37 | :inherited-members: 38 | 39 | Lock 40 | ---- 41 | .. autoclass:: Lock 42 | :members: 43 | :inherited-members: 44 | -------------------------------------------------------------------------------- /demos/blog/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ escape(handler.settings["blog_title"]) }} 6 | 7 | 8 | {% block head %}{% end %} 9 | 10 | 11 |
12 | 23 |
{% block body %}{% end %}
24 |
25 | {% block bottom %}{% end %} 26 | 27 | 28 | -------------------------------------------------------------------------------- /demos/appengine/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ handler.settings["blog_title"] }} 6 | 7 | 8 | {% block head %}{% end %} 9 | 10 | 11 |
12 | 25 |
{% block body %}{% end %}
26 |
27 | {% block bottom %}{% end %} 28 | 29 | 30 | -------------------------------------------------------------------------------- /demos/tcpecho/server.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from tornado.ioloop import IOLoop 3 | from tornado import gen 4 | from tornado.iostream import StreamClosedError 5 | from tornado.tcpserver import TCPServer 6 | from tornado.options import options, define 7 | 8 | define("port", default=9888, help="TCP port to listen on") 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | class EchoServer(TCPServer): 13 | @gen.coroutine 14 | def handle_stream(self, stream, address): 15 | while True: 16 | try: 17 | data = yield stream.read_until(b"\n") 18 | logger.info("Received bytes: %s", data) 19 | if not data.endswith(b"\n"): 20 | data = data + b"\n" 21 | yield stream.write(data) 22 | except StreamClosedError: 23 | logger.warning("Lost client at host %s", address[0]) 24 | break 25 | except Exception as e: 26 | print(e) 27 | 28 | 29 | if __name__ == "__main__": 30 | options.parse_command_line() 31 | server = EchoServer() 32 | server.listen(options.port) 33 | logger.info("Listening on TCP port %d", options.port) 34 | IOLoop.current().start() 35 | -------------------------------------------------------------------------------- /tornado/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2009 Facebook 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """The Tornado web server and tools.""" 18 | 19 | from __future__ import absolute_import, division, print_function 20 | 21 | # version is a human-readable version number. 22 | 23 | # version_info is a four-tuple for programmatic comparison. The first 24 | # three numbers are the components of the version number. The fourth 25 | # is zero for an official release, positive for a development branch, 26 | # or negative for a release candidate or beta (after the base version 27 | # number has been incremented) 28 | version = "4.5.1" 29 | version_info = (4, 5, 1, 0) 30 | -------------------------------------------------------------------------------- /docs/releases/v2.1.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 2.1.1 2 | =========================== 3 | 4 | Oct 4, 2011 5 | ----------- 6 | 7 | Bug fixes 8 | ~~~~~~~~~ 9 | 10 | * Fixed handling of closed connections with the ``epoll`` (i.e. Linux) 11 | ``IOLoop``. Previously, closed connections could be shut down too early, 12 | which most often manifested as "Stream is closed" exceptions in 13 | ``SimpleAsyncHTTPClient``. 14 | * Fixed a case in which chunked responses could be closed prematurely, 15 | leading to truncated output. 16 | * ``IOStream.connect`` now reports errors more consistently via logging 17 | and the close callback (this affects e.g. connections to localhost 18 | on FreeBSD). 19 | * ``IOStream.read_bytes`` again accepts both ``int`` and ``long`` arguments. 20 | * ``PeriodicCallback`` no longer runs repeatedly when ``IOLoop`` iterations 21 | complete faster than the resolution of ``time.time()`` (mainly a problem 22 | on Windows). 23 | 24 | Backwards-compatibility note 25 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 26 | 27 | * Listening for ``IOLoop.ERROR`` alone is no longer sufficient for detecting 28 | closed connections on an otherwise unused socket. ``IOLoop.ERROR`` must 29 | always be used in combination with ``READ`` or ``WRITE``. 30 | -------------------------------------------------------------------------------- /demos/websocket/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tornado Chat Demo 6 | 7 | 8 | 9 |
10 |
11 | {% for message in messages %} 12 | {% include "message.html" %} 13 | {% end %} 14 |
15 |
16 |
17 | 18 | 19 | 20 | 25 | 26 |
21 | 22 | 23 | {% module xsrf_form_html() %} 24 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /demos/chat/static/chat.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009 FriendFeed 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | body { 18 | background: white; 19 | margin: 10px; 20 | } 21 | 22 | body, 23 | input { 24 | font-family: sans-serif; 25 | font-size: 10pt; 26 | color: black; 27 | } 28 | 29 | table { 30 | border-collapse: collapse; 31 | border: 0; 32 | } 33 | 34 | td { 35 | border: 0; 36 | padding: 0; 37 | } 38 | 39 | #body { 40 | position: absolute; 41 | bottom: 10px; 42 | left: 10px; 43 | } 44 | 45 | #input { 46 | margin-top: 0.5em; 47 | } 48 | 49 | #inbox .message { 50 | padding-top: 0.25em; 51 | } 52 | 53 | #nav { 54 | float: right; 55 | z-index: 99; 56 | } 57 | -------------------------------------------------------------------------------- /demos/websocket/static/chat.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009 FriendFeed 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | body { 18 | background: white; 19 | margin: 10px; 20 | } 21 | 22 | body, 23 | input { 24 | font-family: sans-serif; 25 | font-size: 10pt; 26 | color: black; 27 | } 28 | 29 | table { 30 | border-collapse: collapse; 31 | border: 0; 32 | } 33 | 34 | td { 35 | border: 0; 36 | padding: 0; 37 | } 38 | 39 | #body { 40 | position: absolute; 41 | bottom: 10px; 42 | left: 10px; 43 | } 44 | 45 | #input { 46 | margin-top: 0.5em; 47 | } 48 | 49 | #inbox .message { 50 | padding-top: 0.25em; 51 | } 52 | 53 | #nav { 54 | float: right; 55 | z-index: 99; 56 | } 57 | -------------------------------------------------------------------------------- /docs/escape.rst: -------------------------------------------------------------------------------- 1 | ``tornado.escape`` --- Escaping and string manipulation 2 | ======================================================= 3 | 4 | .. automodule:: tornado.escape 5 | 6 | Escaping functions 7 | ------------------ 8 | 9 | .. autofunction:: xhtml_escape 10 | .. autofunction:: xhtml_unescape 11 | 12 | .. autofunction:: url_escape 13 | .. autofunction:: url_unescape 14 | 15 | .. autofunction:: json_encode 16 | .. autofunction:: json_decode 17 | 18 | Byte/unicode conversions 19 | ------------------------ 20 | These functions are used extensively within Tornado itself, 21 | but should not be directly needed by most applications. Note that 22 | much of the complexity of these functions comes from the fact that 23 | Tornado supports both Python 2 and Python 3. 24 | 25 | .. autofunction:: utf8 26 | .. autofunction:: to_unicode 27 | .. function:: native_str 28 | 29 | Converts a byte or unicode string into type `str`. Equivalent to 30 | `utf8` on Python 2 and `to_unicode` on Python 3. 31 | 32 | .. autofunction:: to_basestring 33 | 34 | .. autofunction:: recursive_unicode 35 | 36 | Miscellaneous functions 37 | ----------------------- 38 | .. autofunction:: linkify 39 | .. autofunction:: squeeze 40 | -------------------------------------------------------------------------------- /demos/chat/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tornado Chat Demo 6 | 7 | 8 | 9 |
10 |
11 | {% for message in messages %} 12 | {% module Template("message.html", message=message) %} 13 | {% end %} 14 |
15 |
16 |
17 | 18 | 19 | 20 | 25 | 26 |
21 | 22 | 23 | {% module xsrf_form_html() %} 24 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /tornado/test/gettext_translations/fr_FR/LC_MESSAGES/tornado_test.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-01-27 11:05+0300\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 20 | 21 | #: extract_me.py:11 22 | msgid "school" 23 | msgstr "école" 24 | 25 | #: extract_me.py:12 26 | msgctxt "law" 27 | msgid "right" 28 | msgstr "le droit" 29 | 30 | #: extract_me.py:13 31 | msgctxt "good" 32 | msgid "right" 33 | msgstr "le bien" 34 | 35 | #: extract_me.py:14 36 | msgctxt "organization" 37 | msgid "club" 38 | msgid_plural "clubs" 39 | msgstr[0] "le club" 40 | msgstr[1] "les clubs" 41 | 42 | #: extract_me.py:15 43 | msgctxt "stick" 44 | msgid "club" 45 | msgid_plural "clubs" 46 | msgstr[0] "le bâton" 47 | msgstr[1] "les bâtons" 48 | -------------------------------------------------------------------------------- /tornado/speedups.c: -------------------------------------------------------------------------------- 1 | #define PY_SSIZE_T_CLEAN 2 | #include 3 | 4 | static PyObject* websocket_mask(PyObject* self, PyObject* args) { 5 | const char* mask; 6 | Py_ssize_t mask_len; 7 | const char* data; 8 | Py_ssize_t data_len; 9 | Py_ssize_t i; 10 | PyObject* result; 11 | char* buf; 12 | 13 | if (!PyArg_ParseTuple(args, "s#s#", &mask, &mask_len, &data, &data_len)) { 14 | return NULL; 15 | } 16 | 17 | result = PyBytes_FromStringAndSize(NULL, data_len); 18 | if (!result) { 19 | return NULL; 20 | } 21 | buf = PyBytes_AsString(result); 22 | for (i = 0; i < data_len; i++) { 23 | buf[i] = data[i] ^ mask[i % 4]; 24 | } 25 | 26 | return result; 27 | } 28 | 29 | static PyMethodDef methods[] = { 30 | {"websocket_mask", websocket_mask, METH_VARARGS, ""}, 31 | {NULL, NULL, 0, NULL} 32 | }; 33 | 34 | #if PY_MAJOR_VERSION >= 3 35 | static struct PyModuleDef speedupsmodule = { 36 | PyModuleDef_HEAD_INIT, 37 | "speedups", 38 | NULL, 39 | -1, 40 | methods 41 | }; 42 | 43 | PyMODINIT_FUNC 44 | PyInit_speedups(void) { 45 | return PyModule_Create(&speedupsmodule); 46 | } 47 | #else // Python 2.x 48 | PyMODINIT_FUNC 49 | initspeedups(void) { 50 | Py_InitModule("tornado.speedups", methods); 51 | } 52 | #endif 53 | -------------------------------------------------------------------------------- /docs/websocket.rst: -------------------------------------------------------------------------------- 1 | ``tornado.websocket`` --- Bidirectional communication to the browser 2 | ==================================================================== 3 | 4 | .. testsetup:: 5 | 6 | import tornado.websocket 7 | 8 | .. automodule:: tornado.websocket 9 | 10 | .. autoclass:: WebSocketHandler 11 | 12 | Event handlers 13 | -------------- 14 | 15 | .. automethod:: WebSocketHandler.open 16 | .. automethod:: WebSocketHandler.on_message 17 | .. automethod:: WebSocketHandler.on_close 18 | .. automethod:: WebSocketHandler.select_subprotocol 19 | .. automethod:: WebSocketHandler.on_ping 20 | 21 | Output 22 | ------ 23 | 24 | .. automethod:: WebSocketHandler.write_message 25 | .. automethod:: WebSocketHandler.close 26 | 27 | Configuration 28 | ------------- 29 | 30 | .. automethod:: WebSocketHandler.check_origin 31 | .. automethod:: WebSocketHandler.get_compression_options 32 | .. automethod:: WebSocketHandler.set_nodelay 33 | 34 | Other 35 | ----- 36 | 37 | .. automethod:: WebSocketHandler.ping 38 | .. automethod:: WebSocketHandler.on_pong 39 | .. autoexception:: WebSocketClosedError 40 | 41 | 42 | Client-side support 43 | ------------------- 44 | 45 | .. autofunction:: websocket_connect 46 | .. autoclass:: WebSocketClientConnection 47 | :members: 48 | -------------------------------------------------------------------------------- /maint/test/cython/cythonapp_test.py: -------------------------------------------------------------------------------- 1 | from tornado.testing import AsyncTestCase, gen_test 2 | from tornado.util import ArgReplacer 3 | import unittest 4 | 5 | import cythonapp 6 | 7 | 8 | class CythonCoroutineTest(AsyncTestCase): 9 | @gen_test 10 | def test_native_coroutine(self): 11 | x = yield cythonapp.native_coroutine() 12 | self.assertEqual(x, "goodbye") 13 | 14 | @gen_test 15 | def test_decorated_coroutine(self): 16 | x = yield cythonapp.decorated_coroutine() 17 | self.assertEqual(x, "goodbye") 18 | 19 | 20 | class CythonArgReplacerTest(unittest.TestCase): 21 | def test_arg_replacer_function(self): 22 | replacer = ArgReplacer(cythonapp.function_with_args, 'two') 23 | args = (1, 'old', 3) 24 | kwargs = {} 25 | self.assertEqual(replacer.get_old_value(args, kwargs), 'old') 26 | self.assertEqual(replacer.replace('new', args, kwargs), 27 | ('old', [1, 'new', 3], {})) 28 | 29 | def test_arg_replacer_method(self): 30 | replacer = ArgReplacer(cythonapp.AClass().method_with_args, 'two') 31 | args = (1, 'old', 3) 32 | kwargs = {} 33 | self.assertEqual(replacer.get_old_value(args, kwargs), 'old') 34 | self.assertEqual(replacer.replace('new', args, kwargs), 35 | ('old', [1, 'new', 3], {})) 36 | -------------------------------------------------------------------------------- /docs/guide/queues.rst: -------------------------------------------------------------------------------- 1 | :class:`~tornado.queues.Queue` example - a concurrent web spider 2 | ================================================================ 3 | 4 | .. currentmodule:: tornado.queues 5 | 6 | Tornado's `tornado.queues` module implements an asynchronous producer / 7 | consumer pattern for coroutines, analogous to the pattern implemented for 8 | threads by the Python standard library's `queue` module. 9 | 10 | A coroutine that yields `Queue.get` pauses until there is an item in the queue. 11 | If the queue has a maximum size set, a coroutine that yields `Queue.put` pauses 12 | until there is room for another item. 13 | 14 | A `~Queue` maintains a count of unfinished tasks, which begins at zero. 15 | `~Queue.put` increments the count; `~Queue.task_done` decrements it. 16 | 17 | In the web-spider example here, the queue begins containing only base_url. When 18 | a worker fetches a page it parses the links and puts new ones in the queue, 19 | then calls `~Queue.task_done` to decrement the counter once. Eventually, a 20 | worker fetches a page whose URLs have all been seen before, and there is also 21 | no work left in the queue. Thus that worker's call to `~Queue.task_done` 22 | decrements the counter to zero. The main coroutine, which is waiting for 23 | `~Queue.join`, is unpaused and finishes. 24 | 25 | .. literalinclude:: ../../demos/webspider/webspider.py 26 | -------------------------------------------------------------------------------- /docs/auth.rst: -------------------------------------------------------------------------------- 1 | ``tornado.auth`` --- Third-party login with OpenID and OAuth 2 | ============================================================ 3 | 4 | .. testsetup:: 5 | 6 | import tornado.auth, tornado.gen, tornado.web 7 | 8 | .. automodule:: tornado.auth 9 | 10 | Common protocols 11 | ---------------- 12 | 13 | These classes implement the OpenID and OAuth standards. They will 14 | generally need to be subclassed to use them with any particular site. 15 | The degree of customization required will vary, but in most cases 16 | overridding the class attributes (which are named beginning with 17 | underscores for historical reasons) should be sufficient. 18 | 19 | .. autoclass:: OpenIdMixin 20 | :members: 21 | 22 | .. autoclass:: OAuthMixin 23 | 24 | .. automethod:: authorize_redirect 25 | .. automethod:: get_authenticated_user 26 | .. automethod:: _oauth_consumer_token 27 | .. automethod:: _oauth_get_user_future 28 | .. automethod:: get_auth_http_client 29 | 30 | .. autoclass:: OAuth2Mixin 31 | :members: 32 | 33 | Google 34 | ------ 35 | 36 | .. autoclass:: GoogleOAuth2Mixin 37 | :members: 38 | 39 | Facebook 40 | -------- 41 | 42 | .. autoclass:: FacebookGraphMixin 43 | :members: 44 | 45 | Twitter 46 | ------- 47 | 48 | .. autoclass:: TwitterMixin 49 | :members: 50 | -------------------------------------------------------------------------------- /demos/benchmark/gen_benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # A simple benchmark of the tornado.gen module. 4 | # Runs in two modes, testing new-style (@coroutine and Futures) 5 | # and old-style (@engine and Tasks) coroutines. 6 | 7 | from timeit import Timer 8 | 9 | from tornado import gen 10 | from tornado.options import options, define, parse_command_line 11 | 12 | define('num', default=10000, help='number of iterations') 13 | 14 | # These benchmarks are delicate. They hit various fast-paths in the gen 15 | # machinery in order to stay synchronous so we don't need an IOLoop. 16 | # This removes noise from the results, but it's easy to change things 17 | # in a way that completely invalidates the results. 18 | 19 | @gen.engine 20 | def e2(callback): 21 | callback() 22 | 23 | @gen.engine 24 | def e1(): 25 | for i in range(10): 26 | yield gen.Task(e2) 27 | 28 | @gen.coroutine 29 | def c2(): 30 | pass 31 | 32 | @gen.coroutine 33 | def c1(): 34 | for i in range(10): 35 | yield c2() 36 | 37 | def main(): 38 | parse_command_line() 39 | t = Timer(e1) 40 | results = t.timeit(options.num) / options.num 41 | print('engine: %0.3f ms per iteration' % (results * 1000)) 42 | t = Timer(c1) 43 | results = t.timeit(options.num) / options.num 44 | print('coroutine: %0.3f ms per iteration' % (results * 1000)) 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /demos/helloworld/helloworld.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2009 Facebook 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | import tornado.options 20 | import tornado.web 21 | 22 | from tornado.options import define, options 23 | 24 | define("port", default=8888, help="run on the given port", type=int) 25 | 26 | 27 | class MainHandler(tornado.web.RequestHandler): 28 | def get(self): 29 | self.write("Hello, world") 30 | 31 | 32 | def main(): 33 | tornado.options.parse_command_line() 34 | application = tornado.web.Application([ 35 | (r"/", MainHandler), 36 | ]) 37 | http_server = tornado.httpserver.HTTPServer(application) 38 | http_server.listen(options.port) 39 | tornado.ioloop.IOLoop.current().start() 40 | 41 | 42 | if __name__ == "__main__": 43 | main() 44 | -------------------------------------------------------------------------------- /demos/blog/templates/feed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% set date_format = "%Y-%m-%dT%H:%M:%SZ" %} 4 | {{ handler.settings["blog_title"] }} 5 | {% if len(entries) > 0 %} 6 | {{ max(e.updated for e in entries).strftime(date_format) }} 7 | {% else %} 8 | {{ datetime.datetime.utcnow().strftime(date_format) }} 9 | {% end %} 10 | http://{{ request.host }}/ 11 | 12 | 13 | {{ handler.settings["blog_title"] }} 14 | {% for entry in entries %} 15 | 16 | http://{{ request.host }}/entry/{{ entry.slug }} 17 | {{ entry.title }} 18 | 19 | {{ entry.updated.strftime(date_format) }} 20 | {{ entry.published.strftime(date_format) }} 21 | 22 |
{% raw entry.html %}
23 |
24 |
25 | {% end %} 26 |
27 | -------------------------------------------------------------------------------- /demos/appengine/templates/feed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% set date_format = "%Y-%m-%dT%H:%M:%SZ" %} 4 | {{ handler.settings["blog_title"] }} 5 | {% if len(entries) > 0 %} 6 | {{ max(e.updated for e in entries).strftime(date_format) }} 7 | {% else %} 8 | {{ datetime.datetime.utcnow().strftime(date_format) }} 9 | {% end %} 10 | http://{{ request.host }}/ 11 | 12 | 13 | {{ handler.settings["blog_title"] }} 14 | {% for entry in entries %} 15 | 16 | http://{{ request.host }}/entry/{{ entry.slug }} 17 | {{ entry.title }} 18 | 19 | {{ entry.updated.strftime(date_format) }} 20 | {{ entry.published.strftime(date_format) }} 21 | 22 |
{% raw entry.html %}
23 |
24 |
25 | {% end %} 26 |
27 | -------------------------------------------------------------------------------- /docs/iostream.rst: -------------------------------------------------------------------------------- 1 | ``tornado.iostream`` --- Convenient wrappers for non-blocking sockets 2 | ===================================================================== 3 | 4 | .. automodule:: tornado.iostream 5 | 6 | Base class 7 | ---------- 8 | 9 | .. autoclass:: BaseIOStream 10 | 11 | Main interface 12 | ^^^^^^^^^^^^^^ 13 | 14 | .. automethod:: BaseIOStream.write 15 | .. automethod:: BaseIOStream.read_bytes 16 | .. automethod:: BaseIOStream.read_until 17 | .. automethod:: BaseIOStream.read_until_regex 18 | .. automethod:: BaseIOStream.read_until_close 19 | .. automethod:: BaseIOStream.close 20 | .. automethod:: BaseIOStream.set_close_callback 21 | .. automethod:: BaseIOStream.closed 22 | .. automethod:: BaseIOStream.reading 23 | .. automethod:: BaseIOStream.writing 24 | .. automethod:: BaseIOStream.set_nodelay 25 | 26 | Methods for subclasses 27 | ^^^^^^^^^^^^^^^^^^^^^^ 28 | 29 | .. automethod:: BaseIOStream.fileno 30 | .. automethod:: BaseIOStream.close_fd 31 | .. automethod:: BaseIOStream.write_to_fd 32 | .. automethod:: BaseIOStream.read_from_fd 33 | .. automethod:: BaseIOStream.get_fd_error 34 | 35 | Implementations 36 | --------------- 37 | 38 | .. autoclass:: IOStream 39 | :members: 40 | 41 | .. autoclass:: SSLIOStream 42 | :members: 43 | 44 | .. autoclass:: PipeIOStream 45 | :members: 46 | 47 | Exceptions 48 | ---------- 49 | 50 | .. autoexception:: StreamBufferFullError 51 | .. autoexception:: StreamClosedError 52 | .. autoexception:: UnsatisfiableReadError 53 | -------------------------------------------------------------------------------- /maint/test/websocket/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import logging 4 | 5 | from tornado import gen 6 | from tornado.ioloop import IOLoop 7 | from tornado.options import define, options, parse_command_line 8 | from tornado.websocket import websocket_connect 9 | 10 | define('url', default='ws://localhost:9001') 11 | define('name', default='Tornado') 12 | 13 | @gen.engine 14 | def run_tests(): 15 | url = options.url + '/getCaseCount' 16 | control_ws = yield websocket_connect(url, None) 17 | num_tests = int((yield control_ws.read_message())) 18 | logging.info('running %d cases', num_tests) 19 | msg = yield control_ws.read_message() 20 | assert msg is None 21 | 22 | for i in range(1, num_tests + 1): 23 | logging.info('running test case %d', i) 24 | url = options.url + '/runCase?case=%d&agent=%s' % (i, options.name) 25 | test_ws = yield websocket_connect(url, None, compression_options={}) 26 | while True: 27 | message = yield test_ws.read_message() 28 | if message is None: 29 | break 30 | test_ws.write_message(message, binary=isinstance(message, bytes)) 31 | 32 | url = options.url + '/updateReports?agent=%s' % options.name 33 | update_ws = yield websocket_connect(url, None) 34 | msg = yield update_ws.read_message() 35 | assert msg is None 36 | IOLoop.instance().stop() 37 | 38 | def main(): 39 | parse_command_line() 40 | 41 | IOLoop.instance().add_callback(run_tests) 42 | 43 | IOLoop.instance().start() 44 | 45 | if __name__ == '__main__': 46 | main() 47 | -------------------------------------------------------------------------------- /demos/appengine/templates/compose.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body %} 4 |
5 |
6 |
7 |
8 | 9 |  {{ _("Cancel") }} 10 |
11 | {% if entry %} 12 | 13 | {% end %} 14 | {% module xsrf_form_html() %} 15 |
16 | {% end %} 17 | 18 | {% block bottom %} 19 | 20 | 40 | {% end %} 41 | -------------------------------------------------------------------------------- /tornado/test/import_test.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from __future__ import absolute_import, division, print_function 3 | from tornado.test.util import unittest 4 | 5 | 6 | class ImportTest(unittest.TestCase): 7 | def test_import_everything(self): 8 | # Some of our modules are not otherwise tested. Import them 9 | # all (unless they have external dependencies) here to at 10 | # least ensure that there are no syntax errors. 11 | import tornado.auth 12 | import tornado.autoreload 13 | import tornado.concurrent 14 | import tornado.escape 15 | import tornado.gen 16 | import tornado.http1connection 17 | import tornado.httpclient 18 | import tornado.httpserver 19 | import tornado.httputil 20 | import tornado.ioloop 21 | import tornado.iostream 22 | import tornado.locale 23 | import tornado.log 24 | import tornado.netutil 25 | import tornado.options 26 | import tornado.process 27 | import tornado.simple_httpclient 28 | import tornado.stack_context 29 | import tornado.tcpserver 30 | import tornado.tcpclient 31 | import tornado.template 32 | import tornado.testing 33 | import tornado.util 34 | import tornado.web 35 | import tornado.websocket 36 | import tornado.wsgi 37 | 38 | # for modules with dependencies, if those dependencies can be loaded, 39 | # load them too. 40 | 41 | def test_import_pycurl(self): 42 | try: 43 | import pycurl # type: ignore 44 | except ImportError: 45 | pass 46 | else: 47 | import tornado.curl_httpclient 48 | -------------------------------------------------------------------------------- /demos/blog/schema.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2009 FriendFeed 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | -- not use this file except in compliance with the License. You may obtain 5 | -- a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | -- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | -- License for the specific language governing permissions and limitations 13 | -- under the License. 14 | 15 | -- To create the database: 16 | -- CREATE DATABASE blog; 17 | -- GRANT ALL PRIVILEGES ON blog.* TO 'blog'@'localhost' IDENTIFIED BY 'blog'; 18 | -- 19 | -- To reload the tables: 20 | -- mysql --user=blog --password=blog --database=blog < schema.sql 21 | 22 | SET SESSION storage_engine = "InnoDB"; 23 | SET SESSION time_zone = "+0:00"; 24 | ALTER DATABASE CHARACTER SET "utf8"; 25 | 26 | DROP TABLE IF EXISTS entries; 27 | CREATE TABLE entries ( 28 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 29 | author_id INT NOT NULL REFERENCES authors(id), 30 | slug VARCHAR(100) NOT NULL UNIQUE, 31 | title VARCHAR(512) NOT NULL, 32 | markdown MEDIUMTEXT NOT NULL, 33 | html MEDIUMTEXT NOT NULL, 34 | published DATETIME NOT NULL, 35 | updated TIMESTAMP NOT NULL, 36 | KEY (published) 37 | ); 38 | 39 | DROP TABLE IF EXISTS authors; 40 | CREATE TABLE authors ( 41 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 42 | email VARCHAR(100) NOT NULL UNIQUE, 43 | name VARCHAR(100) NOT NULL, 44 | hashed_password VARCHAR(100) NOT NULL 45 | ); 46 | -------------------------------------------------------------------------------- /demos/blog/templates/compose.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block body %} 4 |
5 |
6 |
7 | 12 | {% if entry %} 13 | 14 | {% end %} 15 | {% module xsrf_form_html() %} 16 |
17 | {% end %} 18 | 19 | {% block bottom %} 20 | 21 | 41 | {% end %} 42 | -------------------------------------------------------------------------------- /docs/guide/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ------------ 3 | 4 | `Tornado `_ is a Python web framework and 5 | asynchronous networking library, originally developed at `FriendFeed 6 | `_. By using non-blocking network I/O, Tornado 7 | can scale to tens of thousands of open connections, making it ideal for 8 | `long polling `_, 9 | `WebSockets `_, and other 10 | applications that require a long-lived connection to each user. 11 | 12 | Tornado can be roughly divided into four major components: 13 | 14 | * A web framework (including `.RequestHandler` which is subclassed to 15 | create web applications, and various supporting classes). 16 | * Client- and server-side implementions of HTTP (`.HTTPServer` and 17 | `.AsyncHTTPClient`). 18 | * An asynchronous networking library including the classes `.IOLoop` 19 | and `.IOStream`, which serve as the building blocks for the HTTP 20 | components and can also be used to implement other protocols. 21 | * A coroutine library (`tornado.gen`) which allows asynchronous 22 | code to be written in a more straightforward way than chaining 23 | callbacks. 24 | 25 | The Tornado web framework and HTTP server together offer a full-stack 26 | alternative to `WSGI `_. 27 | While it is possible to use the Tornado web framework in a WSGI 28 | container (`.WSGIAdapter`), or use the Tornado HTTP server as a 29 | container for other WSGI frameworks (`.WSGIContainer`), each of these 30 | combinations has limitations and to take full advantage of Tornado you 31 | will need to use the Tornado's web framework and HTTP server together. 32 | -------------------------------------------------------------------------------- /maint/scripts/test_resolvers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | import pprint 5 | import socket 6 | 7 | from tornado import gen 8 | from tornado.ioloop import IOLoop 9 | from tornado.netutil import Resolver, ThreadedResolver 10 | from tornado.options import parse_command_line, define, options 11 | 12 | try: 13 | import twisted 14 | except ImportError: 15 | twisted = None 16 | 17 | try: 18 | import pycares 19 | except ImportError: 20 | pycares = None 21 | 22 | define('family', default='unspec', 23 | help='Address family to query: unspec, inet, or inet6') 24 | 25 | @gen.coroutine 26 | def main(): 27 | args = parse_command_line() 28 | 29 | if not args: 30 | args = ['localhost', 'www.google.com', 31 | 'www.facebook.com', 'www.dropbox.com'] 32 | 33 | resolvers = [Resolver(), ThreadedResolver()] 34 | 35 | if twisted is not None: 36 | from tornado.platform.twisted import TwistedResolver 37 | resolvers.append(TwistedResolver()) 38 | 39 | if pycares is not None: 40 | from tornado.platform.caresresolver import CaresResolver 41 | resolvers.append(CaresResolver()) 42 | 43 | family = { 44 | 'unspec': socket.AF_UNSPEC, 45 | 'inet': socket.AF_INET, 46 | 'inet6': socket.AF_INET6, 47 | }[options.family] 48 | 49 | for host in args: 50 | print('Resolving %s' % host) 51 | for resolver in resolvers: 52 | addrinfo = yield resolver.resolve(host, 80, family) 53 | print('%s: %s' % (resolver.__class__.__name__, 54 | pprint.pformat(addrinfo))) 55 | print() 56 | 57 | if __name__ == '__main__': 58 | IOLoop.instance().run_sync(main) 59 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Tornado Web Server 2 | ================== 3 | 4 | .. image:: https://badges.gitter.im/Join%20Chat.svg 5 | :alt: Join the chat at https://gitter.im/tornadoweb/tornado 6 | :target: https://gitter.im/tornadoweb/tornado?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge 7 | 8 | `Tornado `_ is a Python web framework and 9 | asynchronous networking library, originally developed at `FriendFeed 10 | `_. By using non-blocking network I/O, Tornado 11 | can scale to tens of thousands of open connections, making it ideal for 12 | `long polling `_, 13 | `WebSockets `_, and other 14 | applications that require a long-lived connection to each user. 15 | 16 | Hello, world 17 | ------------ 18 | 19 | Here is a simple "Hello, world" example web app for Tornado: 20 | 21 | .. code-block:: python 22 | 23 | import tornado.ioloop 24 | import tornado.web 25 | 26 | class MainHandler(tornado.web.RequestHandler): 27 | def get(self): 28 | self.write("Hello, world") 29 | 30 | def make_app(): 31 | return tornado.web.Application([ 32 | (r"/", MainHandler), 33 | ]) 34 | 35 | if __name__ == "__main__": 36 | app = make_app() 37 | app.listen(8888) 38 | tornado.ioloop.IOLoop.current().start() 39 | 40 | This example does not use any of Tornado's asynchronous features; for 41 | that see this `simple chat room 42 | `_. 43 | 44 | Documentation 45 | ------------- 46 | 47 | Documentation and links to additional resources are available at 48 | http://www.tornadoweb.org 49 | -------------------------------------------------------------------------------- /docs/httpclient.rst: -------------------------------------------------------------------------------- 1 | ``tornado.httpclient`` --- Asynchronous HTTP client 2 | =================================================== 3 | 4 | .. automodule:: tornado.httpclient 5 | 6 | HTTP client interfaces 7 | ---------------------- 8 | 9 | .. autoclass:: HTTPClient 10 | :members: 11 | 12 | .. autoclass:: AsyncHTTPClient 13 | :members: 14 | 15 | Request objects 16 | --------------- 17 | .. autoclass:: HTTPRequest 18 | :members: 19 | 20 | Response objects 21 | ---------------- 22 | .. autoclass:: HTTPResponse 23 | :members: 24 | 25 | Exceptions 26 | ---------- 27 | .. autoexception:: HTTPError 28 | :members: 29 | 30 | Command-line interface 31 | ---------------------- 32 | 33 | This module provides a simple command-line interface to fetch a url 34 | using Tornado's HTTP client. Example usage:: 35 | 36 | # Fetch the url and print its body 37 | python -m tornado.httpclient http://www.google.com 38 | 39 | # Just print the headers 40 | python -m tornado.httpclient --print_headers --print_body=false http://www.google.com 41 | 42 | Implementations 43 | ~~~~~~~~~~~~~~~ 44 | 45 | .. automodule:: tornado.simple_httpclient 46 | :members: 47 | 48 | .. module:: tornado.curl_httpclient 49 | 50 | .. class:: CurlAsyncHTTPClient(io_loop, max_clients=10, defaults=None) 51 | 52 | ``libcurl``-based HTTP client. 53 | 54 | Example Code 55 | ~~~~~~~~~~~~ 56 | 57 | * `A simple webspider `_ 58 | shows how to fetch URLs concurrently. 59 | * `The file uploader demo `_ 60 | uses either HTTP POST or HTTP PUT to upload files to a server. 61 | -------------------------------------------------------------------------------- /demos/benchmark/chunk_benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Downloads a large file in chunked encoding with both curl and simple clients 4 | 5 | import logging 6 | from tornado.curl_httpclient import CurlAsyncHTTPClient 7 | from tornado.simple_httpclient import SimpleAsyncHTTPClient 8 | from tornado.ioloop import IOLoop 9 | from tornado.options import define, options, parse_command_line 10 | from tornado.web import RequestHandler, Application 11 | 12 | define('port', default=8888) 13 | define('num_chunks', default=1000) 14 | define('chunk_size', default=2048) 15 | 16 | 17 | class ChunkHandler(RequestHandler): 18 | def get(self): 19 | for i in xrange(options.num_chunks): 20 | self.write('A' * options.chunk_size) 21 | self.flush() 22 | self.finish() 23 | 24 | 25 | def main(): 26 | parse_command_line() 27 | app = Application([('/', ChunkHandler)]) 28 | app.listen(options.port, address='127.0.0.1') 29 | 30 | def callback(response): 31 | response.rethrow() 32 | assert len(response.body) == (options.num_chunks * options.chunk_size) 33 | logging.warning("fetch completed in %s seconds", response.request_time) 34 | IOLoop.current().stop() 35 | 36 | logging.warning("Starting fetch with curl client") 37 | curl_client = CurlAsyncHTTPClient() 38 | curl_client.fetch('http://localhost:%d/' % options.port, 39 | callback=callback) 40 | IOLoop.current().start() 41 | 42 | logging.warning("Starting fetch with simple client") 43 | simple_client = SimpleAsyncHTTPClient() 44 | simple_client.fetch('http://localhost:%d/' % options.port, 45 | callback=callback) 46 | IOLoop.current().start() 47 | 48 | 49 | if __name__ == '__main__': 50 | main() 51 | -------------------------------------------------------------------------------- /demos/appengine/README: -------------------------------------------------------------------------------- 1 | Running the Tornado AppEngine example 2 | ===================================== 3 | This example is designed to run in Google AppEngine, so there are a couple 4 | of steps to get it running. You can download the Google AppEngine Python 5 | development environment at http://code.google.com/appengine/downloads.html. 6 | 7 | 1. Link or copy the tornado code directory into this directory: 8 | 9 | ln -s ../../tornado tornado 10 | 11 | AppEngine doesn't use the Python modules installed on this machine. 12 | You need to have the 'tornado' module copied or linked for AppEngine 13 | to find it. 14 | 15 | 3. Install and run dev_appserver 16 | 17 | If you don't already have the App Engine SDK, download it from 18 | http://code.google.com/appengine/downloads.html 19 | 20 | To start the tornado demo, run the dev server on this directory: 21 | 22 | dev_appserver.py . 23 | 24 | 4. Visit http://localhost:8080/ in your browser 25 | 26 | If you sign in as an administrator, you will be able to create and 27 | edit blog posts. If you sign in as anybody else, you will only see 28 | the existing blog posts. 29 | 30 | 31 | If you want to deploy the blog in production: 32 | 33 | 1. Register a new appengine application and put its id in app.yaml 34 | 35 | First register a new application at http://appengine.google.com/. 36 | Then edit app.yaml in this directory and change the "application" 37 | setting from "tornado-appenginge" to your new application id. 38 | 39 | 2. Deploy to App Engine 40 | 41 | If you registered an application id, you can now upload your new 42 | Tornado blog by running this command: 43 | 44 | appcfg update . 45 | 46 | After that, visit application_id.appspot.com, where application_id 47 | is the application you registered. 48 | 49 | -------------------------------------------------------------------------------- /maint/test/appengine/common/cgi_runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import unittest 4 | 5 | # Most of our tests depend on IOLoop, which is not usable on app engine. 6 | # Run the tests that work, and check that everything else is at least 7 | # importable (via tornado.test.import_test) 8 | TEST_MODULES = [ 9 | 'tornado.httputil.doctests', 10 | 'tornado.iostream.doctests', 11 | 'tornado.util.doctests', 12 | #'tornado.test.auth_test', 13 | #'tornado.test.concurrent_test', 14 | #'tornado.test.curl_httpclient_test', 15 | 'tornado.test.escape_test', 16 | #'tornado.test.gen_test', 17 | #'tornado.test.httpclient_test', 18 | #'tornado.test.httpserver_test', 19 | 'tornado.test.httputil_test', 20 | 'tornado.test.import_test', 21 | #'tornado.test.ioloop_test', 22 | #'tornado.test.iostream_test', 23 | 'tornado.test.locale_test', 24 | #'tornado.test.netutil_test', 25 | #'tornado.test.log_test', 26 | 'tornado.test.options_test', 27 | #'tornado.test.process_test', 28 | #'tornado.test.simple_httpclient_test', 29 | #'tornado.test.stack_context_test', 30 | 'tornado.test.template_test', 31 | #'tornado.test.testing_test', 32 | #'tornado.test.twisted_test', 33 | 'tornado.test.util_test', 34 | #'tornado.test.web_test', 35 | #'tornado.test.websocket_test', 36 | #'tornado.test.wsgi_test', 37 | ] 38 | 39 | def all(): 40 | return unittest.defaultTestLoader.loadTestsFromNames(TEST_MODULES) 41 | 42 | def main(): 43 | print "Content-Type: text/plain\r\n\r\n", 44 | 45 | try: 46 | unittest.main(defaultTest='all', argv=sys.argv[:1]) 47 | except SystemExit, e: 48 | if e.code == 0: 49 | print "PASS" 50 | else: 51 | raise 52 | 53 | if __name__ == '__main__': 54 | main() 55 | -------------------------------------------------------------------------------- /docs/releases/v3.2.1.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 3.2.1 2 | =========================== 3 | 4 | May 5, 2014 5 | ----------- 6 | 7 | Security fixes 8 | ~~~~~~~~~~~~~~ 9 | 10 | * The signed-value format used by `.RequestHandler.set_secure_cookie` 11 | and `.RequestHandler.get_secure_cookie` has changed to be more secure. 12 | **This is a disruptive change**. The ``secure_cookie`` functions 13 | take new ``version`` parameters to support transitions between cookie 14 | formats. 15 | * The new cookie format fixes a vulnerability that may be present in 16 | applications that use multiple cookies where the name of one cookie 17 | is a prefix of the name of another. 18 | * To minimize disruption, cookies in the older format will be accepted 19 | by default until they expire. Applications that may be vulnerable 20 | can reject all cookies in the older format by passing ``min_version=2`` 21 | to `.RequestHandler.get_secure_cookie`. 22 | * Thanks to Joost Pol of `Certified Secure `_ 23 | for reporting this issue. 24 | 25 | Backwards-compatibility notes 26 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 27 | 28 | * Signed cookies issued by `.RequestHandler.set_secure_cookie` in Tornado 29 | 3.2.1 cannot be read by older releases. If you need to run 3.2.1 30 | in parallel with older releases, you can pass ``version=1`` to 31 | `.RequestHandler.set_secure_cookie` to issue cookies that are 32 | backwards-compatible (but have a known weakness, so this option 33 | should only be used for a transitional period). 34 | 35 | Other changes 36 | ~~~~~~~~~~~~~ 37 | 38 | * The C extension used to speed up the websocket module now compiles 39 | correctly on Windows with MSVC and 64-bit mode. The fallback to 40 | the pure-Python alternative now works correctly on Mac OS X machines 41 | with no C compiler installed. 42 | -------------------------------------------------------------------------------- /docs/ioloop.rst: -------------------------------------------------------------------------------- 1 | ``tornado.ioloop`` --- Main event loop 2 | ====================================== 3 | 4 | .. automodule:: tornado.ioloop 5 | 6 | IOLoop objects 7 | -------------- 8 | 9 | .. autoclass:: IOLoop 10 | 11 | Running an IOLoop 12 | ^^^^^^^^^^^^^^^^^ 13 | 14 | .. automethod:: IOLoop.current 15 | .. automethod:: IOLoop.make_current 16 | .. automethod:: IOLoop.instance 17 | .. automethod:: IOLoop.initialized 18 | .. automethod:: IOLoop.install 19 | .. automethod:: IOLoop.clear_instance 20 | .. automethod:: IOLoop.start 21 | .. automethod:: IOLoop.stop 22 | .. automethod:: IOLoop.run_sync 23 | .. automethod:: IOLoop.close 24 | 25 | I/O events 26 | ^^^^^^^^^^ 27 | 28 | .. automethod:: IOLoop.add_handler 29 | .. automethod:: IOLoop.update_handler 30 | .. automethod:: IOLoop.remove_handler 31 | 32 | Callbacks and timeouts 33 | ^^^^^^^^^^^^^^^^^^^^^^ 34 | 35 | .. automethod:: IOLoop.add_callback 36 | .. automethod:: IOLoop.add_callback_from_signal 37 | .. automethod:: IOLoop.add_future 38 | .. automethod:: IOLoop.add_timeout 39 | .. automethod:: IOLoop.call_at 40 | .. automethod:: IOLoop.call_later 41 | .. automethod:: IOLoop.remove_timeout 42 | .. automethod:: IOLoop.spawn_callback 43 | .. automethod:: IOLoop.time 44 | .. autoclass:: PeriodicCallback 45 | :members: 46 | 47 | Debugging and error handling 48 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 49 | 50 | .. automethod:: IOLoop.handle_callback_exception 51 | .. automethod:: IOLoop.set_blocking_signal_threshold 52 | .. automethod:: IOLoop.set_blocking_log_threshold 53 | .. automethod:: IOLoop.log_stack 54 | 55 | Methods for subclasses 56 | ^^^^^^^^^^^^^^^^^^^^^^ 57 | 58 | .. automethod:: IOLoop.initialize 59 | .. automethod:: IOLoop.close_fd 60 | .. automethod:: IOLoop.split_fd 61 | -------------------------------------------------------------------------------- /demos/benchmark/template_benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # A simple benchmark of tornado template rendering, based on 4 | # https://github.com/mitsuhiko/jinja2/blob/master/examples/bench.py 5 | 6 | import sys 7 | from timeit import Timer 8 | 9 | from tornado.options import options, define, parse_command_line 10 | from tornado.template import Template 11 | 12 | define('num', default=100, help='number of iterations') 13 | define('dump', default=False, help='print template generated code and exit') 14 | 15 | context = { 16 | 'page_title': 'mitsuhiko\'s benchmark', 17 | 'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) for x in range(1000)] 18 | } 19 | 20 | tmpl = Template("""\ 21 | 22 | 23 | 24 | {{ page_title }} 25 | 26 | 27 |
28 |

{{ page_title }}

29 |
30 | 39 |
40 | 41 | {% for row in table %} 42 | 43 | {% for cell in row %} 44 | 45 | {% end %} 46 | 47 | {% end %} 48 |
{{ cell }}
49 |
50 | 51 | \ 52 | """) 53 | 54 | def render(): 55 | tmpl.generate(**context) 56 | 57 | def main(): 58 | parse_command_line() 59 | if options.dump: 60 | print(tmpl.code) 61 | sys.exit(0) 62 | t = Timer(render) 63 | results = t.timeit(options.num) / options.num 64 | print('%0.3f ms per iteration' % (results*1000)) 65 | 66 | if __name__ == '__main__': 67 | main() 68 | -------------------------------------------------------------------------------- /docs/gen.rst: -------------------------------------------------------------------------------- 1 | ``tornado.gen`` --- Simplify asynchronous code 2 | ============================================== 3 | 4 | .. testsetup:: 5 | 6 | from tornado.web import * 7 | from tornado import gen 8 | 9 | .. automodule:: tornado.gen 10 | 11 | Decorators 12 | ---------- 13 | 14 | .. autofunction:: coroutine 15 | 16 | .. autofunction:: engine 17 | 18 | Utility functions 19 | ----------------- 20 | 21 | .. autoexception:: Return 22 | 23 | .. autofunction:: with_timeout 24 | .. autoexception:: TimeoutError 25 | 26 | .. autofunction:: sleep 27 | 28 | .. autodata:: moment 29 | :annotation: 30 | 31 | .. autoclass:: WaitIterator 32 | :members: 33 | 34 | .. autofunction:: multi 35 | 36 | .. autofunction:: multi_future 37 | 38 | .. autofunction:: Task 39 | 40 | .. class:: Arguments 41 | 42 | The result of a `Task` or `Wait` whose callback had more than one 43 | argument (or keyword arguments). 44 | 45 | The `Arguments` object is a `collections.namedtuple` and can be 46 | used either as a tuple ``(args, kwargs)`` or an object with attributes 47 | ``args`` and ``kwargs``. 48 | 49 | .. autofunction:: convert_yielded 50 | 51 | .. autofunction:: maybe_future 52 | 53 | .. autofunction:: is_coroutine_function 54 | 55 | Legacy interface 56 | ---------------- 57 | 58 | Before support for `Futures <.Future>` was introduced in Tornado 3.0, 59 | coroutines used subclasses of `YieldPoint` in their ``yield`` expressions. 60 | These classes are still supported but should generally not be used 61 | except for compatibility with older interfaces. None of these classes 62 | are compatible with native (``await``-based) coroutines. 63 | 64 | .. autoclass:: YieldPoint 65 | :members: 66 | 67 | .. autoclass:: Callback 68 | 69 | .. autoclass:: Wait 70 | 71 | .. autoclass:: WaitAll 72 | 73 | .. autoclass:: MultiYieldPoint 74 | -------------------------------------------------------------------------------- /demos/facebook/static/facebook.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009 Facebook 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | body { 18 | background: white; 19 | color: black; 20 | margin: 15px; 21 | } 22 | 23 | body, 24 | input, 25 | textarea { 26 | font-family: "Lucida Grande", Tahoma, Verdana, sans-serif; 27 | font-size: 10pt; 28 | } 29 | 30 | table { 31 | border-collapse: collapse; 32 | border: 0; 33 | } 34 | 35 | td { 36 | border: 0; 37 | padding: 0; 38 | } 39 | 40 | img { 41 | border: 0; 42 | } 43 | 44 | a { 45 | text-decoration: none; 46 | color: #3b5998; 47 | } 48 | 49 | a:hover { 50 | text-decoration: underline; 51 | } 52 | 53 | .post { 54 | border-bottom: 1px solid #eeeeee; 55 | min-height: 50px; 56 | padding-bottom: 10px; 57 | margin-top: 10px; 58 | } 59 | 60 | .post .picture { 61 | float: left; 62 | } 63 | 64 | .post .picture img { 65 | height: 50px; 66 | width: 50px; 67 | } 68 | 69 | .post .body { 70 | margin-left: 60px; 71 | } 72 | 73 | .post .media img { 74 | border: 1px solid #cccccc; 75 | padding: 3px; 76 | } 77 | 78 | .post .media:hover img { 79 | border: 1px solid #3b5998; 80 | } 81 | 82 | .post a.actor { 83 | font-weight: bold; 84 | } 85 | 86 | .post .meta { 87 | font-size: 11px; 88 | } 89 | 90 | .post a.permalink { 91 | color: #777777; 92 | } 93 | 94 | #body { 95 | max-width: 700px; 96 | margin: auto; 97 | } 98 | -------------------------------------------------------------------------------- /demos/file_upload/file_receiver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Usage: python file_receiver.py 4 | 5 | Demonstrates a server that receives a multipart-form-encoded set of files in an 6 | HTTP POST, or streams in the raw data of a single file in an HTTP PUT. 7 | 8 | See file_uploader.py in this directory for code that uploads files in this format. 9 | """ 10 | 11 | import logging 12 | 13 | try: 14 | from urllib.parse import unquote 15 | except ImportError: 16 | # Python 2. 17 | from urllib import unquote 18 | 19 | import tornado.ioloop 20 | import tornado.web 21 | from tornado import options 22 | 23 | 24 | class POSTHandler(tornado.web.RequestHandler): 25 | def post(self): 26 | for field_name, files in self.request.files.items(): 27 | for info in files: 28 | filename, content_type = info['filename'], info['content_type'] 29 | body = info['body'] 30 | logging.info('POST "%s" "%s" %d bytes', 31 | filename, content_type, len(body)) 32 | 33 | self.write('OK') 34 | 35 | 36 | @tornado.web.stream_request_body 37 | class PUTHandler(tornado.web.RequestHandler): 38 | def initialize(self): 39 | self.bytes_read = 0 40 | 41 | def data_received(self, chunk): 42 | self.bytes_read += len(chunk) 43 | 44 | def put(self, filename): 45 | filename = unquote(filename) 46 | mtype = self.request.headers.get('Content-Type') 47 | logging.info('PUT "%s" "%s" %d bytes', filename, mtype, self.bytes_read) 48 | self.write('OK') 49 | 50 | 51 | def make_app(): 52 | return tornado.web.Application([ 53 | (r"/post", POSTHandler), 54 | (r"/(.*)", PUTHandler), 55 | ]) 56 | 57 | 58 | if __name__ == "__main__": 59 | # Tornado configures logging. 60 | options.parse_command_line() 61 | app = make_app() 62 | app.listen(8888) 63 | tornado.ioloop.IOLoop.current().start() 64 | -------------------------------------------------------------------------------- /maint/test/appengine/common/runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import with_statement 3 | 4 | import contextlib 5 | import errno 6 | import os 7 | import random 8 | import signal 9 | import socket 10 | import subprocess 11 | import sys 12 | import time 13 | import urllib2 14 | 15 | if __name__ == "__main__": 16 | tornado_root = os.path.abspath(os.path.join(os.path.dirname(__file__), 17 | '../../..')) 18 | # dev_appserver doesn't seem to set SO_REUSEADDR 19 | port = random.randrange(10000, 11000) 20 | # does dev_appserver.py ever live anywhere but /usr/local/bin? 21 | proc = subprocess.Popen([sys.executable, 22 | "/usr/local/bin/dev_appserver.py", 23 | os.path.dirname(os.path.abspath(__file__)), 24 | "--port=%d" % port, 25 | "--skip_sdk_update_check", 26 | ], 27 | cwd=tornado_root) 28 | 29 | try: 30 | for i in xrange(50): 31 | with contextlib.closing(socket.socket()) as sock: 32 | err = sock.connect_ex(('localhost', port)) 33 | if err == 0: 34 | break 35 | elif err != errno.ECONNREFUSED: 36 | raise Exception("Got unexpected socket error %d" % err) 37 | time.sleep(0.1) 38 | else: 39 | raise Exception("Server didn't start listening") 40 | 41 | resp = urllib2.urlopen("http://localhost:%d/" % port) 42 | print resp.read() 43 | finally: 44 | # dev_appserver sometimes ignores SIGTERM (especially on 2.5), 45 | # so try a few times to kill it. 46 | for sig in [signal.SIGTERM, signal.SIGTERM, signal.SIGKILL]: 47 | os.kill(proc.pid, sig) 48 | res = os.waitpid(proc.pid, os.WNOHANG) 49 | if res != (0,0): 50 | break 51 | time.sleep(0.1) 52 | else: 53 | os.waitpid(proc.pid, 0) 54 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Appveyor is Windows CI: https://ci.appveyor.com/project/bdarnell/tornado 2 | environment: 3 | global: 4 | TORNADO_EXTENSION: "1" 5 | 6 | # We only build with 3.5+ because it works out of the box, while other 7 | # versions require lots of machinery. 8 | matrix: 9 | - PYTHON: "C:\\Python35" 10 | PYTHON_VERSION: "3.5.x" 11 | PYTHON_ARCH: "32" 12 | 13 | - PYTHON: "C:\\Python35-x64" 14 | PYTHON_VERSION: "3.5.x" 15 | PYTHON_ARCH: "64" 16 | 17 | - PYTHON: "C:\\Python36" 18 | PYTHON_VERSION: "3.6.x" 19 | PYTHON_ARCH: "32" 20 | 21 | - PYTHON: "C:\\Python36-x64" 22 | PYTHON_VERSION: "3.6.x" 23 | PYTHON_ARCH: "64" 24 | 25 | install: 26 | # Make sure the right python version is first on the PATH. 27 | - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" 28 | 29 | # Check that we have the expected version and architecture for Python 30 | - "python --version" 31 | - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" 32 | 33 | # Upgrade to the latest version of pip to avoid it displaying warnings 34 | # about it being out of date. 35 | - "pip install --disable-pip-version-check --user --upgrade pip" 36 | 37 | - "pip install tox wheel" 38 | 39 | build: false # Not a C# project, build stuff at the test step instead. 40 | 41 | test_script: 42 | # Build the compiled extension and run the project tests. 43 | # This is a bit of a hack that doesn't scale with new python versions, 44 | # but for now it lets us avoid duplication with .travis.yml and tox.ini. 45 | # Running "py3x-full" would be nice but it's failing on installing 46 | # dependencies with no useful logs. 47 | - "tox -e py35,py36 --skip-missing-interpreters" 48 | 49 | after_test: 50 | # If tests are successful, create binary packages for the project. 51 | - "python setup.py bdist_wheel" 52 | - ps: "ls dist" 53 | 54 | artifacts: 55 | # Archive the generated packages in the ci.appveyor.com build report. 56 | - path: dist\* 57 | 58 | #on_success: 59 | # - TODO: upload the content of dist/*.whl to a public wheelhouse 60 | # 61 | -------------------------------------------------------------------------------- /tornado/platform/auto.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Facebook 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """Implementation of platform-specific functionality. 18 | 19 | For each function or class described in `tornado.platform.interface`, 20 | the appropriate platform-specific implementation exists in this module. 21 | Most code that needs access to this functionality should do e.g.:: 22 | 23 | from tornado.platform.auto import set_close_exec 24 | """ 25 | 26 | from __future__ import absolute_import, division, print_function 27 | 28 | import os 29 | 30 | if 'APPENGINE_RUNTIME' in os.environ: 31 | from tornado.platform.common import Waker 32 | 33 | def set_close_exec(fd): 34 | pass 35 | elif os.name == 'nt': 36 | from tornado.platform.common import Waker 37 | from tornado.platform.windows import set_close_exec 38 | else: 39 | from tornado.platform.posix import set_close_exec, Waker 40 | 41 | try: 42 | # monotime monkey-patches the time module to have a monotonic function 43 | # in versions of python before 3.3. 44 | import monotime 45 | # Silence pyflakes warning about this unused import 46 | monotime 47 | except ImportError: 48 | pass 49 | try: 50 | # monotonic can provide a monotonic function in versions of python before 51 | # 3.3, too. 52 | from monotonic import monotonic as monotonic_time 53 | except ImportError: 54 | try: 55 | from time import monotonic as monotonic_time 56 | except ImportError: 57 | monotonic_time = None 58 | 59 | __all__ = ['Waker', 'set_close_exec', 'monotonic_time'] 60 | -------------------------------------------------------------------------------- /tornado/platform/posix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Facebook 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """Posix implementations of platform-specific functionality.""" 18 | 19 | from __future__ import absolute_import, division, print_function 20 | 21 | import fcntl 22 | import os 23 | 24 | from tornado.platform import common, interface 25 | 26 | 27 | def set_close_exec(fd): 28 | flags = fcntl.fcntl(fd, fcntl.F_GETFD) 29 | fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) 30 | 31 | 32 | def _set_nonblocking(fd): 33 | flags = fcntl.fcntl(fd, fcntl.F_GETFL) 34 | fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) 35 | 36 | 37 | class Waker(interface.Waker): 38 | def __init__(self): 39 | r, w = os.pipe() 40 | _set_nonblocking(r) 41 | _set_nonblocking(w) 42 | set_close_exec(r) 43 | set_close_exec(w) 44 | self.reader = os.fdopen(r, "rb", 0) 45 | self.writer = os.fdopen(w, "wb", 0) 46 | 47 | def fileno(self): 48 | return self.reader.fileno() 49 | 50 | def write_fileno(self): 51 | return self.writer.fileno() 52 | 53 | def wake(self): 54 | try: 55 | self.writer.write(b"x") 56 | except (IOError, ValueError): 57 | pass 58 | 59 | def consume(self): 60 | try: 61 | while True: 62 | result = self.reader.read() 63 | if not result: 64 | break 65 | except IOError: 66 | pass 67 | 68 | def close(self): 69 | self.reader.close() 70 | common.try_close(self.writer) 71 | -------------------------------------------------------------------------------- /tornado/test/http1connection_test.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, division, print_function 2 | 3 | import socket 4 | 5 | from tornado.http1connection import HTTP1Connection 6 | from tornado.httputil import HTTPMessageDelegate 7 | from tornado.iostream import IOStream 8 | from tornado.locks import Event 9 | from tornado.netutil import add_accept_handler 10 | from tornado.testing import AsyncTestCase, bind_unused_port, gen_test 11 | 12 | 13 | class HTTP1ConnectionTest(AsyncTestCase): 14 | def setUp(self): 15 | super(HTTP1ConnectionTest, self).setUp() 16 | self.asyncSetUp() 17 | 18 | @gen_test 19 | def asyncSetUp(self): 20 | listener, port = bind_unused_port() 21 | event = Event() 22 | 23 | def accept_callback(conn, addr): 24 | self.server_stream = IOStream(conn) 25 | self.addCleanup(self.server_stream.close) 26 | event.set() 27 | 28 | add_accept_handler(listener, accept_callback) 29 | self.client_stream = IOStream(socket.socket()) 30 | self.addCleanup(self.client_stream.close) 31 | yield [self.client_stream.connect(('127.0.0.1', port)), 32 | event.wait()] 33 | self.io_loop.remove_handler(listener) 34 | listener.close() 35 | 36 | @gen_test 37 | def test_http10_no_content_length(self): 38 | # Regression test for a bug in which can_keep_alive would crash 39 | # for an HTTP/1.0 (not 1.1) response with no content-length. 40 | conn = HTTP1Connection(self.client_stream, True) 41 | self.server_stream.write(b"HTTP/1.0 200 Not Modified\r\n\r\nhello") 42 | self.server_stream.close() 43 | 44 | event = Event() 45 | test = self 46 | body = [] 47 | 48 | class Delegate(HTTPMessageDelegate): 49 | def headers_received(self, start_line, headers): 50 | test.code = start_line.code 51 | 52 | def data_received(self, data): 53 | body.append(data) 54 | 55 | def finish(self): 56 | event.set() 57 | 58 | yield conn.read_response(Delegate()) 59 | yield event.wait() 60 | self.assertEqual(self.code, 200) 61 | self.assertEqual(b''.join(body), b'hello') 62 | -------------------------------------------------------------------------------- /demos/websocket/static/chat.js: -------------------------------------------------------------------------------- 1 | // Copyright 2009 FriendFeed 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | $(document).ready(function() { 16 | if (!window.console) window.console = {}; 17 | if (!window.console.log) window.console.log = function() {}; 18 | 19 | $("#messageform").on("submit", function() { 20 | newMessage($(this)); 21 | return false; 22 | }); 23 | $("#messageform").on("keypress", function(e) { 24 | if (e.keyCode == 13) { 25 | newMessage($(this)); 26 | return false; 27 | } 28 | }); 29 | $("#message").select(); 30 | updater.start(); 31 | }); 32 | 33 | function newMessage(form) { 34 | var message = form.formToDict(); 35 | updater.socket.send(JSON.stringify(message)); 36 | form.find("input[type=text]").val("").select(); 37 | } 38 | 39 | jQuery.fn.formToDict = function() { 40 | var fields = this.serializeArray(); 41 | var json = {} 42 | for (var i = 0; i < fields.length; i++) { 43 | json[fields[i].name] = fields[i].value; 44 | } 45 | if (json.next) delete json.next; 46 | return json; 47 | }; 48 | 49 | var updater = { 50 | socket: null, 51 | 52 | start: function() { 53 | var url = "ws://" + location.host + "/chatsocket"; 54 | updater.socket = new WebSocket(url); 55 | updater.socket.onmessage = function(event) { 56 | updater.showMessage(JSON.parse(event.data)); 57 | } 58 | }, 59 | 60 | showMessage: function(message) { 61 | var existing = $("#m" + message.id); 62 | if (existing.length > 0) return; 63 | var node = $(message.html); 64 | node.hide(); 65 | $("#inbox").append(node); 66 | node.slideDown(); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /maint/scripts/custom_fixers/fix_future_imports.py: -------------------------------------------------------------------------------- 1 | """Updates all source files to import the same set of __future__ directives. 2 | """ 3 | from lib2to3 import fixer_base 4 | from lib2to3 import pytree 5 | from lib2to3.pgen2 import token 6 | from lib2to3.fixer_util import FromImport, Name, Comma, Newline 7 | 8 | # copied from fix_tuple_params.py 9 | def is_docstring(stmt): 10 | return isinstance(stmt, pytree.Node) and \ 11 | stmt.children[0].type == token.STRING 12 | 13 | class FixFutureImports(fixer_base.BaseFix): 14 | BM_compatible = True 15 | 16 | PATTERN = """import_from< 'from' module_name="__future__" 'import' any >""" 17 | 18 | def start_tree(self, tree, filename): 19 | self.found_future_import = False 20 | 21 | def new_future_import(self, old): 22 | new = FromImport("__future__", 23 | [Name("absolute_import", prefix=" "), Comma(), 24 | Name("division", prefix=" "), Comma(), 25 | Name("print_function", prefix=" ")]) 26 | if old is not None: 27 | new.prefix = old.prefix 28 | return new 29 | 30 | def transform(self, node, results): 31 | self.found_future_import = True 32 | return self.new_future_import(node) 33 | 34 | def finish_tree(self, tree, filename): 35 | if self.found_future_import: 36 | return 37 | if not isinstance(tree, pytree.Node): 38 | # Empty files (usually __init__.py) show up as a single Leaf 39 | # instead of a Node, so leave them alone 40 | return 41 | first_stmt = tree.children[0] 42 | if is_docstring(first_stmt): 43 | # Skip a line and add the import after the docstring 44 | tree.insert_child(1, Newline()) 45 | pos = 2 46 | elif first_stmt.prefix: 47 | # No docstring, but an initial comment (perhaps a #! line). 48 | # Transfer the initial comment to a new blank line. 49 | newline = Newline() 50 | newline.prefix = first_stmt.prefix 51 | first_stmt.prefix = "" 52 | tree.insert_child(0, newline) 53 | pos = 1 54 | else: 55 | # No comments or docstring, just insert at the start 56 | pos = 0 57 | tree.insert_child(pos, self.new_future_import(None)) 58 | tree.insert_child(pos+1, Newline()) # terminates the import stmt 59 | -------------------------------------------------------------------------------- /tornado/platform/interface.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Facebook 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """Interfaces for platform-specific functionality. 18 | 19 | This module exists primarily for documentation purposes and as base classes 20 | for other tornado.platform modules. Most code should import the appropriate 21 | implementation from `tornado.platform.auto`. 22 | """ 23 | 24 | from __future__ import absolute_import, division, print_function 25 | 26 | 27 | def set_close_exec(fd): 28 | """Sets the close-on-exec bit (``FD_CLOEXEC``)for a file descriptor.""" 29 | raise NotImplementedError() 30 | 31 | 32 | class Waker(object): 33 | """A socket-like object that can wake another thread from ``select()``. 34 | 35 | The `~tornado.ioloop.IOLoop` will add the Waker's `fileno()` to 36 | its ``select`` (or ``epoll`` or ``kqueue``) calls. When another 37 | thread wants to wake up the loop, it calls `wake`. Once it has woken 38 | up, it will call `consume` to do any necessary per-wake cleanup. When 39 | the ``IOLoop`` is closed, it closes its waker too. 40 | """ 41 | def fileno(self): 42 | """Returns the read file descriptor for this waker. 43 | 44 | Must be suitable for use with ``select()`` or equivalent on the 45 | local platform. 46 | """ 47 | raise NotImplementedError() 48 | 49 | def write_fileno(self): 50 | """Returns the write file descriptor for this waker.""" 51 | raise NotImplementedError() 52 | 53 | def wake(self): 54 | """Triggers activity on the waker's file descriptor.""" 55 | raise NotImplementedError() 56 | 57 | def consume(self): 58 | """Called after the listen has woken up to do any necessary cleanup.""" 59 | raise NotImplementedError() 60 | 61 | def close(self): 62 | """Closes the waker's file descriptor(s).""" 63 | raise NotImplementedError() 64 | 65 | 66 | def monotonic_time(): 67 | raise NotImplementedError() 68 | -------------------------------------------------------------------------------- /tornado/test/tcpserver_test.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, division, print_function 2 | 3 | import socket 4 | 5 | from tornado import gen 6 | from tornado.iostream import IOStream 7 | from tornado.log import app_log 8 | from tornado.stack_context import NullContext 9 | from tornado.tcpserver import TCPServer 10 | from tornado.test.util import skipBefore35, exec_test 11 | from tornado.testing import AsyncTestCase, ExpectLog, bind_unused_port, gen_test 12 | 13 | 14 | class TCPServerTest(AsyncTestCase): 15 | @gen_test 16 | def test_handle_stream_coroutine_logging(self): 17 | # handle_stream may be a coroutine and any exception in its 18 | # Future will be logged. 19 | class TestServer(TCPServer): 20 | @gen.coroutine 21 | def handle_stream(self, stream, address): 22 | yield gen.moment 23 | stream.close() 24 | 1 / 0 25 | 26 | server = client = None 27 | try: 28 | sock, port = bind_unused_port() 29 | with NullContext(): 30 | server = TestServer() 31 | server.add_socket(sock) 32 | client = IOStream(socket.socket()) 33 | with ExpectLog(app_log, "Exception in callback"): 34 | yield client.connect(('localhost', port)) 35 | yield client.read_until_close() 36 | yield gen.moment 37 | finally: 38 | if server is not None: 39 | server.stop() 40 | if client is not None: 41 | client.close() 42 | 43 | @skipBefore35 44 | @gen_test 45 | def test_handle_stream_native_coroutine(self): 46 | # handle_stream may be a native coroutine. 47 | 48 | namespace = exec_test(globals(), locals(), """ 49 | class TestServer(TCPServer): 50 | async def handle_stream(self, stream, address): 51 | stream.write(b'data') 52 | stream.close() 53 | """) 54 | 55 | sock, port = bind_unused_port() 56 | server = namespace['TestServer']() 57 | server.add_socket(sock) 58 | client = IOStream(socket.socket()) 59 | yield client.connect(('localhost', port)) 60 | result = yield client.read_until_close() 61 | self.assertEqual(result, b'data') 62 | server.stop() 63 | client.close() 64 | 65 | def test_stop_twice(self): 66 | sock, port = bind_unused_port() 67 | server = TCPServer() 68 | server.add_socket(sock) 69 | server.stop() 70 | server.stop() 71 | -------------------------------------------------------------------------------- /demos/benchmark/stack_context_benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Benchmark for stack_context functionality.""" 3 | import collections 4 | import contextlib 5 | import functools 6 | import subprocess 7 | import sys 8 | 9 | from tornado import stack_context 10 | 11 | class Benchmark(object): 12 | def enter_exit(self, count): 13 | """Measures the overhead of the nested "with" statements 14 | when using many contexts. 15 | """ 16 | if count < 0: 17 | return 18 | with self.make_context(): 19 | self.enter_exit(count - 1) 20 | 21 | def call_wrapped(self, count): 22 | """Wraps and calls a function at each level of stack depth 23 | to measure the overhead of the wrapped function. 24 | """ 25 | # This queue is analogous to IOLoop.add_callback, but lets us 26 | # benchmark the stack_context in isolation without system call 27 | # overhead. 28 | queue = collections.deque() 29 | self.call_wrapped_inner(queue, count) 30 | while queue: 31 | queue.popleft()() 32 | 33 | def call_wrapped_inner(self, queue, count): 34 | if count < 0: 35 | return 36 | with self.make_context(): 37 | queue.append(stack_context.wrap( 38 | functools.partial(self.call_wrapped_inner, queue, count - 1))) 39 | 40 | class StackBenchmark(Benchmark): 41 | def make_context(self): 42 | return stack_context.StackContext(self.__context) 43 | 44 | @contextlib.contextmanager 45 | def __context(self): 46 | yield 47 | 48 | class ExceptionBenchmark(Benchmark): 49 | def make_context(self): 50 | return stack_context.ExceptionStackContext(self.__handle_exception) 51 | 52 | def __handle_exception(self, typ, value, tb): 53 | pass 54 | 55 | def main(): 56 | base_cmd = [ 57 | sys.executable, '-m', 'timeit', '-s', 58 | 'from stack_context_benchmark import StackBenchmark, ExceptionBenchmark'] 59 | cmds = [ 60 | 'StackBenchmark().enter_exit(50)', 61 | 'StackBenchmark().call_wrapped(50)', 62 | 'StackBenchmark().enter_exit(500)', 63 | 'StackBenchmark().call_wrapped(500)', 64 | 65 | 'ExceptionBenchmark().enter_exit(50)', 66 | 'ExceptionBenchmark().call_wrapped(50)', 67 | 'ExceptionBenchmark().enter_exit(500)', 68 | 'ExceptionBenchmark().call_wrapped(500)', 69 | ] 70 | for cmd in cmds: 71 | print(cmd) 72 | subprocess.check_call(base_cmd + [cmd]) 73 | 74 | if __name__ == '__main__': 75 | main() 76 | -------------------------------------------------------------------------------- /demos/blog/README: -------------------------------------------------------------------------------- 1 | Running the Tornado Blog example app 2 | ==================================== 3 | This demo is a simple blogging engine that uses MySQL to store posts and 4 | Google Accounts for author authentication. Since it depends on MySQL, you 5 | need to set up MySQL and the database schema for the demo to run. 6 | 7 | If you have `docker` and `docker-compose` installed, the demo and all 8 | its prerequisites can be installed with `docker-compose up`. 9 | 10 | 1. Install prerequisites and build tornado 11 | 12 | See http://www.tornadoweb.org/ for installation instructions. If you can 13 | run the "helloworld" example application, your environment is set up 14 | correctly. 15 | 16 | 2. Install MySQL if needed 17 | 18 | Consult the documentation for your platform. Under Ubuntu Linux you 19 | can run "apt-get install mysql". Under OS X you can download the 20 | MySQL PKG file from http://dev.mysql.com/downloads/mysql/ 21 | 22 | 3. Install Python prerequisites 23 | 24 | Install the packages MySQL-python, torndb, and markdown (e.g. using pip or 25 | easy_install). Note that these packages currently only work on 26 | Python 2. Tornado supports Python 3, but this blog demo does not. 27 | 28 | 3. Connect to MySQL and create a database and user for the blog. 29 | 30 | Connect to MySQL as a user that can create databases and users: 31 | mysql -u root 32 | 33 | Create a database named "blog": 34 | mysql> CREATE DATABASE blog; 35 | 36 | Allow the "blog" user to connect with the password "blog": 37 | mysql> GRANT ALL PRIVILEGES ON blog.* TO 'blog'@'localhost' IDENTIFIED BY 'blog'; 38 | 39 | 4. Create the tables in your new database. 40 | 41 | You can use the provided schema.sql file by running this command: 42 | mysql --user=blog --password=blog --database=blog < schema.sql 43 | 44 | You can run the above command again later if you want to delete the 45 | contents of the blog and start over after testing. 46 | 47 | 5. Run the blog example 48 | 49 | With the default user, password, and database you can just run: 50 | ./blog.py 51 | 52 | If you've changed anything, you can alter the default MySQL settings 53 | with arguments on the command line, e.g.: 54 | ./blog.py --mysql_user=casey --mysql_password=happiness --mysql_database=foodblog 55 | 56 | 6. Visit your new blog 57 | 58 | Open http://localhost:8888/ in your web browser. You will be redirected to 59 | a Google account sign-in page because the blog uses Google accounts for 60 | authentication. 61 | 62 | Currently the first user to connect will automatically be given the 63 | ability to create and edit posts. 64 | 65 | Once you've created one blog post, subsequent users will not be 66 | prompted to sign in. 67 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Ensure we get the local copy of tornado instead of what's on the standard path 2 | import os 3 | import sys 4 | import time 5 | sys.path.insert(0, os.path.abspath("..")) 6 | import tornado 7 | 8 | master_doc = "index" 9 | 10 | project = "Tornado" 11 | copyright = "2009-%s, The Tornado Authors" % time.strftime("%Y") 12 | 13 | version = release = tornado.version 14 | 15 | extensions = [ 16 | "sphinx.ext.autodoc", 17 | "sphinx.ext.coverage", 18 | "sphinx.ext.doctest", 19 | "sphinx.ext.intersphinx", 20 | "sphinx.ext.viewcode", 21 | ] 22 | 23 | primary_domain = 'py' 24 | default_role = 'py:obj' 25 | 26 | autodoc_member_order = "bysource" 27 | autoclass_content = "both" 28 | 29 | # Without this line sphinx includes a copy of object.__init__'s docstring 30 | # on any class that doesn't define __init__. 31 | # https://bitbucket.org/birkenfeld/sphinx/issue/1337/autoclass_content-both-uses-object__init__ 32 | autodoc_docstring_signature = False 33 | 34 | coverage_skip_undoc_in_source = True 35 | coverage_ignore_modules = [ 36 | "tornado.platform.asyncio", 37 | "tornado.platform.caresresolver", 38 | "tornado.platform.twisted", 39 | ] 40 | # I wish this could go in a per-module file... 41 | coverage_ignore_classes = [ 42 | # tornado.concurrent 43 | "TracebackFuture", 44 | 45 | # tornado.gen 46 | "Runner", 47 | 48 | # tornado.ioloop 49 | "PollIOLoop", 50 | 51 | # tornado.web 52 | "ChunkedTransferEncoding", 53 | "GZipContentEncoding", 54 | "OutputTransform", 55 | "TemplateModule", 56 | "url", 57 | 58 | # tornado.websocket 59 | "WebSocketProtocol", 60 | "WebSocketProtocol13", 61 | "WebSocketProtocol76", 62 | ] 63 | 64 | coverage_ignore_functions = [ 65 | # various modules 66 | "doctests", 67 | "main", 68 | 69 | # tornado.escape 70 | # parse_qs_bytes should probably be documented but it's complicated by 71 | # having different implementations between py2 and py3. 72 | "parse_qs_bytes", 73 | 74 | # tornado.gen 75 | "Multi", 76 | ] 77 | 78 | html_favicon = 'favicon.ico' 79 | 80 | latex_documents = [ 81 | ('index', 'tornado.tex', 'Tornado Documentation', 'The Tornado Authors', 'manual', False), 82 | ] 83 | 84 | intersphinx_mapping = { 85 | 'python': ('https://docs.python.org/3.5/', None), 86 | } 87 | 88 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 89 | 90 | # On RTD we can't import sphinx_rtd_theme, but it will be applied by 91 | # default anyway. This block will use the same theme when building locally 92 | # as on RTD. 93 | if not on_rtd: 94 | import sphinx_rtd_theme 95 | html_theme = 'sphinx_rtd_theme' 96 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 97 | -------------------------------------------------------------------------------- /demos/benchmark/benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # A simple benchmark of tornado's HTTP stack. 4 | # Requires 'ab' to be installed. 5 | # 6 | # Running without profiling: 7 | # demos/benchmark/benchmark.py 8 | # demos/benchmark/benchmark.py --quiet --num_runs=5|grep "Requests per second" 9 | # 10 | # Running with profiling: 11 | # 12 | # python -m cProfile -o /tmp/prof demos/benchmark/benchmark.py 13 | # python -m pstats /tmp/prof 14 | # % sort time 15 | # % stats 20 16 | 17 | from tornado.ioloop import IOLoop 18 | from tornado.options import define, options, parse_command_line 19 | from tornado.web import RequestHandler, Application 20 | 21 | import random 22 | import signal 23 | import subprocess 24 | 25 | try: 26 | xrange 27 | except NameError: 28 | xrange = range 29 | 30 | # choose a random port to avoid colliding with TIME_WAIT sockets left over 31 | # from previous runs. 32 | define("min_port", type=int, default=8000) 33 | define("max_port", type=int, default=9000) 34 | 35 | # Increasing --n without --keepalive will eventually run into problems 36 | # due to TIME_WAIT sockets 37 | define("n", type=int, default=15000) 38 | define("c", type=int, default=25) 39 | define("keepalive", type=bool, default=False) 40 | define("quiet", type=bool, default=False) 41 | 42 | # Repeat the entire benchmark this many times (on different ports) 43 | # This gives JITs time to warm up, etc. Pypy needs 3-5 runs at 44 | # --n=15000 for its JIT to reach full effectiveness 45 | define("num_runs", type=int, default=1) 46 | 47 | define("ioloop", type=str, default=None) 48 | 49 | class RootHandler(RequestHandler): 50 | def get(self): 51 | self.write("Hello, world") 52 | 53 | def _log(self): 54 | pass 55 | 56 | def handle_sigchld(sig, frame): 57 | IOLoop.current().add_callback_from_signal(IOLoop.current().stop) 58 | 59 | def main(): 60 | parse_command_line() 61 | if options.ioloop: 62 | IOLoop.configure(options.ioloop) 63 | for i in xrange(options.num_runs): 64 | run() 65 | 66 | def run(): 67 | io_loop = IOLoop(make_current=True) 68 | app = Application([("/", RootHandler)]) 69 | port = random.randrange(options.min_port, options.max_port) 70 | app.listen(port, address='127.0.0.1') 71 | signal.signal(signal.SIGCHLD, handle_sigchld) 72 | args = ["ab"] 73 | args.extend(["-n", str(options.n)]) 74 | args.extend(["-c", str(options.c)]) 75 | if options.keepalive: 76 | args.append("-k") 77 | if options.quiet: 78 | # just stops the progress messages printed to stderr 79 | args.append("-q") 80 | args.append("http://127.0.0.1:%d/" % port) 81 | subprocess.Popen(args) 82 | io_loop.start() 83 | io_loop.close() 84 | io_loop.clear_current() 85 | 86 | if __name__ == '__main__': 87 | main() 88 | -------------------------------------------------------------------------------- /docs/releases/v1.1.0.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 1.1 2 | ========================= 3 | 4 | Sep 7, 2010 5 | ----------- 6 | 7 | :: 8 | 9 | We are pleased to announce the release of Tornado 1.1, available from 10 | https://github.com/downloads/facebook/tornado/tornado-1.1.tar.gz 11 | 12 | Changes in this release: 13 | * RequestHandler.async_callback and related functions in other classes 14 | are no longer needed in most cases (although it's harmless to continue 15 | using them). Uncaught exceptions will now cause the request to be closed 16 | even in a callback. If you're curious how this works, see the new 17 | tornado.stack_context module. 18 | * The new tornado.testing module contains support for unit testing 19 | asynchronous IOLoop-based code. 20 | * AsyncHTTPClient has been rewritten (the new implementation was 21 | available as AsyncHTTPClient2 in Tornado 1.0; both names are 22 | supported for backwards compatibility). 23 | * The tornado.auth module has had a number of updates, including support 24 | for OAuth 2.0 and the Facebook Graph API, and upgrading Twitter and 25 | Google support to OAuth 1.0a. 26 | * The websocket module is back and supports the latest version (76) of the 27 | websocket protocol. Note that this module's interface is different 28 | from the websocket module that appeared in pre-1.0 versions of Tornado. 29 | * New method RequestHandler.initialize() can be overridden in subclasses 30 | to simplify handling arguments from URLSpecs. The sequence of methods 31 | called during initialization is documented at 32 | http://tornadoweb.org/documentation#overriding-requesthandler-methods 33 | * get_argument() and related methods now work on PUT requests in addition 34 | to POST. 35 | * The httpclient module now supports HTTP proxies. 36 | * When HTTPServer is run in SSL mode, the SSL handshake is now non-blocking. 37 | * Many smaller bug fixes and documentation updates 38 | 39 | Backwards-compatibility notes: 40 | * While most users of Tornado should not have to deal with the stack_context 41 | module directly, users of worker thread pools and similar constructs may 42 | need to use stack_context.wrap and/or NullContext to avoid memory leaks. 43 | * The new AsyncHTTPClient still works with libcurl version 7.16.x, but it 44 | performs better when both libcurl and pycurl are at least version 7.18.2. 45 | * OAuth transactions started under previous versions of the auth module 46 | cannot be completed under the new module. This applies only to the 47 | initial authorization process; once an authorized token is issued that 48 | token works with either version. 49 | 50 | Many thanks to everyone who contributed patches, bug reports, and feedback 51 | that went into this release! 52 | 53 | -Ben 54 | -------------------------------------------------------------------------------- /docs/releases/v1.0.0.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 1.0 2 | ========================= 3 | 4 | July 22, 2010 5 | ------------- 6 | 7 | :: 8 | 9 | We are pleased to announce the release of Tornado 1.0, available 10 | from 11 | https://github.com/downloads/facebook/tornado/tornado-1.0.tar.gz. 12 | There have been many changes since version 0.2; here are some of 13 | the highlights: 14 | 15 | New features: 16 | * Improved support for running other WSGI applications in a 17 | Tornado server (tested with Django and CherryPy) 18 | * Improved performance on Mac OS X and BSD (kqueue-based IOLoop), 19 | and experimental support for win32 20 | * Rewritten AsyncHTTPClient available as 21 | tornado.httpclient.AsyncHTTPClient2 (this will become the 22 | default in a future release) 23 | * Support for standard .mo files in addition to .csv in the locale 24 | module 25 | * Pre-forking support for running multiple Tornado processes at 26 | once (see HTTPServer.start()) 27 | * SSL and gzip support in HTTPServer 28 | * reverse_url() function refers to urls from the Application 29 | config by name from templates and RequestHandlers 30 | * RequestHandler.on_connection_close() callback is called when the 31 | client has closed the connection (subject to limitations of the 32 | underlying network stack, any proxies, etc) 33 | * Static files can now be served somewhere other than /static/ via 34 | the static_url_prefix application setting 35 | * URL regexes can now use named groups ("(?P)") to pass 36 | arguments to get()/post() via keyword instead of position 37 | * HTTP header dictionary-like objects now support multiple values 38 | for the same header via the get_all() and add() methods. 39 | * Several new options in the httpclient module, including 40 | prepare_curl_callback and header_callback 41 | * Improved logging configuration in tornado.options. 42 | * UIModule.html_body() can be used to return html to be inserted 43 | at the end of the document body. 44 | 45 | Backwards-incompatible changes: 46 | * RequestHandler.get_error_html() now receives the exception 47 | object as a keyword argument if the error was caused by an 48 | uncaught exception. 49 | * Secure cookies are now more secure, but incompatible with 50 | cookies set by Tornado 0.2. To read cookies set by older 51 | versions of Tornado, pass include_name=False to 52 | RequestHandler.get_secure_cookie() 53 | * Parameters passed to RequestHandler.get/post() by extraction 54 | from the path now have %-escapes decoded, for consistency with 55 | the processing that was already done with other query 56 | parameters. 57 | 58 | Many thanks to everyone who contributed patches, bug reports, and 59 | feedback that went into this release! 60 | 61 | -Ben 62 | -------------------------------------------------------------------------------- /docs/releases/v2.0.0.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 2.0 2 | ========================= 3 | 4 | Jun 21, 2011 5 | ------------ 6 | 7 | :: 8 | 9 | Major changes: 10 | * Template output is automatically escaped by default; see backwards 11 | compatibility note below. 12 | * The default AsyncHTTPClient implementation is now simple_httpclient. 13 | * Python 3.2 is now supported. 14 | 15 | Backwards compatibility: 16 | * Template autoescaping is enabled by default. Applications upgrading from 17 | a previous release of Tornado must either disable autoescaping or adapt 18 | their templates to work with it. For most applications, the simplest 19 | way to do this is to pass autoescape=None to the Application constructor. 20 | Note that this affects certain built-in methods, e.g. xsrf_form_html 21 | and linkify, which must now be called with {% raw %} instead of {} 22 | * Applications that wish to continue using curl_httpclient instead of 23 | simple_httpclient may do so by calling 24 | AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") 25 | at the beginning of the process. Users of Python 2.5 will probably want 26 | to use curl_httpclient as simple_httpclient only supports ssl on Python 2.6+. 27 | * Python 3 compatibility involved many changes throughout the codebase, 28 | so users are encouraged to test their applications more thoroughly than 29 | usual when upgrading to this release. 30 | 31 | Other changes in this release: 32 | * Templates support several new directives: 33 | - {% autoescape ...%} to control escaping behavior 34 | - {% raw ... %} for unescaped output 35 | - {% module ... %} for calling UIModules 36 | * {% module Template(path, **kwargs) %} may now be used to call another 37 | template with an independent namespace 38 | * All IOStream callbacks are now run directly on the IOLoop via add_callback. 39 | * HTTPServer now supports IPv6 where available. To disable, pass 40 | family=socket.AF_INET to HTTPServer.bind(). 41 | * HTTPClient now supports IPv6, configurable via allow_ipv6=bool on the 42 | HTTPRequest. allow_ipv6 defaults to false on simple_httpclient and true 43 | on curl_httpclient. 44 | * RequestHandlers can use an encoding other than utf-8 for query parameters 45 | by overriding decode_argument() 46 | * Performance improvements, especially for applications that use a lot of 47 | IOLoop timeouts 48 | * HTTP OPTIONS method no longer requires an XSRF token. 49 | * JSON output (RequestHandler.write(dict)) now sets Content-Type to 50 | application/json 51 | * Etag computation can now be customized or disabled by overriding 52 | RequestHandler.compute_etag 53 | * USE_SIMPLE_HTTPCLIENT environment variable is no longer supported. 54 | Use AsyncHTTPClient.configure instead. 55 | -------------------------------------------------------------------------------- /tornado/platform/select.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2012 Facebook 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | """Select-based IOLoop implementation. 17 | 18 | Used as a fallback for systems that don't support epoll or kqueue. 19 | """ 20 | from __future__ import absolute_import, division, print_function 21 | 22 | import select 23 | 24 | from tornado.ioloop import IOLoop, PollIOLoop 25 | 26 | 27 | class _Select(object): 28 | """A simple, select()-based IOLoop implementation for non-Linux systems""" 29 | def __init__(self): 30 | self.read_fds = set() 31 | self.write_fds = set() 32 | self.error_fds = set() 33 | self.fd_sets = (self.read_fds, self.write_fds, self.error_fds) 34 | 35 | def close(self): 36 | pass 37 | 38 | def register(self, fd, events): 39 | if fd in self.read_fds or fd in self.write_fds or fd in self.error_fds: 40 | raise IOError("fd %s already registered" % fd) 41 | if events & IOLoop.READ: 42 | self.read_fds.add(fd) 43 | if events & IOLoop.WRITE: 44 | self.write_fds.add(fd) 45 | if events & IOLoop.ERROR: 46 | self.error_fds.add(fd) 47 | # Closed connections are reported as errors by epoll and kqueue, 48 | # but as zero-byte reads by select, so when errors are requested 49 | # we need to listen for both read and error. 50 | # self.read_fds.add(fd) 51 | 52 | def modify(self, fd, events): 53 | self.unregister(fd) 54 | self.register(fd, events) 55 | 56 | def unregister(self, fd): 57 | self.read_fds.discard(fd) 58 | self.write_fds.discard(fd) 59 | self.error_fds.discard(fd) 60 | 61 | def poll(self, timeout): 62 | readable, writeable, errors = select.select( 63 | self.read_fds, self.write_fds, self.error_fds, timeout) 64 | events = {} 65 | for fd in readable: 66 | events[fd] = events.get(fd, 0) | IOLoop.READ 67 | for fd in writeable: 68 | events[fd] = events.get(fd, 0) | IOLoop.WRITE 69 | for fd in errors: 70 | events[fd] = events.get(fd, 0) | IOLoop.ERROR 71 | return events.items() 72 | 73 | 74 | class SelectIOLoop(PollIOLoop): 75 | def initialize(self, **kwargs): 76 | super(SelectIOLoop, self).initialize(impl=_Select(), **kwargs) 77 | -------------------------------------------------------------------------------- /demos/blog/static/blog.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009 Facebook 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | body { 18 | background: white; 19 | color: black; 20 | margin: 15px; 21 | margin-top: 0; 22 | } 23 | 24 | body, 25 | input, 26 | textarea { 27 | font-family: Georgia, serif; 28 | font-size: 12pt; 29 | } 30 | 31 | table { 32 | border-collapse: collapse; 33 | border: 0; 34 | } 35 | 36 | td { 37 | border: 0; 38 | padding: 0; 39 | } 40 | 41 | h1, 42 | h2, 43 | h3, 44 | h4 { 45 | font-family: "Helvetica Nue", Helvetica, Arial, sans-serif; 46 | margin: 0; 47 | } 48 | 49 | h1 { 50 | font-size: 20pt; 51 | } 52 | 53 | pre, 54 | code { 55 | font-family: monospace; 56 | color: #060; 57 | } 58 | 59 | pre { 60 | margin-left: 1em; 61 | padding-left: 1em; 62 | border-left: 1px solid silver; 63 | line-height: 14pt; 64 | } 65 | 66 | a, 67 | a code { 68 | color: #00c; 69 | } 70 | 71 | #body { 72 | max-width: 800px; 73 | margin: auto; 74 | } 75 | 76 | #header { 77 | background-color: #3b5998; 78 | padding: 5px; 79 | padding-left: 10px; 80 | padding-right: 10px; 81 | margin-bottom: 1em; 82 | } 83 | 84 | #header, 85 | #header a { 86 | color: white; 87 | } 88 | 89 | #header h1 a { 90 | text-decoration: none; 91 | } 92 | 93 | #footer, 94 | #content { 95 | margin-left: 10px; 96 | margin-right: 10px; 97 | } 98 | 99 | #footer { 100 | margin-top: 3em; 101 | } 102 | 103 | .entry h1 a { 104 | color: black; 105 | text-decoration: none; 106 | } 107 | 108 | .entry { 109 | margin-bottom: 2em; 110 | } 111 | 112 | .entry .date { 113 | margin-top: 3px; 114 | } 115 | 116 | .entry p { 117 | margin: 0; 118 | margin-bottom: 1em; 119 | } 120 | 121 | .entry .body { 122 | margin-top: 1em; 123 | line-height: 16pt; 124 | } 125 | 126 | .compose td { 127 | vertical-align: middle; 128 | padding-bottom: 5px; 129 | } 130 | 131 | .compose td.field { 132 | padding-right: 10px; 133 | } 134 | 135 | .compose .title, 136 | .compose .submit { 137 | font-family: "Helvetica Nue", Helvetica, Arial, sans-serif; 138 | font-weight: bold; 139 | } 140 | 141 | .compose .title { 142 | font-size: 20pt; 143 | } 144 | 145 | .compose .title, 146 | .compose .markdown { 147 | width: 100%; 148 | } 149 | 150 | .compose .markdown { 151 | height: 500px; 152 | line-height: 16pt; 153 | } 154 | -------------------------------------------------------------------------------- /demos/appengine/static/blog.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009 Facebook 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | body { 18 | background: white; 19 | color: black; 20 | margin: 15px; 21 | margin-top: 0; 22 | } 23 | 24 | body, 25 | input, 26 | textarea { 27 | font-family: Georgia, serif; 28 | font-size: 12pt; 29 | } 30 | 31 | table { 32 | border-collapse: collapse; 33 | border: 0; 34 | } 35 | 36 | td { 37 | border: 0; 38 | padding: 0; 39 | } 40 | 41 | h1, 42 | h2, 43 | h3, 44 | h4 { 45 | font-family: "Helvetica Nue", Helvetica, Arial, sans-serif; 46 | margin: 0; 47 | } 48 | 49 | h1 { 50 | font-size: 20pt; 51 | } 52 | 53 | pre, 54 | code { 55 | font-family: monospace; 56 | color: #060; 57 | } 58 | 59 | pre { 60 | margin-left: 1em; 61 | padding-left: 1em; 62 | border-left: 1px solid silver; 63 | line-height: 14pt; 64 | } 65 | 66 | a, 67 | a code { 68 | color: #00c; 69 | } 70 | 71 | #body { 72 | max-width: 800px; 73 | margin: auto; 74 | } 75 | 76 | #header { 77 | background-color: #3b5998; 78 | padding: 5px; 79 | padding-left: 10px; 80 | padding-right: 10px; 81 | margin-bottom: 1em; 82 | } 83 | 84 | #header, 85 | #header a { 86 | color: white; 87 | } 88 | 89 | #header h1 a { 90 | text-decoration: none; 91 | } 92 | 93 | #footer, 94 | #content { 95 | margin-left: 10px; 96 | margin-right: 10px; 97 | } 98 | 99 | #footer { 100 | margin-top: 3em; 101 | } 102 | 103 | .entry h1 a { 104 | color: black; 105 | text-decoration: none; 106 | } 107 | 108 | .entry { 109 | margin-bottom: 2em; 110 | } 111 | 112 | .entry .date { 113 | margin-top: 3px; 114 | } 115 | 116 | .entry p { 117 | margin: 0; 118 | margin-bottom: 1em; 119 | } 120 | 121 | .entry .body { 122 | margin-top: 1em; 123 | line-height: 16pt; 124 | } 125 | 126 | .compose td { 127 | vertical-align: middle; 128 | padding-bottom: 5px; 129 | } 130 | 131 | .compose td.field { 132 | padding-right: 10px; 133 | } 134 | 135 | .compose .title, 136 | .compose .submit { 137 | font-family: "Helvetica Nue", Helvetica, Arial, sans-serif; 138 | font-weight: bold; 139 | } 140 | 141 | .compose .title { 142 | font-size: 20pt; 143 | } 144 | 145 | .compose .title, 146 | .compose .body_source { 147 | width: 100%; 148 | } 149 | 150 | .compose .body_source { 151 | height: 500px; 152 | line-height: 16pt; 153 | } 154 | -------------------------------------------------------------------------------- /docs/releases/v4.4.0.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 4.4 2 | ========================= 3 | 4 | Jul 15, 2016 5 | ------------ 6 | 7 | General 8 | ~~~~~~~ 9 | 10 | * Tornado now requires Python 2.7 or 3.3+; versions 2.6 and 3.2 are no 11 | longer supported. Pypy3 is still supported even though its latest 12 | release is mainly based on Python 3.2. 13 | * The `monotonic `_ package is 14 | now supported as an alternative to `Monotime 15 | `_ for monotonic clock support 16 | on Python 2. 17 | 18 | ``tornado.curl_httpclient`` 19 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 20 | 21 | * Failures in ``_curl_setup_request`` no longer cause the 22 | ``max_clients`` pool to be exhausted. 23 | * Non-ascii header values are now handled correctly. 24 | 25 | `tornado.gen` 26 | ~~~~~~~~~~~~~ 27 | 28 | * `.with_timeout` now accepts any yieldable object (except 29 | `.YieldPoint`), not just `tornado.concurrent.Future`. 30 | 31 | `tornado.httpclient` 32 | ~~~~~~~~~~~~~~~~~~~~ 33 | 34 | * The errors raised by timeouts now indicate what state the request 35 | was in; the error message is no longer simply "599 Timeout". 36 | * Calling `repr` on a `tornado.httpclient.HTTPError` no longer raises 37 | an error. 38 | 39 | `tornado.httpserver` 40 | ~~~~~~~~~~~~~~~~~~~~ 41 | 42 | * Int-like enums (including `http.HTTPStatus`) can now be used as 43 | status codes. 44 | * Responses with status code ``204 No Content`` no longer emit a 45 | ``Content-Length: 0`` header. 46 | 47 | `tornado.ioloop` 48 | ~~~~~~~~~~~~~~~~ 49 | 50 | * Improved performance when there are large numbers of active timeouts. 51 | 52 | `tornado.netutil` 53 | ~~~~~~~~~~~~~~~~~ 54 | 55 | * All included `.Resolver` implementations raise `IOError` (or a 56 | subclass) for any resolution failure. 57 | 58 | `tornado.options` 59 | ~~~~~~~~~~~~~~~~~ 60 | 61 | * Options can now be modified with subscript syntax in addition to 62 | attribute syntax. 63 | * The special variable ``__file__`` is now available inside config files. 64 | 65 | ``tornado.simple_httpclient`` 66 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 67 | 68 | * HTTP/1.0 (not 1.1) responses without a ``Content-Length`` header now 69 | work correctly. 70 | 71 | `tornado.tcpserver` 72 | ~~~~~~~~~~~~~~~~~~~ 73 | 74 | * `.TCPServer.bind` now accepts a ``reuse_port`` argument. 75 | 76 | `tornado.testing` 77 | ~~~~~~~~~~~~~~~~~ 78 | 79 | * Test sockets now always use ``127.0.0.1`` instead of ``localhost``. 80 | This avoids conflicts when the automatically-assigned port is 81 | available on IPv4 but not IPv6, or in unusual network configurations 82 | when ``localhost`` has multiple IP addresses. 83 | 84 | `tornado.web` 85 | ~~~~~~~~~~~~~ 86 | 87 | * ``image/svg+xml`` is now on the list of compressible mime types. 88 | * Fixed an error on Python 3 when compression is used with multiple 89 | ``Vary`` headers. 90 | 91 | `tornado.websocket` 92 | ~~~~~~~~~~~~~~~~~~~ 93 | 94 | * ``WebSocketHandler.__init__`` now uses `super`, which improves 95 | support for multiple inheritance. 96 | -------------------------------------------------------------------------------- /docs/releases/v2.4.0.rst: -------------------------------------------------------------------------------- 1 | What's new in Tornado 2.4 2 | ========================= 3 | 4 | Sep 4, 2012 5 | ----------- 6 | 7 | General 8 | ~~~~~~~ 9 | 10 | * Fixed Python 3 bugs in `tornado.auth`, `tornado.locale`, and `tornado.wsgi`. 11 | 12 | HTTP clients 13 | ~~~~~~~~~~~~ 14 | 15 | * Removed ``max_simultaneous_connections`` argument from `tornado.httpclient` 16 | (both implementations). This argument hasn't been useful for some time 17 | (if you were using it you probably want ``max_clients`` instead) 18 | * ``tornado.simple_httpclient`` now accepts and ignores HTTP 1xx status 19 | responses. 20 | 21 | `tornado.ioloop` and `tornado.iostream` 22 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 23 | 24 | * Fixed a bug introduced in 2.3 that would cause `.IOStream` close callbacks 25 | to not run if there were pending reads. 26 | * Improved error handling in `.SSLIOStream` and SSL-enabled `.TCPServer`. 27 | * ``SSLIOStream.get_ssl_certificate`` now has a ``binary_form`` argument 28 | which is passed to ``SSLSocket.getpeercert``. 29 | * ``SSLIOStream.write`` can now be called while the connection is in progress, 30 | same as non-SSL `.IOStream` (but be careful not to send sensitive data until 31 | the connection has completed and the certificate has been verified). 32 | * `.IOLoop.add_handler` cannot be called more than once with the same file 33 | descriptor. This was always true for ``epoll``, but now the other 34 | implementations enforce it too. 35 | * On Windows, `.TCPServer` uses ``SO_EXCLUSIVEADDRUSER`` instead of ``SO_REUSEADDR``. 36 | 37 | `tornado.template` 38 | ~~~~~~~~~~~~~~~~~~ 39 | 40 | * ``{% break %}`` and ``{% continue %}`` can now be used looping constructs 41 | in templates. 42 | * It is no longer an error for an if/else/for/etc block in a template to 43 | have an empty body. 44 | 45 | `tornado.testing` 46 | ~~~~~~~~~~~~~~~~~ 47 | 48 | * New class `tornado.testing.AsyncHTTPSTestCase` is like `.AsyncHTTPTestCase`. 49 | but enables SSL for the testing server (by default using a self-signed 50 | testing certificate). 51 | * `tornado.testing.main` now accepts additional keyword arguments and forwards 52 | them to `unittest.main`. 53 | 54 | `tornado.web` 55 | ~~~~~~~~~~~~~ 56 | 57 | * New method `.RequestHandler.get_template_namespace` can be overridden to 58 | add additional variables without modifying keyword arguments to 59 | ``render_string``. 60 | * `.RequestHandler.add_header` now works with `.WSGIApplication`. 61 | * `.RequestHandler.get_secure_cookie` now handles a potential error case. 62 | * ``RequestHandler.__init__`` now calls ``super().__init__`` to ensure that 63 | all constructors are called when multiple inheritance is used. 64 | * Docs have been updated with a description of all available 65 | :py:attr:`Application settings ` 66 | 67 | Other modules 68 | ~~~~~~~~~~~~~ 69 | 70 | * `.OAuthMixin` now accepts ``"oob"`` as a ``callback_uri``. 71 | * `.OpenIdMixin` now also returns the ``claimed_id`` field for the user. 72 | * `tornado.platform.twisted` shutdown sequence is now more compatible. 73 | * The logging configuration used in `tornado.options` is now more tolerant 74 | of non-ascii byte strings. 75 | -------------------------------------------------------------------------------- /maint/circlerefs/circlerefs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Test script to find circular references. 3 | 4 | Circular references are not leaks per se, because they will eventually 5 | be GC'd. However, on CPython, they prevent the reference-counting fast 6 | path from being used and instead rely on the slower full GC. This 7 | increases memory footprint and CPU overhead, so we try to eliminate 8 | circular references created by normal operation. 9 | """ 10 | from __future__ import print_function 11 | 12 | import gc 13 | import traceback 14 | import types 15 | from tornado import web, ioloop, gen, httpclient 16 | 17 | 18 | def find_circular_references(garbage=None): 19 | def inner(level): 20 | for item in level: 21 | item_id = id(item) 22 | if item_id not in garbage_ids: 23 | continue 24 | if item_id in visited_ids: 25 | continue 26 | if item_id in stack_ids: 27 | candidate = stack[stack.index(item):] 28 | candidate.append(item) 29 | found.append(candidate) 30 | continue 31 | 32 | stack.append(item) 33 | stack_ids.add(item_id) 34 | inner(gc.get_referents(item)) 35 | stack.pop() 36 | stack_ids.remove(item_id) 37 | visited_ids.add(item_id) 38 | 39 | garbage = garbage or gc.garbage 40 | found = [] 41 | stack = [] 42 | stack_ids = set() 43 | garbage_ids = set(map(id, garbage)) 44 | visited_ids = set() 45 | 46 | inner(garbage) 47 | inner = None 48 | return found 49 | 50 | 51 | class CollectHandler(web.RequestHandler): 52 | @gen.coroutine 53 | def get(self): 54 | self.write("Collected: {}\n".format(gc.collect())) 55 | self.write("Garbage: {}\n".format(len(gc.garbage))) 56 | for circular in find_circular_references(): 57 | print('\n==========\n Circular \n==========') 58 | for item in circular: 59 | print(' ', repr(item)) 60 | for item in circular: 61 | if isinstance(item, types.FrameType): 62 | print('\nLocals:', item.f_locals) 63 | print('\nTraceback:', repr(item)) 64 | traceback.print_stack(item) 65 | 66 | 67 | class DummyHandler(web.RequestHandler): 68 | @gen.coroutine 69 | def get(self): 70 | self.write('ok\n') 71 | 72 | 73 | application = web.Application([ 74 | (r'/dummy/', DummyHandler), 75 | (r'/collect/', CollectHandler), 76 | ], debug=True) 77 | 78 | 79 | @gen.coroutine 80 | def main(): 81 | gc.disable() 82 | gc.collect() 83 | gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_SAVEALL) 84 | print('GC disabled') 85 | 86 | print("Start on 8888") 87 | application.listen(8888, '127.0.0.1') 88 | 89 | # Do a little work. Alternately, could leave this script running and 90 | # poke at it with a browser. 91 | client = httpclient.AsyncHTTPClient() 92 | yield client.fetch('http://127.0.0.1:8888/dummy/') 93 | 94 | # Now report on the results. 95 | gc.collect() 96 | resp = yield client.fetch('http://127.0.0.1:8888/collect/') 97 | print(resp.body) 98 | 99 | if __name__ == "__main__": 100 | ioloop.IOLoop.current().run_sync(main) 101 | -------------------------------------------------------------------------------- /tornado/platform/caresresolver.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, division, print_function 2 | import pycares # type: ignore 3 | import socket 4 | 5 | from tornado import gen 6 | from tornado.ioloop import IOLoop 7 | from tornado.netutil import Resolver, is_valid_ip 8 | 9 | 10 | class CaresResolver(Resolver): 11 | """Name resolver based on the c-ares library. 12 | 13 | This is a non-blocking and non-threaded resolver. It may not produce 14 | the same results as the system resolver, but can be used for non-blocking 15 | resolution when threads cannot be used. 16 | 17 | c-ares fails to resolve some names when ``family`` is ``AF_UNSPEC``, 18 | so it is only recommended for use in ``AF_INET`` (i.e. IPv4). This is 19 | the default for ``tornado.simple_httpclient``, but other libraries 20 | may default to ``AF_UNSPEC``. 21 | 22 | .. versionchanged:: 4.1 23 | The ``io_loop`` argument is deprecated. 24 | """ 25 | def initialize(self, io_loop=None): 26 | self.io_loop = io_loop or IOLoop.current() 27 | self.channel = pycares.Channel(sock_state_cb=self._sock_state_cb) 28 | self.fds = {} 29 | 30 | def _sock_state_cb(self, fd, readable, writable): 31 | state = ((IOLoop.READ if readable else 0) | 32 | (IOLoop.WRITE if writable else 0)) 33 | if not state: 34 | self.io_loop.remove_handler(fd) 35 | del self.fds[fd] 36 | elif fd in self.fds: 37 | self.io_loop.update_handler(fd, state) 38 | self.fds[fd] = state 39 | else: 40 | self.io_loop.add_handler(fd, self._handle_events, state) 41 | self.fds[fd] = state 42 | 43 | def _handle_events(self, fd, events): 44 | read_fd = pycares.ARES_SOCKET_BAD 45 | write_fd = pycares.ARES_SOCKET_BAD 46 | if events & IOLoop.READ: 47 | read_fd = fd 48 | if events & IOLoop.WRITE: 49 | write_fd = fd 50 | self.channel.process_fd(read_fd, write_fd) 51 | 52 | @gen.coroutine 53 | def resolve(self, host, port, family=0): 54 | if is_valid_ip(host): 55 | addresses = [host] 56 | else: 57 | # gethostbyname doesn't take callback as a kwarg 58 | self.channel.gethostbyname(host, family, (yield gen.Callback(1))) 59 | callback_args = yield gen.Wait(1) 60 | assert isinstance(callback_args, gen.Arguments) 61 | assert not callback_args.kwargs 62 | result, error = callback_args.args 63 | if error: 64 | raise IOError('C-Ares returned error %s: %s while resolving %s' % 65 | (error, pycares.errno.strerror(error), host)) 66 | addresses = result.addresses 67 | addrinfo = [] 68 | for address in addresses: 69 | if '.' in address: 70 | address_family = socket.AF_INET 71 | elif ':' in address: 72 | address_family = socket.AF_INET6 73 | else: 74 | address_family = socket.AF_UNSPEC 75 | if family != socket.AF_UNSPEC and family != address_family: 76 | raise IOError('Requested socket family %d but got %d' % 77 | (family, address_family)) 78 | addrinfo.append((address_family, (address, port))) 79 | raise gen.Return(addrinfo) 80 | -------------------------------------------------------------------------------- /demos/webspider/webspider.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import time 4 | from datetime import timedelta 5 | 6 | try: 7 | from HTMLParser import HTMLParser 8 | from urlparse import urljoin, urldefrag 9 | except ImportError: 10 | from html.parser import HTMLParser 11 | from urllib.parse import urljoin, urldefrag 12 | 13 | from tornado import httpclient, gen, ioloop, queues 14 | 15 | base_url = 'http://www.tornadoweb.org/en/stable/' 16 | concurrency = 10 17 | 18 | 19 | @gen.coroutine 20 | def get_links_from_url(url): 21 | """Download the page at `url` and parse it for links. 22 | 23 | Returned links have had the fragment after `#` removed, and have been made 24 | absolute so, e.g. the URL 'gen.html#tornado.gen.coroutine' becomes 25 | 'http://www.tornadoweb.org/en/stable/gen.html'. 26 | """ 27 | try: 28 | response = yield httpclient.AsyncHTTPClient().fetch(url) 29 | print('fetched %s' % url) 30 | 31 | html = response.body if isinstance(response.body, str) \ 32 | else response.body.decode() 33 | urls = [urljoin(url, remove_fragment(new_url)) 34 | for new_url in get_links(html)] 35 | except Exception as e: 36 | print('Exception: %s %s' % (e, url)) 37 | raise gen.Return([]) 38 | 39 | raise gen.Return(urls) 40 | 41 | 42 | def remove_fragment(url): 43 | pure_url, frag = urldefrag(url) 44 | return pure_url 45 | 46 | 47 | def get_links(html): 48 | class URLSeeker(HTMLParser): 49 | def __init__(self): 50 | HTMLParser.__init__(self) 51 | self.urls = [] 52 | 53 | def handle_starttag(self, tag, attrs): 54 | href = dict(attrs).get('href') 55 | if href and tag == 'a': 56 | self.urls.append(href) 57 | 58 | url_seeker = URLSeeker() 59 | url_seeker.feed(html) 60 | return url_seeker.urls 61 | 62 | 63 | @gen.coroutine 64 | def main(): 65 | q = queues.Queue() 66 | start = time.time() 67 | fetching, fetched = set(), set() 68 | 69 | @gen.coroutine 70 | def fetch_url(): 71 | current_url = yield q.get() 72 | try: 73 | if current_url in fetching: 74 | return 75 | 76 | print('fetching %s' % current_url) 77 | fetching.add(current_url) 78 | urls = yield get_links_from_url(current_url) 79 | fetched.add(current_url) 80 | 81 | for new_url in urls: 82 | # Only follow links beneath the base URL 83 | if new_url.startswith(base_url): 84 | yield q.put(new_url) 85 | 86 | finally: 87 | q.task_done() 88 | 89 | @gen.coroutine 90 | def worker(): 91 | while True: 92 | yield fetch_url() 93 | 94 | q.put(base_url) 95 | 96 | # Start workers, then wait for the work queue to be empty. 97 | for _ in range(concurrency): 98 | worker() 99 | yield q.join(timeout=timedelta(seconds=300)) 100 | assert fetching == fetched 101 | print('Done in %d seconds, fetched %s URLs.' % ( 102 | time.time() - start, len(fetched))) 103 | 104 | 105 | if __name__ == '__main__': 106 | import logging 107 | logging.basicConfig() 108 | io_loop = ioloop.IOLoop.current() 109 | io_loop.run_sync(main) 110 | -------------------------------------------------------------------------------- /maint/vm/windows/bootstrap.py: -------------------------------------------------------------------------------- 1 | r"""Installs files needed for tornado testing on windows. 2 | 3 | These instructions are compatible with the VMs provided by http://modern.ie. 4 | The bootstrapping script works on the WinXP/IE6 and Win8/IE10 configurations, 5 | although tornado's tests do not pass on XP. 6 | 7 | 1) Install virtualbox guest additions (from the device menu in virtualbox) 8 | 2) Set up a shared folder to the root of your tornado repo. It must be a 9 | read-write mount to use tox, although the tests can be run directly 10 | in a read-only mount. This will probably assign drive letter E:. 11 | 3) Install Python 2.7 from python.org. 12 | 4) Run this script by double-clicking it, or running 13 | "c:\python27\python.exe bootstrap.py" in a shell. 14 | 15 | To run the tests by hand, cd to e:\ and run 16 | c:\python27\python.exe -m tornado.test.runtests 17 | To run the tests with tox, cd to e:\maint\vm\windows and run 18 | c:\python27\scripts\tox 19 | To run under cygwin (which must be installed separately), run 20 | cd /cygdrive/e; python -m tornado.test.runtests 21 | """ 22 | 23 | import os 24 | import subprocess 25 | import sys 26 | import urllib 27 | 28 | TMPDIR = r'c:\tornado_bootstrap' 29 | 30 | PYTHON_VERSIONS = [ 31 | (r'c:\python27\python.exe', 'http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi'), 32 | (r'c:\python33\python.exe', 'http://www.python.org/ftp/python/3.3.0/python-3.3.0.msi'), 33 | ] 34 | 35 | SCRIPTS_DIR = r'c:\python27\scripts' 36 | EASY_INSTALL = os.path.join(SCRIPTS_DIR, 'easy_install.exe') 37 | 38 | PY_PACKAGES = ['tox', 'virtualenv', 'pip'] 39 | 40 | def download_to_cache(url, local_name=None): 41 | if local_name is None: 42 | local_name = url.split('/')[-1] 43 | filename = os.path.join(TMPDIR, local_name) 44 | if not os.path.exists(filename): 45 | data = urllib.urlopen(url).read() 46 | with open(filename, 'wb') as f: 47 | f.write(data) 48 | return filename 49 | 50 | def main(): 51 | if not os.path.exists(TMPDIR): 52 | os.mkdir(TMPDIR) 53 | os.chdir(TMPDIR) 54 | for exe, url in PYTHON_VERSIONS: 55 | if os.path.exists(exe): 56 | print "%s already exists, skipping" % exe 57 | continue 58 | print "Installing %s" % url 59 | filename = download_to_cache(url) 60 | # http://blog.jaraco.com/2012/01/how-i-install-python-on-windows.html 61 | subprocess.check_call(['msiexec', '/i', filename, 62 | 'ALLUSERS=1', '/passive']) 63 | 64 | if not os.path.exists(EASY_INSTALL): 65 | filename = download_to_cache('http://python-distribute.org/distribute_setup.py') 66 | subprocess.check_call([sys.executable, filename]) 67 | 68 | subprocess.check_call([EASY_INSTALL] + PY_PACKAGES) 69 | 70 | # cygwin's setup.exe doesn't like being run from a script (looks 71 | # UAC-related). If it did, something like this might install it. 72 | # (install python, python-setuptools, python3, and easy_install 73 | # unittest2 (cygwin's python 2 is 2.6)) 74 | #filename = download_to_cache('http://cygwin.com/setup.exe') 75 | #CYGTMPDIR = os.path.join(TMPDIR, 'cygwin') 76 | #if not os.path.exists(CYGTMPDIR): 77 | # os.mkdir(CYGTMPDIR) 78 | ## http://www.jbmurphy.com/2011/06/16/powershell-script-to-install-cygwin/ 79 | #CYGWIN_ARGS = [filename, '-q', '-l', CYGTMPDIR, 80 | # '-s', 'http://mirror.nyi.net/cygwin/', '-R', r'c:\cygwin'] 81 | #subprocess.check_call(CYGWIN_ARGS) 82 | 83 | 84 | if __name__ == '__main__': 85 | main() 86 | -------------------------------------------------------------------------------- /demos/websocket/chatdemo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2009 Facebook 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | """Simplified chat demo for websockets. 17 | 18 | Authentication, error handling, etc are left as an exercise for the reader :) 19 | """ 20 | 21 | import logging 22 | import tornado.escape 23 | import tornado.ioloop 24 | import tornado.options 25 | import tornado.web 26 | import tornado.websocket 27 | import os.path 28 | import uuid 29 | 30 | from tornado.options import define, options 31 | 32 | define("port", default=8888, help="run on the given port", type=int) 33 | 34 | 35 | class Application(tornado.web.Application): 36 | def __init__(self): 37 | handlers = [ 38 | (r"/", MainHandler), 39 | (r"/chatsocket", ChatSocketHandler), 40 | ] 41 | settings = dict( 42 | cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__", 43 | template_path=os.path.join(os.path.dirname(__file__), "templates"), 44 | static_path=os.path.join(os.path.dirname(__file__), "static"), 45 | xsrf_cookies=True, 46 | ) 47 | super(Application, self).__init__(handlers, **settings) 48 | 49 | 50 | class MainHandler(tornado.web.RequestHandler): 51 | def get(self): 52 | self.render("index.html", messages=ChatSocketHandler.cache) 53 | 54 | class ChatSocketHandler(tornado.websocket.WebSocketHandler): 55 | waiters = set() 56 | cache = [] 57 | cache_size = 200 58 | 59 | def get_compression_options(self): 60 | # Non-None enables compression with default options. 61 | return {} 62 | 63 | def open(self): 64 | ChatSocketHandler.waiters.add(self) 65 | 66 | def on_close(self): 67 | ChatSocketHandler.waiters.remove(self) 68 | 69 | @classmethod 70 | def update_cache(cls, chat): 71 | cls.cache.append(chat) 72 | if len(cls.cache) > cls.cache_size: 73 | cls.cache = cls.cache[-cls.cache_size:] 74 | 75 | @classmethod 76 | def send_updates(cls, chat): 77 | logging.info("sending message to %d waiters", len(cls.waiters)) 78 | for waiter in cls.waiters: 79 | try: 80 | waiter.write_message(chat) 81 | except: 82 | logging.error("Error sending message", exc_info=True) 83 | 84 | def on_message(self, message): 85 | logging.info("got message %r", message) 86 | parsed = tornado.escape.json_decode(message) 87 | chat = { 88 | "id": str(uuid.uuid4()), 89 | "body": parsed["body"], 90 | } 91 | chat["html"] = tornado.escape.to_basestring( 92 | self.render_string("message.html", message=chat)) 93 | 94 | ChatSocketHandler.update_cache(chat) 95 | ChatSocketHandler.send_updates(chat) 96 | 97 | 98 | def main(): 99 | tornado.options.parse_command_line() 100 | app = Application() 101 | app.listen(options.port) 102 | tornado.ioloop.IOLoop.current().start() 103 | 104 | 105 | if __name__ == "__main__": 106 | main() 107 | -------------------------------------------------------------------------------- /tornado/platform/kqueue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2012 Facebook 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | """KQueue-based IOLoop implementation for BSD/Mac systems.""" 17 | from __future__ import absolute_import, division, print_function 18 | 19 | import select 20 | 21 | from tornado.ioloop import IOLoop, PollIOLoop 22 | 23 | assert hasattr(select, 'kqueue'), 'kqueue not supported' 24 | 25 | 26 | class _KQueue(object): 27 | """A kqueue-based event loop for BSD/Mac systems.""" 28 | def __init__(self): 29 | self._kqueue = select.kqueue() 30 | self._active = {} 31 | 32 | def fileno(self): 33 | return self._kqueue.fileno() 34 | 35 | def close(self): 36 | self._kqueue.close() 37 | 38 | def register(self, fd, events): 39 | if fd in self._active: 40 | raise IOError("fd %s already registered" % fd) 41 | self._control(fd, events, select.KQ_EV_ADD) 42 | self._active[fd] = events 43 | 44 | def modify(self, fd, events): 45 | self.unregister(fd) 46 | self.register(fd, events) 47 | 48 | def unregister(self, fd): 49 | events = self._active.pop(fd) 50 | self._control(fd, events, select.KQ_EV_DELETE) 51 | 52 | def _control(self, fd, events, flags): 53 | kevents = [] 54 | if events & IOLoop.WRITE: 55 | kevents.append(select.kevent( 56 | fd, filter=select.KQ_FILTER_WRITE, flags=flags)) 57 | if events & IOLoop.READ: 58 | kevents.append(select.kevent( 59 | fd, filter=select.KQ_FILTER_READ, flags=flags)) 60 | # Even though control() takes a list, it seems to return EINVAL 61 | # on Mac OS X (10.6) when there is more than one event in the list. 62 | for kevent in kevents: 63 | self._kqueue.control([kevent], 0) 64 | 65 | def poll(self, timeout): 66 | kevents = self._kqueue.control(None, 1000, timeout) 67 | events = {} 68 | for kevent in kevents: 69 | fd = kevent.ident 70 | if kevent.filter == select.KQ_FILTER_READ: 71 | events[fd] = events.get(fd, 0) | IOLoop.READ 72 | if kevent.filter == select.KQ_FILTER_WRITE: 73 | if kevent.flags & select.KQ_EV_EOF: 74 | # If an asynchronous connection is refused, kqueue 75 | # returns a write event with the EOF flag set. 76 | # Turn this into an error for consistency with the 77 | # other IOLoop implementations. 78 | # Note that for read events, EOF may be returned before 79 | # all data has been consumed from the socket buffer, 80 | # so we only check for EOF on write events. 81 | events[fd] = IOLoop.ERROR 82 | else: 83 | events[fd] = events.get(fd, 0) | IOLoop.WRITE 84 | if kevent.flags & select.KQ_EV_ERROR: 85 | events[fd] = events.get(fd, 0) | IOLoop.ERROR 86 | return events.items() 87 | 88 | 89 | class KQueueIOLoop(PollIOLoop): 90 | def initialize(self, **kwargs): 91 | super(KQueueIOLoop, self).initialize(impl=_KQueue(), **kwargs) 92 | -------------------------------------------------------------------------------- /tornado/test/util.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, division, print_function 2 | 3 | import os 4 | import platform 5 | import socket 6 | import sys 7 | import textwrap 8 | 9 | from tornado.testing import bind_unused_port 10 | 11 | # Encapsulate the choice of unittest or unittest2 here. 12 | # To be used as 'from tornado.test.util import unittest'. 13 | if sys.version_info < (2, 7): 14 | # In py26, we must always use unittest2. 15 | import unittest2 as unittest # type: ignore 16 | else: 17 | # Otherwise, use whichever version of unittest was imported in 18 | # tornado.testing. 19 | from tornado.testing import unittest 20 | 21 | skipIfNonUnix = unittest.skipIf(os.name != 'posix' or sys.platform == 'cygwin', 22 | "non-unix platform") 23 | 24 | # travis-ci.org runs our tests in an overworked virtual machine, which makes 25 | # timing-related tests unreliable. 26 | skipOnTravis = unittest.skipIf('TRAVIS' in os.environ, 27 | 'timing tests unreliable on travis') 28 | 29 | skipOnAppEngine = unittest.skipIf('APPENGINE_RUNTIME' in os.environ, 30 | 'not available on Google App Engine') 31 | 32 | # Set the environment variable NO_NETWORK=1 to disable any tests that 33 | # depend on an external network. 34 | skipIfNoNetwork = unittest.skipIf('NO_NETWORK' in os.environ, 35 | 'network access disabled') 36 | 37 | skipIfNoIPv6 = unittest.skipIf(not socket.has_ipv6, 'ipv6 support not present') 38 | 39 | 40 | skipBefore33 = unittest.skipIf(sys.version_info < (3, 3), 'PEP 380 (yield from) not available') 41 | skipBefore35 = unittest.skipIf(sys.version_info < (3, 5), 'PEP 492 (async/await) not available') 42 | skipNotCPython = unittest.skipIf(platform.python_implementation() != 'CPython', 43 | 'Not CPython implementation') 44 | 45 | 46 | def refusing_port(): 47 | """Returns a local port number that will refuse all connections. 48 | 49 | Return value is (cleanup_func, port); the cleanup function 50 | must be called to free the port to be reused. 51 | """ 52 | # On travis-ci, port numbers are reassigned frequently. To avoid 53 | # collisions with other tests, we use an open client-side socket's 54 | # ephemeral port number to ensure that nothing can listen on that 55 | # port. 56 | server_socket, port = bind_unused_port() 57 | server_socket.setblocking(1) 58 | client_socket = socket.socket() 59 | client_socket.connect(("127.0.0.1", port)) 60 | conn, client_addr = server_socket.accept() 61 | conn.close() 62 | server_socket.close() 63 | return (client_socket.close, client_addr[1]) 64 | 65 | 66 | def exec_test(caller_globals, caller_locals, s): 67 | """Execute ``s`` in a given context and return the result namespace. 68 | 69 | Used to define functions for tests in particular python 70 | versions that would be syntax errors in older versions. 71 | """ 72 | # Flatten the real global and local namespace into our fake 73 | # globals: it's all global from the perspective of code defined 74 | # in s. 75 | global_namespace = dict(caller_globals, **caller_locals) # type: ignore 76 | local_namespace = {} 77 | exec(textwrap.dedent(s), global_namespace, local_namespace) 78 | return local_namespace 79 | 80 | 81 | def is_coverage_running(): 82 | """Return whether coverage is currently running. 83 | """ 84 | if 'coverage' not in sys.modules: 85 | return False 86 | tracer = sys.gettrace() 87 | if tracer is None: 88 | return False 89 | try: 90 | mod = tracer.__module__ 91 | except AttributeError: 92 | try: 93 | mod = tracer.__class__.__module__ 94 | except AttributeError: 95 | return False 96 | return mod.startswith('coverage') 97 | --------------------------------------------------------------------------------