Actually update to tornado 2.2.0.
This is the v2.2.0 tag in git://github.com/facebook/tornado, ie.
commit id 02bc76155de5bf4dca243e4d0c019c0ac4c8b3be . I'm not sure
exactly which version was imported previously, but it wasn't quite this one.
The good news is it doesn't affect the tornado source code, just a bunch of
supporting files we don't really use.
* commit '3f6c64ca9fda3b2bf3f8ca3acb47b3d8cb255cbf':
Squashed 'tr/vendor/tornado/' changes from 94078a6..3dd205c
Change-Id: I3a2ec0d6bf020e6d883126d1f3d96e10eefe9c6c
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..2db6c2f
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,18 @@
+# Test coverage configuration.
+# Usage:
+# pip install coverage
+# coverage erase # clears previous data if any
+# coverage run -m tornado.test.runtests
+# coverage report # prints to stdout
+# coverage html # creates ./htmlcov/*.html including annotated source
+[run]
+branch = true
+source = tornado
+omit =
+ tornado/platform/*
+ tornado/test/*
+ */_auto2to3*
+
+[report]
+# Ignore missing source files, i.e. fake template-generated "files"
+ignore_errors = true
diff --git a/.gitignore b/.gitignore
index 85b6dd3..ba77616 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,13 @@
*.pyc
*.so
*~
-build
-dist/
-tornado.egg-info
+build/
+/dist/
+MANIFEST
+/tornado.egg-info/
_auto2to3*
+.tox/
+.vagrant
+/.coverage
+/htmlcov/
+/env/
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644
index 1a86eb2..0000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,11 +0,0 @@
-Metadata-Version: 1.0
-Name: tornado
-Version: 2.2
-Summary: Tornado is an open source version of the scalable, non-blocking web server and and tools that power FriendFeed
-Home-page: http://www.tornadoweb.org/
-Author: Facebook
-Author-email: python-tornado@googlegroups.com
-License: http://www.apache.org/licenses/LICENSE-2.0
-Download-URL: http://github.com/downloads/facebook/tornado/tornado-2.2.tar.gz
-Description: UNKNOWN
-Platform: UNKNOWN
diff --git a/maint/README b/maint/README
new file mode 100644
index 0000000..9a9122b
--- /dev/null
+++ b/maint/README
@@ -0,0 +1,3 @@
+This directory contains tools and scripts that are used in the development
+and maintainance of Tornado itself, but are probably not of interest to
+Tornado users.
diff --git a/maint/appengine/README b/maint/appengine/README
new file mode 100644
index 0000000..8d534f2
--- /dev/null
+++ b/maint/appengine/README
@@ -0,0 +1,8 @@
+Unit test support for app engine. Currently very limited as most of
+our tests depend on direct network access, but these tests ensure that the
+modules that are supposed to work on app engine don't depend on any
+forbidden modules.
+
+The code lives in maint/appengine/common, but should be run from the py25
+or py27 subdirectories (which contain an app.yaml and a bunch of symlinks).
+runtests.py is the entry point; cgi_runtests.py is used internally.
diff --git a/maint/appengine/common/cgi_runtests.py b/maint/appengine/common/cgi_runtests.py
new file mode 100644
index 0000000..a3f1596
--- /dev/null
+++ b/maint/appengine/common/cgi_runtests.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+import sys
+import unittest
+
+# Most of our tests depend on IOLoop, which is not importable on app engine.
+# Run the tests that work, and check that forbidden imports don't sneak
+# in to modules that are supposed to work on app engine.
+TEST_MODULES = [
+ 'tornado.httputil.doctests',
+ #'tornado.iostream.doctests',
+ 'tornado.util.doctests',
+ #'tornado.test.auth_test',
+ #'tornado.test.curl_httpclient_test',
+ 'tornado.test.escape_test',
+ #'tornado.test.gen_test',
+ #'tornado.test.httpclient_test',
+ #'tornado.test.httpserver_test',
+ 'tornado.test.httputil_test',
+ #'tornado.test.import_test',
+ #'tornado.test.ioloop_test',
+ #'tornado.test.iostream_test',
+ #'tornado.test.process_test',
+ #'tornado.test.simple_httpclient_test',
+ #'tornado.test.stack_context_test',
+ 'tornado.test.template_test',
+ #'tornado.test.testing_test',
+ #'tornado.test.twisted_test',
+ #'tornado.test.web_test',
+ #'tornado.test.wsgi_test',
+]
+
+def import_everything():
+ # import tornado.auth
+ # import tornado.autoreload
+ # import tornado.curl_httpclient # depends on pycurl
+ # import tornado.database # depends on MySQLdb
+ import tornado.escape
+ # import tornado.httpclient
+ # import tornado.httpserver
+ import tornado.httputil
+ # import tornado.ioloop
+ # import tornado.iostream
+ import tornado.locale
+ import tornado.options
+ # import tornado.netutil
+ # import tornado.platform.twisted # depends on twisted
+ # import tornado.process
+ # import tornado.simple_httpclient
+ import tornado.stack_context
+ import tornado.template
+ import tornado.testing
+ import tornado.util
+ import tornado.web
+ # import tornado.websocket
+ import tornado.wsgi
+
+def all():
+ return unittest.defaultTestLoader.loadTestsFromNames(TEST_MODULES)
+
+def main():
+ print "Content-Type: text/plain\r\n\r\n",
+
+ import_everything()
+
+ try:
+ unittest.main(defaultTest="all", argv=sys.argv)
+ except SystemExit, e:
+ if e.code == 0:
+ print "PASS"
+ else:
+ raise
+
+if __name__ == '__main__':
+ main()
+
diff --git a/maint/appengine/common/runtests.py b/maint/appengine/common/runtests.py
new file mode 100644
index 0000000..2db8d1a
--- /dev/null
+++ b/maint/appengine/common/runtests.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+from __future__ import with_statement
+
+import contextlib
+import errno
+import os
+import random
+import signal
+import socket
+import subprocess
+import sys
+import time
+import urllib2
+
+if __name__ == "__main__":
+ tornado_root = os.path.abspath(os.path.join(os.path.dirname(__file__),
+ '../../..'))
+ # dev_appserver doesn't seem to set SO_REUSEADDR
+ port = random.randrange(10000, 11000)
+ # does dev_appserver.py ever live anywhere but /usr/local/bin?
+ proc = subprocess.Popen([sys.executable,
+ "/usr/local/bin/dev_appserver.py",
+ os.path.dirname(os.path.abspath(__file__)),
+ "--port=%d" % port,
+ "--skip_sdk_update_check",
+ ],
+ cwd=tornado_root)
+
+ try:
+ for i in xrange(50):
+ with contextlib.closing(socket.socket()) as sock:
+ err = sock.connect_ex(('localhost', port))
+ if err == 0:
+ break
+ elif err != errno.ECONNREFUSED:
+ raise Exception("Got unexpected socket error %d" % err)
+ time.sleep(0.1)
+ else:
+ raise Exception("Server didn't start listening")
+
+ resp = urllib2.urlopen("http://localhost:%d/" % port)
+ print resp.read()
+ finally:
+ # dev_appserver sometimes ignores SIGTERM (especially on 2.5),
+ # so try a few times to kill it.
+ for sig in [signal.SIGTERM, signal.SIGTERM, signal.SIGKILL]:
+ os.kill(proc.pid, sig)
+ res = os.waitpid(proc.pid, os.WNOHANG)
+ if res != (0,0):
+ break
+ time.sleep(0.1)
+ else:
+ os.waitpid(proc.pid, 0)
diff --git a/maint/appengine/py25/app.yaml b/maint/appengine/py25/app.yaml
new file mode 100644
index 0000000..8bd7892
--- /dev/null
+++ b/maint/appengine/py25/app.yaml
@@ -0,0 +1,8 @@
+application: tornado-tests-appengine25
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /
+ script: cgi_runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py25/cgi_runtests.py b/maint/appengine/py25/cgi_runtests.py
new file mode 120000
index 0000000..a9fc90e
--- /dev/null
+++ b/maint/appengine/py25/cgi_runtests.py
@@ -0,0 +1 @@
+../common/cgi_runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py25/runtests.py b/maint/appengine/py25/runtests.py
new file mode 120000
index 0000000..2cce26b
--- /dev/null
+++ b/maint/appengine/py25/runtests.py
@@ -0,0 +1 @@
+../common/runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py25/tornado b/maint/appengine/py25/tornado
new file mode 120000
index 0000000..13b2e09
--- /dev/null
+++ b/maint/appengine/py25/tornado
@@ -0,0 +1 @@
+../../../tornado
\ No newline at end of file
diff --git a/maint/appengine/py27/app.yaml b/maint/appengine/py27/app.yaml
new file mode 100644
index 0000000..e5dea07
--- /dev/null
+++ b/maint/appengine/py27/app.yaml
@@ -0,0 +1,9 @@
+application: tornado-tests-appengine27
+version: 1
+runtime: python27
+threadsafe: false
+api_version: 1
+
+handlers:
+- url: /
+ script: cgi_runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py27/cgi_runtests.py b/maint/appengine/py27/cgi_runtests.py
new file mode 120000
index 0000000..a9fc90e
--- /dev/null
+++ b/maint/appengine/py27/cgi_runtests.py
@@ -0,0 +1 @@
+../common/cgi_runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py27/runtests.py b/maint/appengine/py27/runtests.py
new file mode 120000
index 0000000..2cce26b
--- /dev/null
+++ b/maint/appengine/py27/runtests.py
@@ -0,0 +1 @@
+../common/runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py27/tornado b/maint/appengine/py27/tornado
new file mode 120000
index 0000000..13b2e09
--- /dev/null
+++ b/maint/appengine/py27/tornado
@@ -0,0 +1 @@
+../../../tornado
\ No newline at end of file
diff --git a/maint/appengine/setup.py b/maint/appengine/setup.py
new file mode 100644
index 0000000..5d2d314
--- /dev/null
+++ b/maint/appengine/setup.py
@@ -0,0 +1,4 @@
+# Dummy setup file to make tox happy. In the appengine world things aren't
+# installed through setup.py
+import distutils.core
+distutils.core.setup()
diff --git a/maint/appengine/tox.ini b/maint/appengine/tox.ini
new file mode 100644
index 0000000..0970bf8
--- /dev/null
+++ b/maint/appengine/tox.ini
@@ -0,0 +1,19 @@
+# App Engine tests require the SDK to be installed separately.
+# Version 1.6.1 or newer is required (older versions don't work when
+# python is run from a virtualenv)
+#
+# These are currently excluded from the main tox.ini because their
+# logs are spammy and they're a little flaky.
+[tox]
+envlist = py25-appengine, py27-appengine
+
+[testenv]
+changedir = {toxworkdir}
+
+[testenv:py25-appengine]
+basepython = python2.5
+commands = python {toxinidir}/py25/runtests.py {posargs:}
+
+[testenv:py27-appengine]
+basepython = python2.7
+commands = python {toxinidir}/py27/runtests.py {posargs:}
diff --git a/maint/requirements.txt b/maint/requirements.txt
new file mode 100644
index 0000000..30e0d32
--- /dev/null
+++ b/maint/requirements.txt
@@ -0,0 +1,21 @@
+# Frozen pip requirements for tools used in the development of tornado
+
+# Tornado's optional dependencies
+MySQL-python==1.2.3
+Twisted==11.1.0
+pycurl==7.19.0
+
+# Other useful tools
+Sphinx==1.1.2
+coverage==3.5.1
+pyflakes==0.5.0
+tox==1.3
+virtualenv==1.7
+
+# Indirect dependencies
+Jinja2==2.6
+Pygments==1.4
+docutils==0.8.1
+py==1.4.6
+wsgiref==0.1.2
+zope.interface==3.8.0
diff --git a/maint/test/README b/maint/test/README
new file mode 100644
index 0000000..e4e6cd9
--- /dev/null
+++ b/maint/test/README
@@ -0,0 +1,3 @@
+This directory contains additional tests that are not included in the main
+suite (because e.g. they have extra dependencies, run slowly, or produce
+more output than a simple pass/fail)
diff --git a/maint/test/websocket/.gitignore b/maint/test/websocket/.gitignore
new file mode 100644
index 0000000..a9a1bd3
--- /dev/null
+++ b/maint/test/websocket/.gitignore
@@ -0,0 +1 @@
+reports/
diff --git a/maint/test/websocket/client.py b/maint/test/websocket/client.py
new file mode 100644
index 0000000..1679887
--- /dev/null
+++ b/maint/test/websocket/client.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+import sys
+from tornado.options import options, define, parse_command_line
+from twisted.python import log
+from twisted.internet import reactor
+from autobahn.fuzzing import FuzzingClientFactory
+
+define('servers', type=str, multiple=True,
+ default=['Tornado=ws://localhost:9000'])
+
+define('cases', type=str, multiple=True,
+ default=["*"])
+define('exclude', type=str, multiple=True,
+ default=["9.*"])
+
+if __name__ == '__main__':
+ parse_command_line()
+ log.startLogging(sys.stdout)
+ servers = []
+ for server in options.servers:
+ name, _, url = server.partition('=')
+ servers.append({"agent": name, "url": url, "options": {"version": 17}})
+ spec = {
+ "options": {"failByDrop": False},
+ "enable-ssl": False,
+ "servers": servers,
+ "cases": options.cases,
+ "exclude-cases": options.exclude,
+ "exclude-agent-cases": {},
+ }
+ fuzzer = FuzzingClientFactory(spec)
+ reactor.run()
diff --git a/maint/test/websocket/run.sh b/maint/test/websocket/run.sh
new file mode 100755
index 0000000..9478687
--- /dev/null
+++ b/maint/test/websocket/run.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Runs the autobahn websocket conformance test against tornado in both
+# python2 and python3. Output goes in ./reports/servers/index.html.
+#
+# The --cases and --exclude arguments can be used to run only part of
+# the suite. The default is --exclude="9.*" to skip the relatively slow
+# performance tests; pass --exclude="" to override and include them.
+
+set -e
+
+# build/update the virtualenvs
+tox
+
+.tox/py25/bin/python server.py --port=9001 &
+PY25_SERVER_PID=$!
+
+.tox/py27/bin/python server.py --port=9002 &
+PY27_SERVER_PID=$!
+
+.tox/py32/bin/python server.py --port=9003 &
+PY32_SERVER_PID=$!
+
+.tox/pypy/bin/python server.py --port=9004 &
+PYPY_SERVER_PID=$!
+
+sleep 1
+
+.tox/py27/bin/python ./client.py --servers=Tornado/py25=ws://localhost:9001,Tornado/py27=ws://localhost:9002,Tornado/py32=ws://localhost:9003,Tornado/pypy=ws://localhost:9004 "$@" || true
+
+kill $PY25_SERVER_PID
+kill $PY27_SERVER_PID
+kill $PY32_SERVER_PID
+kill $PYPY_SERVER_PID
+wait
+
+echo "Tests complete. Output is in ./reports/servers/index.html"
\ No newline at end of file
diff --git a/maint/test/websocket/server.py b/maint/test/websocket/server.py
new file mode 100644
index 0000000..b44056c
--- /dev/null
+++ b/maint/test/websocket/server.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+from tornado.ioloop import IOLoop
+from tornado.options import define, options, parse_command_line
+from tornado.util import bytes_type
+from tornado.websocket import WebSocketHandler
+from tornado.web import Application
+
+define('port', default=9000)
+
+class EchoHandler(WebSocketHandler):
+ def on_message(self, message):
+ self.write_message(message, binary=isinstance(message, bytes_type))
+
+if __name__ == '__main__':
+ parse_command_line()
+ app = Application([
+ ('/', EchoHandler),
+ ])
+ app.listen(options.port, address='127.0.0.1')
+ IOLoop.instance().start()
diff --git a/maint/test/websocket/tox.ini b/maint/test/websocket/tox.ini
new file mode 100644
index 0000000..0709749
--- /dev/null
+++ b/maint/test/websocket/tox.ini
@@ -0,0 +1,12 @@
+# We don't actually use tox to run this test, but it's the easiest way
+# to install autobahn and deal with 2to3 for the python3 version.
+# See run.sh for the real test runner.
+[tox]
+envlist = py27, py32, py25, pypy
+setupdir=../../..
+
+[testenv]
+commands = python -c pass
+
+[testenv:py27]
+deps = autobahn
diff --git a/maint/vm/README b/maint/vm/README
new file mode 100644
index 0000000..7660588
--- /dev/null
+++ b/maint/vm/README
@@ -0,0 +1,23 @@
+This directory contains virtual machine setup scripts for testing Tornado.
+
+Requirements:
+
+Vagrant (http://vagrantup.com) and VirtualBox (http://virtualbox.org).
+Vagrant provides an easy download for Ubuntu 10.04 (aka lucid64); base
+images for other platforms are harder to find and can be built with
+VeeWee (https://github.com/jedi4ever/veewee).
+
+Usage:
+
+cd to the appropriate directory and run `vagrant up`, then `vagrant ssh`.
+From there, simply run `tox` to run the full test suite, or cd to /tornado
+and test manually. Afterwards, use `vagrant suspend` or `vagrant destroy`
+to clean up.
+
+Notes:
+
+Python distutils (and therefore tox) assume that if the platform
+supports hard links, they can be used in the Tornado source directory.
+VirtualBox's shared folder filesystem does not support hard links (or
+symlinks), so we have to use NFS shared folders instead. (which has
+the unfortunate side effect of requiring sudo on the host machine)
diff --git a/maint/vm/freebsd/Vagrantfile b/maint/vm/freebsd/Vagrantfile
new file mode 100644
index 0000000..1672492
--- /dev/null
+++ b/maint/vm/freebsd/Vagrantfile
@@ -0,0 +1,27 @@
+require 'vagrant/systems/freebsd'
+
+Vagrant::Config.run do |config|
+ # A freebsd image can be created with veewee
+ # https://github.com/jedi4ever/veewee
+ #
+ # vagrant basebox define freebsd freebsd-8.2-pcbsd-i386-netboot
+ # vagrant basebox build freebsd
+ # vagrant basebox export freebsd
+ # vagrant box add freebsd freebsd.box
+ config.vm.box = "freebsd"
+
+ config.vm.system = :freebsd
+
+ # Note that virtualbox shared folders don't work with freebsd, so
+ # we'd need nfs shared folders here even if virtualbox gains
+ # support for symlinks.
+ config.vm.network "172.19.1.3"
+ config.vm.share_folder("tornado", "/tornado", "../../..", :nfs => true)
+
+ # This doesn't seem to get mounted by default for freebsd,
+ # but that's actually a good thing since there are apparently issues
+ # when one nfs export is a subfolder of another.
+ #config.vm.share_folder("v-root", "/vagrant", ".", :nfs => true)
+
+ config.vm.provision :shell, :path => "setup.sh"
+end
\ No newline at end of file
diff --git a/maint/vm/freebsd/setup.sh b/maint/vm/freebsd/setup.sh
new file mode 100644
index 0000000..25003c2
--- /dev/null
+++ b/maint/vm/freebsd/setup.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+chsh -s bash vagrant
+
+# This doesn't get created automatically for freebsd since virtualbox
+# shared folders don't work.
+ln -snf /tornado/maint/vm/freebsd /vagrant
+
+PORTS="
+lang/python27
+devel/py-pip
+devel/py-virtualenv
+ftp/curl
+"
+
+PIP_PACKAGES="
+tox
+pycurl
+"
+
+cd /usr/ports
+
+for port in $PORTS; do
+ make -C $port -DBATCH install
+done
+
+pip install $PIP_PACKAGES
+
+/tornado/maint/vm/shared-setup.sh
+
diff --git a/maint/vm/freebsd/tox.ini b/maint/vm/freebsd/tox.ini
new file mode 100644
index 0000000..ef1c4bc
--- /dev/null
+++ b/maint/vm/freebsd/tox.ini
@@ -0,0 +1,13 @@
+[tox]
+envlist=py27-full, py27
+setupdir=/tornado
+# /home is a symlink to /usr/home, but tox doesn't like symlinks here
+toxworkdir=/usr/home/vagrant/tox-tornado
+
+[testenv]
+commands = python -m tornado.test.runtests {posargs:}
+
+[testenv:py27-full]
+# other dependencies aren't really worth the install time
+deps =
+ pycurl
diff --git a/maint/vm/shared-setup.sh b/maint/vm/shared-setup.sh
new file mode 100755
index 0000000..4493ad1
--- /dev/null
+++ b/maint/vm/shared-setup.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+# Run at the end of each vm's provisioning script
+
+set -e
+
+# Link tox.ini into the home directory so you can run tox immediately
+# after ssh'ing in without cd'ing to /vagrant (since cd'ing to /tornado
+# gets the wrong config)
+ln -sf /vagrant/tox.ini ~vagrant/tox.ini
diff --git a/maint/vm/ubuntu10.04/Vagrantfile b/maint/vm/ubuntu10.04/Vagrantfile
new file mode 100644
index 0000000..63520cb
--- /dev/null
+++ b/maint/vm/ubuntu10.04/Vagrantfile
@@ -0,0 +1,9 @@
+Vagrant::Config.run do |config|
+ config.vm.box = "lucid64"
+ config.vm.box_url = "http://files.vagrantup.com/lucid64.box"
+
+ config.vm.network "172.19.1.2"
+ config.vm.share_folder("tornado", "/tornado", "../../..", :nfs=> true)
+
+ config.vm.provision :shell, :path => "setup.sh"
+end
\ No newline at end of file
diff --git a/maint/vm/ubuntu10.04/setup.sh b/maint/vm/ubuntu10.04/setup.sh
new file mode 100644
index 0000000..d5db904
--- /dev/null
+++ b/maint/vm/ubuntu10.04/setup.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+set -e
+
+apt-get update
+
+# libcurl4-gnutls-dev is the default if you ask for libcurl4-dev, but it
+# has bugs that make our tests deadlock (the relevant tests detect this and
+# disable themselves, but it means that to get full coverage we have to use
+# the openssl version).
+# The oddly-named python-software-properties includes add-apt-repository.
+APT_PACKAGES="
+python-pip
+python-dev
+libmysqlclient-dev
+libcurl4-openssl-dev
+python-software-properties
+"
+
+apt-get -y install $APT_PACKAGES
+
+
+# Ubuntu 10.04 has python 2.6 as default; install more from here.
+# The most important thing is to have both 2.5 and a later version so we
+# test with both tornado.epoll and 2.6+ stdlib's select.epoll.
+add-apt-repository ppa:fkrull/deadsnakes
+apt-get update
+
+DEADSNAKES_PACKAGES="
+python2.5
+python2.5-dev
+python2.7
+python2.7-dev
+python3.2
+python3.2-dev
+"
+apt-get -y install $DEADSNAKES_PACKAGES
+
+
+PIP_PACKAGES="
+virtualenv
+tox
+MySQL-python
+pycurl
+twisted
+"
+
+pip install $PIP_PACKAGES
+
+/tornado/maint/vm/shared-setup.sh
diff --git a/maint/vm/ubuntu10.04/tox.ini b/maint/vm/ubuntu10.04/tox.ini
new file mode 100644
index 0000000..87841ac
--- /dev/null
+++ b/maint/vm/ubuntu10.04/tox.ini
@@ -0,0 +1,32 @@
+[tox]
+envlist = py27-full, py25-full, py32, py25, py26, py26-full, py27
+setupdir=/tornado
+toxworkdir=/home/vagrant/tox-tornado
+
+[testenv]
+commands = python -m tornado.test.runtests {posargs:}
+
+[testenv:py25]
+basepython = python2.5
+deps = simplejson
+
+[testenv:py25-full]
+basepython = python2.5
+deps =
+ MySQL-python
+ pycurl
+ simplejson
+ twisted==11.0.0
+
+[testenv:py26-full]
+deps =
+ MySQL-python
+ pycurl
+ twisted==11.0.0
+
+[testenv:py27-full]
+basepython = python2.7
+deps =
+ MySQL-python
+ pycurl
+ twisted==11.0.0
diff --git a/maint/vm/ubuntu11.04/Vagrantfile b/maint/vm/ubuntu11.04/Vagrantfile
new file mode 100644
index 0000000..00a1938
--- /dev/null
+++ b/maint/vm/ubuntu11.04/Vagrantfile
@@ -0,0 +1,8 @@
+Vagrant::Config.run do |config|
+ config.vm.box = "ubuntu11.04"
+
+ config.vm.network "172.19.1.4"
+ config.vm.share_folder("tornado", "/tornado", "../../..", :nfs=> true)
+
+ config.vm.provision :shell, :path => "setup.sh"
+end
\ No newline at end of file
diff --git a/maint/vm/ubuntu11.04/setup.sh b/maint/vm/ubuntu11.04/setup.sh
new file mode 100644
index 0000000..d5a30f6
--- /dev/null
+++ b/maint/vm/ubuntu11.04/setup.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+set -e
+
+# Ubuntu 10.10+ do some extra permissions checks for hard links.
+# Vagrant's nfs shared folders come through with funny uids, but
+# attempts to access them still work despite the visible permissions
+# being incorrect.
+sysctl -w kernel.yama.protected_nonaccess_hardlinks=0
+
+apt-get update
+
+# libcurl4-gnutls-dev is the default if you ask for libcurl4-dev, but it
+# has bugs that make our tests deadlock (the relevant tests detect this and
+# disable themselves, but it means that to get full coverage we have to use
+# the openssl version).
+# The oddly-named python-software-properties includes add-apt-repository.
+APT_PACKAGES="
+python-pip
+python-dev
+libmysqlclient-dev
+libcurl4-openssl-dev
+python-software-properties
+"
+
+apt-get -y install $APT_PACKAGES
+
+
+# Ubuntu 11.04 has python 2.7 as default; install more from here.
+# The most important thing is to have both 2.5 and a later version so we
+# test with both tornado.epoll and 2.6+ stdlib's select.epoll.
+add-apt-repository ppa:fkrull/deadsnakes
+apt-get update
+
+DEADSNAKES_PACKAGES="
+python2.5
+python2.5-dev
+python2.6
+python2.6-dev
+python3.2
+python3.2-dev
+"
+apt-get -y install $DEADSNAKES_PACKAGES
+
+
+PIP_PACKAGES="
+virtualenv
+tox
+MySQL-python
+pycurl
+twisted
+"
+
+pip install $PIP_PACKAGES
+
+/tornado/maint/vm/shared-setup.sh
diff --git a/maint/vm/ubuntu11.04/tox.ini b/maint/vm/ubuntu11.04/tox.ini
new file mode 100644
index 0000000..87841ac
--- /dev/null
+++ b/maint/vm/ubuntu11.04/tox.ini
@@ -0,0 +1,32 @@
+[tox]
+envlist = py27-full, py25-full, py32, py25, py26, py26-full, py27
+setupdir=/tornado
+toxworkdir=/home/vagrant/tox-tornado
+
+[testenv]
+commands = python -m tornado.test.runtests {posargs:}
+
+[testenv:py25]
+basepython = python2.5
+deps = simplejson
+
+[testenv:py25-full]
+basepython = python2.5
+deps =
+ MySQL-python
+ pycurl
+ simplejson
+ twisted==11.0.0
+
+[testenv:py26-full]
+deps =
+ MySQL-python
+ pycurl
+ twisted==11.0.0
+
+[testenv:py27-full]
+basepython = python2.7
+deps =
+ MySQL-python
+ pycurl
+ twisted==11.0.0
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 861a9f5..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[egg_info]
-tag_build =
-tag_date = 0
-tag_svn_revision = 0
-
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..88c8c37
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,72 @@
+# Tox (http://codespeak.net/~hpk/tox/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the tornado
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+#
+# See also tornado/test/run_pyversion_tests.py, which is faster but
+# less thorough.
+#
+# On my macports-based setup, the environment variable
+# ARCHFLAGS='-arch x86_64' must be set when building pycurl, and a
+# symlink from mysql_config to mysql_config5 must exist when building
+# MySQL-python.
+[tox]
+# "-full" variants include optional dependencies, to ensure
+# that things work both in a bare install and with all the extras.
+envlist = py27-full, py27-curl, py25-full, py32, pypy, py25, py26, py26-full, py27
+[testenv]
+commands = python -m tornado.test.runtests {posargs:}
+
+# python will import relative to the current working directory by default,
+# so cd into the tox working directory to avoid picking up the working
+# copy of the files (especially important for 2to3).
+changedir = {toxworkdir}
+# Note that PYTHONPATH must not be set when running tox (and setting it
+# with the following doesn't seem to work, since tox/virtualenv appends to
+# PYTHONPATH)
+#environment = PYTHONPATH=
+
+[testenv:py25]
+basepython = python2.5
+deps = simplejson
+
+[testenv:py25-full]
+basepython = python2.5
+deps =
+ MySQL-python
+ pycurl
+ simplejson
+ twisted>=11.1.0
+
+# py26-full deliberately runs an older version of twisted to ensure
+# we're still compatible with the oldest version we support.
+[testenv:py26-full]
+basepython = python2.6
+deps =
+ MySQL-python
+ pycurl
+ twisted==11.0.0
+
+[testenv:py27-full]
+basepython = python2.7
+deps =
+ MySQL-python
+ pycurl
+ twisted>=11.1.0
+
+[testenv:py27-curl]
+# Same as py27-full, but runs the tests with curl_httpclient by default.
+# Note that httpclient_test is always run with both client implementations;
+# this flag controls which client all the other tests use.
+basepython = python2.7
+deps =
+ MySQL-python
+ pycurl
+ twisted>=11.0.0
+commands = python -m tornado.test.runtests --httpclient=tornado.curl_httpclient.CurlAsyncHTTPClient {posargs:}
+
+# No pypy-full yet: pycurl doesn't build with pypy, and installing
+# twisted under pypy takes a *very* long time. MySQL-python builds with
+# pypy, but doesn't work.
+
+# No py32-full yet: none of our dependencies currently work on python3.
diff --git a/website/Makefile b/website/Makefile
index 6357830..6032d14 100644
--- a/website/Makefile
+++ b/website/Makefile
@@ -7,6 +7,7 @@
.PHONY: coverage
coverage:
sphinx-build -b coverage ${SPHINXOPTS} sphinx/build/coverage
+ cat sphinx/build/coverage/python.txt
clean:
rm -rf sphinx/build
\ No newline at end of file
diff --git a/website/app.yaml b/website/app.yaml
index 9484a91..92e2fbf 100644
--- a/website/app.yaml
+++ b/website/app.yaml
@@ -1,14 +1,15 @@
application: python-tornado
version: 2
-runtime: python
+runtime: python27
+threadsafe: yes
api_version: 1
handlers:
- url: /static/tornado-0.1.tar.gz
- script: website.py
+ script: website.application
- url: /static/tornado-0.2.tar.gz
- script: website.py
+ script: website.application
- url: /static/
static_dir: static
@@ -22,10 +23,10 @@
upload: static/favicon.ico
- url: /documentation/?
- script: website.py
+ script: website.application
- url: /documentation
static_dir: sphinx/build/html
- url: /.*
- script: website.py
+ script: website.application
diff --git a/website/sphinx/conf.py b/website/sphinx/conf.py
index 1b89d28..84f833e 100644
--- a/website/sphinx/conf.py
+++ b/website/sphinx/conf.py
@@ -4,10 +4,6 @@
sys.path.insert(0, os.path.abspath("../.."))
import tornado
-# For our version of sphinx_coverage.py. The version in sphinx 1.0.7
-# has too many false positives; this version comes from upstream HG.
-sys.path.append(os.path.abspath("."))
-
master_doc = "index"
project = "Tornado"
@@ -15,7 +11,7 @@
version = release = tornado.version
-extensions = ["sphinx.ext.autodoc", "sphinx_coverage", "sphinx.ext.viewcode"]
+extensions = ["sphinx.ext.autodoc", "sphinx.ext.coverage", "sphinx.ext.viewcode"]
primary_domain = 'py'
default_role = 'py:obj'
@@ -24,14 +20,27 @@
autoclass_content = "both"
coverage_skip_undoc_in_source = True
+coverage_ignore_modules = [
+ "tornado.platform.twisted",
+ ]
# I wish this could go in a per-module file...
coverage_ignore_classes = [
+ # tornado.gen
+ "Multi",
+ "Runner",
+ "YieldPoint",
+
# tornado.web
"ChunkedTransferEncoding",
"GZipContentEncoding",
"OutputTransform",
"TemplateModule",
"url",
+
+ # tornado.websocket
+ "WebSocketProtocol",
+ "WebSocketProtocol13",
+ "WebSocketProtocol76",
]
coverage_ignore_functions = [
diff --git a/website/sphinx/gen.rst b/website/sphinx/gen.rst
new file mode 100644
index 0000000..4281eb3
--- /dev/null
+++ b/website/sphinx/gen.rst
@@ -0,0 +1,28 @@
+``tornado.gen`` --- Simplify asynchronous code
+==============================================
+
+.. automodule:: tornado.gen
+
+ Decorator
+ ---------
+
+ .. autofunction:: engine
+
+ Yield points
+ ------------
+
+ Instances of the following classes may be used in yield expressions
+ in the generator.
+
+ .. autoclass:: Task
+
+ .. autoclass:: Callback
+
+ .. autoclass:: Wait
+
+ .. autoclass:: WaitAll
+
+ Other classes
+ -------------
+
+ .. autoclass:: Arguments
diff --git a/website/sphinx/integration.rst b/website/sphinx/integration.rst
index bf0f131..1df2fa0 100644
--- a/website/sphinx/integration.rst
+++ b/website/sphinx/integration.rst
@@ -5,5 +5,6 @@
auth
database
+ twisted
websocket
wsgi
diff --git a/website/sphinx/ioloop.rst b/website/sphinx/ioloop.rst
index 1ff1723..3e6e8ed 100644
--- a/website/sphinx/ioloop.rst
+++ b/website/sphinx/ioloop.rst
@@ -13,9 +13,11 @@
.. automethod:: IOLoop.instance
.. automethod:: IOLoop.initialized
+ .. automethod:: IOLoop.install
.. automethod:: IOLoop.start
- .. automethod:: IOLoop.stop
.. automethod:: IOLoop.running
+ .. automethod:: IOLoop.stop
+ .. automethod:: IOLoop.close
I/O events
^^^^^^^^^^
diff --git a/website/sphinx/netutil.rst b/website/sphinx/netutil.rst
new file mode 100644
index 0000000..f7a0e0d
--- /dev/null
+++ b/website/sphinx/netutil.rst
@@ -0,0 +1,5 @@
+``tornado.netutil`` --- Miscellaneous network utilities
+=======================================================
+
+.. automodule:: tornado.netutil
+ :members:
diff --git a/website/sphinx/networking.rst b/website/sphinx/networking.rst
index e77622d..f71de71 100644
--- a/website/sphinx/networking.rst
+++ b/website/sphinx/networking.rst
@@ -6,3 +6,4 @@
ioloop
iostream
httpclient
+ netutil
diff --git a/website/sphinx/overview.rst b/website/sphinx/overview.rst
index c33e499..29c88ee 100644
--- a/website/sphinx/overview.rst
+++ b/website/sphinx/overview.rst
@@ -1,3 +1,5 @@
+.. currentmodule:: tornado.web
+
Overview
========
@@ -138,6 +140,9 @@
4. One of the HTTP methods is called: ``get()``, ``post()``, ``put()``,
etc. If the URL regular expression contains capturing groups, they
are passed as arguments to this method.
+5. When the request is finished, ``on_finish()`` is called. For synchronous
+ handlers this is immediately after ``get()`` (etc) return; for
+ asynchronous handlers it is after the call to ``finish()``.
Here is an example demonstrating the ``initialize()`` method:
@@ -156,8 +161,8 @@
Other methods designed for overriding include:
-- ``get_error_html(self, status_code, exception=None, **kwargs)`` -
- returns HTML (as a string) for use on error pages.
+- ``write_error(self, status_code, exc_info=None, **kwargs)`` -
+ outputs HTML for use on error pages.
- ``get_current_user(self)`` - see `User
Authentication <#user-authentication>`_ below
- ``get_user_locale(self)`` - returns ``locale`` object to use for the
@@ -166,6 +171,40 @@
``@authenticated`` decorator (default is in ``Application`` settings)
- ``get_template_path(self)`` - returns location of template files
(default is in ``Application`` settings)
+- ``set_default_headers(self)`` - may be used to set additional headers
+ on the response (such as a custom ``Server`` header)
+
+Error Handling
+~~~~~~~~~~~~~~
+
+There are three ways to return an error from a `RequestHandler`:
+
+1. Manually call `~tornado.web.RequestHandler.set_status` and output the
+ response body normally.
+2. Call `~RequestHandler.send_error`. This discards
+ any pending unflushed output and calls `~RequestHandler.write_error` to
+ generate an error page.
+3. Raise an exception. `tornado.web.HTTPError` can be used to generate
+ a specified status code; all other exceptions return a 500 status.
+ The exception handler uses `~RequestHandler.send_error` and
+ `~RequestHandler.write_error` to generate the error page.
+
+The default error page includes a stack trace in debug mode and a one-line
+description of the error (e.g. "500: Internal Server Error") otherwise.
+To produce a custom error page, override `RequestHandler.write_error`.
+This method may produce output normally via methods such as
+`~RequestHandler.write` and `~RequestHandler.render`. If the error was
+caused by an exception, an ``exc_info`` triple will be passed as a keyword
+argument (note that this exception is not guaranteed to be the current
+exception in ``sys.exc_info``, so ``write_error`` must use e.g.
+`traceback.format_exception` instead of `traceback.format_exc`).
+
+In Tornado 2.0 and earlier, custom error pages were implemented by overriding
+``RequestHandler.get_error_html``, which returned the error page as a string
+instead of calling the normal output methods (and had slightly different
+semantics for exceptions). This method is still supported, but it is
+deprecated and applications are encouraged to switch to
+`RequestHandler.write_error`.
Redirection
~~~~~~~~~~~
@@ -317,6 +356,14 @@
these places the name of an alternative escaping function may be used
instead of ``None``.
+Note that while Tornado's automatic escaping is helpful in avoiding
+XSS vulnerabilities, it is not sufficient in all cases. Expressions
+that appear in certain locations, such as in Javascript or CSS, may need
+additional escaping. Additionally, either care must be taken to always
+use double quotes and ``xhtml_escape`` in HTML attributes that may contain
+untrusted content, or a separate escaping function must be used for
+attributes (see e.g. http://wonko.com/post/html-escaping)
+
Cookies and secure cookies
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1059,13 +1106,3 @@
<https://github.com/facebook/tornado/tree/master/demos/appengine>`_ for a
full-featured AppEngine app built on Tornado.
-Caveats and support
-~~~~~~~~~~~~~~~~~~~
-
-Because FriendFeed and other large users of Tornado run `behind
-nginx <#running-tornado-in-production>`_ or Apache proxies, Tornado's
-HTTP server currently does not attempt to handle multi-line headers and
-some types of malformed input.
-
-You can discuss Tornado and report bugs on `the Tornado developer
-mailing list <http://groups.google.com/group/python-tornado>`_.
diff --git a/website/sphinx/process.rst b/website/sphinx/process.rst
new file mode 100644
index 0000000..c9ce63b
--- /dev/null
+++ b/website/sphinx/process.rst
@@ -0,0 +1,5 @@
+``tornado.process`` --- Utilities for multiple processes
+========================================================
+
+.. automodule:: tornado.process
+ :members:
diff --git a/website/sphinx/releases.rst b/website/sphinx/releases.rst
index 69606af..05af64f 100644
--- a/website/sphinx/releases.rst
+++ b/website/sphinx/releases.rst
@@ -4,6 +4,9 @@
.. toctree::
:maxdepth: 2
+ releases/v2.2.0
+ releases/v2.1.1
+ releases/v2.1.0
releases/v2.0.0
releases/v1.2.1
releases/v1.2.0
diff --git a/website/sphinx/releases/v2.0.0.rst b/website/sphinx/releases/v2.0.0.rst
index f53432b..bc8aa3e 100644
--- a/website/sphinx/releases/v2.0.0.rst
+++ b/website/sphinx/releases/v2.0.0.rst
@@ -17,6 +17,8 @@
a previous release of Tornado must either disable autoescaping or adapt
their templates to work with it. For most applications, the simplest
way to do this is to pass autoescape=None to the Application constructor.
+ Note that this affects certain built-in methods, e.g. xsrf_form_html
+ and linkify, which must now be called with {% raw %} instead of {}
* Applications that wish to continue using curl_httpclient instead of
simple_httpclient may do so by calling
AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
diff --git a/website/sphinx/releases/v2.1.0.rst b/website/sphinx/releases/v2.1.0.rst
new file mode 100644
index 0000000..2e1d190
--- /dev/null
+++ b/website/sphinx/releases/v2.1.0.rst
@@ -0,0 +1,164 @@
+What's new in Tornado 2.1
+=========================
+
+Sep 20, 2011
+------------
+
+Backwards-incompatible changes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* Support for secure cookies written by pre-1.0 releases of Tornado has
+ been removed. The `RequestHandler.get_secure_cookie` method no longer
+ takes an ``include_name`` parameter.
+* The ``debug`` application setting now causes stack traces to be displayed
+ in the browser on uncaught exceptions. Since this may leak sensitive
+ information, debug mode is not recommended for public-facing servers.
+
+Security fixes
+~~~~~~~~~~~~~~
+
+* Diginotar has been removed from the default CA certificates file used
+ by `SimpleAsyncHTTPClient`.
+
+New modules
+~~~~~~~~~~~
+
+* `tornado.gen`: A generator-based interface to simplify writing
+ asynchronous functions.
+* `tornado.netutil`: Parts of `tornado.httpserver` have been extracted into
+ a new module for use with non-HTTP protocols.
+* `tornado.platform.twisted`: A bridge between the Tornado IOLoop and the
+ Twisted Reactor, allowing code written for Twisted to be run on Tornado.
+* `tornado.process`: Multi-process mode has been improved, and can now restart
+ crashed child processes. A new entry point has been added at
+ `tornado.process.fork_processes`, although
+ `tornado.httpserver.HTTPServer.start` is still supported.
+
+``tornado.web``
+~~~~~~~~~~~~~~~
+
+* `tornado.web.RequestHandler.write_error` replaces ``get_error_html`` as the
+ preferred way to generate custom error pages (``get_error_html`` is still
+ supported, but deprecated)
+* In `tornado.web.Application`, handlers may be specified by
+ (fully-qualified) name instead of importing and passing the class object
+ itself.
+* It is now possible to use a custom subclass of ``StaticFileHandler``
+ with the ``static_handler_class`` application setting, and this subclass
+ can override the behavior of the ``static_url`` method.
+* `~tornado.web.StaticFileHandler` subclasses can now override
+ ``get_cache_time`` to customize cache control behavior.
+* `tornado.web.RequestHandler.get_secure_cookie` now has a ``max_age_days``
+ parameter to allow applications to override the default one-month expiration.
+* `~tornado.web.RequestHandler.set_cookie` now accepts a ``max_age`` keyword
+ argument to set the ``max-age`` cookie attribute (note underscore vs dash)
+* `tornado.web.RequestHandler.set_default_headers` may be overridden to set
+ headers in a way that does not get reset during error handling.
+* `RequestHandler.add_header` can now be used to set a header that can
+ appear multiple times in the response.
+* `RequestHandler.flush` can now take a callback for flow control.
+* The ``application/json`` content type can now be gzipped.
+* The cookie-signing functions are now accessible as static functions
+ `tornado.web.create_signed_value` and `tornado.web.decode_signed_value`.
+
+``tornado.httpserver``
+~~~~~~~~~~~~~~~~~~~~~~
+
+* To facilitate some advanced multi-process scenarios, ``HTTPServer``
+ has a new method ``add_sockets``, and socket-opening code is
+ available separately as `tornado.netutil.bind_sockets`.
+* The ``cookies`` property is now available on `tornado.httpserver.HTTPRequest`
+ (it is also available in its old location as a property of
+ `~tornado.web.RequestHandler`)
+* `tornado.httpserver.HTTPServer.bind` now takes a backlog argument with the
+ same meaning as ``socket.listen``.
+* `~tornado.httpserver.HTTPServer` can now be run on a unix socket as well
+ as TCP.
+* Fixed exception at startup when ``socket.AI_ADDRCONFIG`` is not available,
+ as on Windows XP
+
+``IOLoop`` and ``IOStream``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* `~tornado.iostream.IOStream` performance has been improved, especially for
+ small synchronous requests.
+* New methods `tornado.iostream.IOStream.read_until_close` and
+ `tornado.iostream.IOStream.read_until_regex`.
+* `IOStream.read_bytes` and `IOStream.read_until_close` now take a
+ ``streaming_callback`` argument to return data as it is received rather
+ than all at once.
+* `IOLoop.add_timeout` now accepts `datetime.timedelta` objects in addition
+ to absolute timestamps.
+* `~tornado.ioloop.PeriodicCallback` now sticks to the specified period
+ instead of creeping later due to accumulated errors.
+* `tornado.ioloop.IOLoop` and `tornado.httpclient.HTTPClient` now have
+ ``close()`` methods that should be used in applications that create
+ and destroy many of these objects.
+* `IOLoop.install` can now be used to use a custom subclass of IOLoop
+ as the singleton without monkey-patching.
+* `~tornado.iostream.IOStream` should now always call the close callback
+ instead of the connect callback on a connection error.
+* The `IOStream` close callback will no longer be called while there
+ are pending read callbacks that can be satisfied with buffered data.
+
+
+``tornado.simple_httpclient``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* Now supports client SSL certificates with the ``client_key`` and
+ ``client_cert`` parameters to `tornado.httpclient.HTTPRequest`
+* Now takes a maximum buffer size, to allow reading files larger than 100MB
+* Now works with HTTP 1.0 servers that don't send a Content-Length header
+* The ``allow_nonstandard_methods`` flag on HTTP client requests now
+ permits methods other than ``POST`` and ``PUT`` to contain bodies.
+* Fixed file descriptor leaks and multiple callback invocations in
+ `SimpleAsyncHTTPClient`
+* No longer consumes extra connection resources when following redirects.
+* Now works with buggy web servers that separate headers with ``\n`` instead
+ of ``\r\n\r\n``.
+* Now sets ``response.request_time`` correctly.
+* Connect timeouts now work correctly.
+
+
+Other modules
+~~~~~~~~~~~~~
+
+* `tornado.auth.OpenIDMixin` now uses the correct realm when the
+ callback URI is on a different domain.
+* `tornado.autoreload` has a new command-line interface which can be used
+ to wrap any script. This replaces the ``--autoreload`` argument to
+ `tornado.testing.main` and is more robust against syntax errors.
+* `tornado.autoreload.watch` can be used to watch files other than
+ the sources of imported modules.
+* `tornado.database.Connection` has new variants of ``execute`` and
+ ``executemany`` that return the number of rows affected instead of
+ the last inserted row id.
+* `tornado.locale.load_translations` now accepts any properly-formatted
+ locale name, not just those in the predefined ``LOCALE_NAMES`` list.
+* `tornado.options.define` now takes a ``group`` parameter to group options
+ in ``--help`` output.
+* Template loaders now take a ``namespace`` constructor argument to add
+ entries to the template namespace.
+* `tornado.websocket` now supports the latest ("hybi-10") version of the
+ protocol (the old version, "hixie-76" is still supported; the correct
+ version is detected automatically).
+* `tornado.websocket` now works on Python 3
+
+
+Bug fixes
+~~~~~~~~~
+
+* Windows support has been improved. Windows is still not an officially
+ supported platform, but the test suite now passes and
+ `tornado.autoreload` works.
+* Uploading files whose names contain special characters will now work.
+* Cookie values containing special characters are now properly quoted
+ and unquoted.
+* Multi-line headers are now supported.
+* Repeated Content-Length headers (which may be added by certain proxies)
+ are now supported in `HTTPServer`.
+* Unicode string literals now work in template expressions.
+* The template ``{% module %}`` directive now works even if applications
+ use a template variable named ``modules``.
+* Requests with "Expect: 100-continue" now work on python 3
+
diff --git a/website/sphinx/releases/v2.1.1.rst b/website/sphinx/releases/v2.1.1.rst
new file mode 100644
index 0000000..69dbbce
--- /dev/null
+++ b/website/sphinx/releases/v2.1.1.rst
@@ -0,0 +1,29 @@
+What's new in Tornado 2.1.1
+===========================
+
+Oct 4, 2011
+-----------
+
+Bug fixes
+~~~~~~~~~
+
+* Fixed handling of closed connections with the ``epoll`` (i.e. Linux)
+ ``IOLoop``. Previously, closed connections could be shut down too early,
+ which most often manifested as "Stream is closed" exceptions in
+ ``SimpleAsyncHTTPClient``.
+* Fixed a case in which chunked responses could be closed prematurely,
+ leading to truncated output.
+* ``IOStream.connect`` now reports errors more consistently via logging
+ and the close callback (this affects e.g. connections to localhost
+ on FreeBSD).
+* ``IOStream.read_bytes`` again accepts both ``int`` and ``long`` arguments.
+* ``PeriodicCallback`` no longer runs repeatedly when ``IOLoop`` iterations
+ complete faster than the resolution of ``time.time()`` (mainly a problem
+ on Windows).
+
+Backwards-compatibility note
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* Listening for ``IOLoop.ERROR`` alone is no longer sufficient for detecting
+ closed connections on an otherwise unused socket. ``IOLoop.ERROR`` must
+ always be used in combination with ``READ`` or ``WRITE``.
diff --git a/website/sphinx/releases/v2.2.0.rst b/website/sphinx/releases/v2.2.0.rst
new file mode 100644
index 0000000..777eab8
--- /dev/null
+++ b/website/sphinx/releases/v2.2.0.rst
@@ -0,0 +1,130 @@
+What's new in Tornado 2.2
+=========================
+
+Jan 30, 2012
+------------
+
+Highlights
+~~~~~~~~~~
+
+* Updated and expanded WebSocket support.
+* Improved compatibility in the Twisted/Tornado bridge.
+* Template errors now generate better stack traces.
+* Better exception handling in `tornado.gen`.
+
+Security fixes
+~~~~~~~~~~~~~~
+
+* `tornado.simple_httpclient` now disables SSLv2 in all cases. Previously
+ SSLv2 would be allowed if the Python interpreter was linked against a
+ pre-1.0 version of OpenSSL.
+
+Backwards-incompatible changes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* `tornado.process.fork_processes` now raises `SystemExit` if all child
+ processes exit cleanly rather than returning ``None``. The old behavior
+ was surprising and inconsistent with most of the documented examples
+ of this function (which did not check the return value).
+* On Python 2.6, `tornado.simple_httpclient` only supports SSLv3. This
+ is because Python 2.6 does not expose a way to support both SSLv3 and TLSv1
+ without also supporting the insecure SSLv2.
+* `tornado.websocket` no longer supports the older "draft 76" version
+ of the websocket protocol by default, although this version can
+ be enabled by overriding `tornado.websocket.WebSocketHandler.allow_draft76`.
+
+``tornado.httpclient``
+~~~~~~~~~~~~~~~~~~~~~~
+
+* `SimpleAsyncHTTPClient` no longer hangs on ``HEAD`` requests,
+ responses with no content, or empty ``POST``/``PUT`` response bodies.
+* `SimpleAsyncHTTPClient` now supports 303 and 307 redirect codes.
+* `tornado.curl_httpclient` now accepts non-integer timeouts.
+* `tornado.curl_httpclient` now supports basic authentication with an
+ empty password.
+
+``tornado.httpserver``
+~~~~~~~~~~~~~~~~~~~~~~
+
+* `HTTPServer` with ``xheaders=True`` will no longer accept
+ ``X-Real-IP`` headers that don't look like valid IP addresses.
+* `HTTPServer` now treats the ``Connection`` request header as
+ case-insensitive.
+
+``tornado.ioloop`` and ``tornado.iostream``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* `IOStream.write` now works correctly when given an empty string.
+* `IOStream.read_until` (and ``read_until_regex``) now perform better
+ when there is a lot of buffered data, which improves peformance of
+ `SimpleAsyncHTTPClient` when downloading files with lots of
+ chunks.
+* `SSLIOStream` now works correctly when ``ssl_version`` is set to
+ a value other than ``SSLv23``.
+* Idle ``IOLoops`` no longer wake up several times a second.
+* `tornado.ioloop.PeriodicCallback` no longer triggers duplicate callbacks
+ when stopped and started repeatedly.
+
+``tornado.template``
+~~~~~~~~~~~~~~~~~~~~
+
+* Exceptions in template code will now show better stack traces that
+ reference lines from the original template file.
+* ``{#`` and ``#}`` can now be used for comments (and unlike the old
+ ``{% comment %}`` directive, these can wrap other template directives).
+* Template directives may now span multiple lines.
+
+``tornado.web``
+~~~~~~~~~~~~~~~
+
+* Now behaves better when given malformed ``Cookie`` headers
+* `RequestHandler.redirect` now has a ``status`` argument to send
+ status codes other than 301 and 302.
+* New method `RequestHandler.on_finish` may be overridden for post-request
+ processing (as a counterpart to `RequestHandler.prepare`)
+* `StaticFileHandler` now outputs ``Content-Length`` and ``Etag`` headers
+ on ``HEAD`` requests.
+* `StaticFileHandler` now has overridable ``get_version`` and
+ ``parse_url_path`` methods for use in subclasses.
+* `RequestHandler.static_url` now takes an ``include_host`` parameter
+ (in addition to the old support for the `RequestHandler.include_host`
+ attribute).
+
+``tornado.websocket``
+~~~~~~~~~~~~~~~~~~~~~
+
+* Updated to support the latest version of the protocol, as finalized
+ in RFC 6455.
+* Many bugs were fixed in all supported protocol versions.
+* `tornado.websocket` no longer supports the older "draft 76" version
+ of the websocket protocol by default, although this version can
+ be enabled by overriding `tornado.websocket.WebSocketHandler.allow_draft76`.
+* `WebSocketHandler.write_message` now accepts a ``binary`` argument
+ to send binary messages.
+* Subprotocols (i.e. the ``Sec-WebSocket-Protocol`` header) are now supported;
+ see the `WebSocketHandler.select_subprotocol` method for details.
+* `WebSocketHandler.get_websocket_scheme` can be used to select the
+ appropriate url scheme (``ws://`` or ``wss://``) in cases where
+ `HTTPRequest.protocol` is not set correctly.
+
+Other modules
+~~~~~~~~~~~~~
+
+* `tornado.auth.TwitterMixin.authenticate_redirect` now takes a
+ ``callback_uri`` parameter.
+* `tornado.auth.TwitterMixin.twitter_request` now accepts both URLs and
+ partial paths (complete URLs are useful for the search API which follows
+ different patterns).
+* Exception handling in `tornado.gen` has been improved. It is now possible
+ to catch exceptions thrown by a ``Task``.
+* `tornado.netutil.bind_sockets` now works when ``getaddrinfo`` returns
+ duplicate addresses.
+* `tornado.platform.twisted` compatibility has been significantly improved.
+ Twisted version 11.1.0 is now supported in addition to 11.0.0.
+* `tornado.process.fork_processes` correctly reseeds the `random` module
+ even when `os.urandom` is not implemented.
+* `tornado.testing.main` supports a new flag ``--exception_on_interrupt``,
+ which can be set to false to make ``Ctrl-C`` kill the process more
+ reliably (at the expense of stack traces when it does so).
+* `tornado.version_info` is now a four-tuple so official releases can be
+ distinguished from development branches.
diff --git a/website/sphinx/sphinx_coverage.py b/website/sphinx/sphinx_coverage.py
deleted file mode 100644
index 5ff81d7..0000000
--- a/website/sphinx/sphinx_coverage.py
+++ /dev/null
@@ -1,264 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.ext.coverage
- ~~~~~~~~~~~~~~~~~~~
-
- Check Python modules and C API for coverage. Mostly written by Josip
- Dzolonga for the Google Highly Open Participation contest.
-
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import re
-import glob
-import inspect
-import cPickle as pickle
-from os import path
-
-from sphinx.builders import Builder
-
-
-# utility
-def write_header(f, text, char='-'):
- f.write(text + '\n')
- f.write(char * len(text) + '\n')
-
-def compile_regex_list(name, exps, warnfunc):
- lst = []
- for exp in exps:
- try:
- lst.append(re.compile(exp))
- except Exception:
- warnfunc('invalid regex %r in %s' % (exp, name))
- return lst
-
-
-class CoverageBuilder(Builder):
-
- name = 'coverage'
-
- def init(self):
- self.c_sourcefiles = []
- for pattern in self.config.coverage_c_path:
- pattern = path.join(self.srcdir, pattern)
- self.c_sourcefiles.extend(glob.glob(pattern))
-
- self.c_regexes = []
- for (name, exp) in self.config.coverage_c_regexes.items():
- try:
- self.c_regexes.append((name, re.compile(exp)))
- except Exception:
- self.warn('invalid regex %r in coverage_c_regexes' % exp)
-
- self.c_ignorexps = {}
- for (name, exps) in self.config.coverage_ignore_c_items.iteritems():
- self.c_ignorexps[name] = compile_regex_list(
- 'coverage_ignore_c_items', exps, self.warn)
- self.mod_ignorexps = compile_regex_list(
- 'coverage_ignore_modules', self.config.coverage_ignore_modules,
- self.warn)
- self.cls_ignorexps = compile_regex_list(
- 'coverage_ignore_classes', self.config.coverage_ignore_classes,
- self.warn)
- self.fun_ignorexps = compile_regex_list(
- 'coverage_ignore_functions', self.config.coverage_ignore_functions,
- self.warn)
-
- def get_outdated_docs(self):
- return 'coverage overview'
-
- def write(self, *ignored):
- self.py_undoc = {}
- self.build_py_coverage()
- self.write_py_coverage()
-
- self.c_undoc = {}
- self.build_c_coverage()
- self.write_c_coverage()
-
- def build_c_coverage(self):
- # Fetch all the info from the header files
- c_objects = self.env.domaindata['c']['objects']
- for filename in self.c_sourcefiles:
- undoc = []
- f = open(filename, 'r')
- try:
- for line in f:
- for key, regex in self.c_regexes:
- match = regex.match(line)
- if match:
- name = match.groups()[0]
- if name not in c_objects:
- for exp in self.c_ignorexps.get(key, ()):
- if exp.match(name):
- break
- else:
- undoc.append((key, name))
- continue
- finally:
- f.close()
- if undoc:
- self.c_undoc[filename] = undoc
-
- def write_c_coverage(self):
- output_file = path.join(self.outdir, 'c.txt')
- op = open(output_file, 'w')
- try:
- if self.config.coverage_write_headline:
- write_header(op, 'Undocumented C API elements', '=')
- op.write('\n')
-
- for filename, undoc in self.c_undoc.iteritems():
- write_header(op, filename)
- for typ, name in undoc:
- op.write(' * %-50s [%9s]\n' % (name, typ))
- op.write('\n')
- finally:
- op.close()
-
- def build_py_coverage(self):
- objects = self.env.domaindata['py']['objects']
- modules = self.env.domaindata['py']['modules']
-
- skip_undoc = self.config.coverage_skip_undoc_in_source
-
- for mod_name in modules:
- ignore = False
- for exp in self.mod_ignorexps:
- if exp.match(mod_name):
- ignore = True
- break
- if ignore:
- continue
-
- try:
- mod = __import__(mod_name, fromlist=['foo'])
- except ImportError, err:
- self.warn('module %s could not be imported: %s' %
- (mod_name, err))
- self.py_undoc[mod_name] = {'error': err}
- continue
-
- funcs = []
- classes = {}
-
- for name, obj in inspect.getmembers(mod):
- # diverse module attributes are ignored:
- if name[0] == '_':
- # begins in an underscore
- continue
- if not hasattr(obj, '__module__'):
- # cannot be attributed to a module
- continue
- if obj.__module__ != mod_name:
- # is not defined in this module
- continue
-
- full_name = '%s.%s' % (mod_name, name)
-
- if inspect.isfunction(obj):
- if full_name not in objects:
- for exp in self.fun_ignorexps:
- if exp.match(name):
- break
- else:
- if skip_undoc and not obj.__doc__:
- continue
- funcs.append(name)
- elif inspect.isclass(obj):
- for exp in self.cls_ignorexps:
- if exp.match(name):
- break
- else:
- if full_name not in objects:
- if skip_undoc and not obj.__doc__:
- continue
- # not documented at all
- classes[name] = []
- continue
-
- attrs = []
-
- for attr_name in dir(obj):
- if attr_name not in obj.__dict__:
- continue
- attr = getattr(obj, attr_name)
- if not (inspect.ismethod(attr) or
- inspect.isfunction(attr)):
- continue
- if attr_name[0] == '_':
- # starts with an underscore, ignore it
- continue
- if skip_undoc and not attr.__doc__:
- # skip methods without docstring if wished
- continue
-
- full_attr_name = '%s.%s' % (full_name, attr_name)
- if full_attr_name not in objects:
- attrs.append(attr_name)
-
- if attrs:
- # some attributes are undocumented
- classes[name] = attrs
-
- self.py_undoc[mod_name] = {'funcs': funcs, 'classes': classes}
-
- def write_py_coverage(self):
- output_file = path.join(self.outdir, 'python.txt')
- op = open(output_file, 'w')
- failed = []
- try:
- if self.config.coverage_write_headline:
- write_header(op, 'Undocumented Python objects', '=')
- keys = self.py_undoc.keys()
- keys.sort()
- for name in keys:
- undoc = self.py_undoc[name]
- if 'error' in undoc:
- failed.append((name, undoc['error']))
- else:
- if not undoc['classes'] and not undoc['funcs']:
- continue
-
- write_header(op, name)
- if undoc['funcs']:
- op.write('Functions:\n')
- op.writelines(' * %s\n' % x for x in undoc['funcs'])
- op.write('\n')
- if undoc['classes']:
- op.write('Classes:\n')
- for name, methods in sorted(undoc['classes'].iteritems()):
- if not methods:
- op.write(' * %s\n' % name)
- else:
- op.write(' * %s -- missing methods:\n' % name)
- op.writelines(' - %s\n' % x for x in methods)
- op.write('\n')
-
- if failed:
- write_header(op, 'Modules that failed to import')
- op.writelines(' * %s -- %s\n' % x for x in failed)
- finally:
- op.close()
-
- def finish(self):
- # dump the coverage data to a pickle file too
- picklepath = path.join(self.outdir, 'undoc.pickle')
- dumpfile = open(picklepath, 'wb')
- try:
- pickle.dump((self.py_undoc, self.c_undoc), dumpfile)
- finally:
- dumpfile.close()
-
-
-def setup(app):
- app.add_builder(CoverageBuilder)
- app.add_config_value('coverage_ignore_modules', [], False)
- app.add_config_value('coverage_ignore_functions', [], False)
- app.add_config_value('coverage_ignore_classes', [], False)
- app.add_config_value('coverage_c_path', [], False)
- app.add_config_value('coverage_c_regexes', {}, False)
- app.add_config_value('coverage_ignore_c_items', {}, False)
- app.add_config_value('coverage_write_headline', True, False)
- app.add_config_value('coverage_skip_undoc_in_source', False, False)
diff --git a/website/sphinx/template.rst b/website/sphinx/template.rst
index bb546d1..40d0be2 100644
--- a/website/sphinx/template.rst
+++ b/website/sphinx/template.rst
@@ -2,4 +2,20 @@
===================================================
.. automodule:: tornado.template
- :members:
+
+ Class reference
+ ---------------
+
+ .. autoclass:: Template
+ :members:
+
+ .. autoclass:: BaseLoader
+ :members:
+
+ .. autoclass:: Loader
+ :members:
+
+ .. autoclass:: DictLoader
+ :members:
+
+ .. autoexception:: ParseError
diff --git a/website/sphinx/twisted.rst b/website/sphinx/twisted.rst
new file mode 100644
index 0000000..6a4d854
--- /dev/null
+++ b/website/sphinx/twisted.rst
@@ -0,0 +1,45 @@
+``tornado.platform.twisted`` --- Run code written for Twisted on Tornado
+========================================================================
+
+.. module:: tornado.platform.twisted
+
+This module contains a Twisted reactor build on the Tornado IOLoop,
+which lets you run applications and libraries written for Twisted in a
+Tornado application. To use it, simply call `install` at the
+beginning of the application::
+
+ import tornado.platform.twisted
+ tornado.platform.twisted.install()
+ from twisted.internet import reactor
+
+When the app is ready to start, call `IOLoop.instance().start()`
+instead of `reactor.run()`. This will allow you to use a mixture of
+Twisted and Tornado code in the same process.
+
+It is also possible to create a non-global reactor by calling
+`tornado.platform.twisted.TornadoReactor(io_loop)`. However, if
+the `IOLoop` and reactor are to be short-lived (such as those used in
+unit tests), additional cleanup may be required. Specifically, it is
+recommended to call::
+
+ reactor.fireSystemEvent('shutdown')
+ reactor.disconnectAll()
+
+before closing the `IOLoop`.
+
+This module has been tested with Twisted versions 11.0.0 and 11.1.0.
+
+.. function:: install(io_loop=None)
+
+Install this package as the default Twisted reactor.
+
+.. class:: TornadoReactor(io_loop=None)
+
+Twisted reactor built on the Tornado IOLoop.
+
+Since it is intented to be used in applications where the top-level
+event loop is ``io_loop.start()`` rather than ``reactor.run()``,
+it is implemented a little differently than other Twisted reactors.
+We override `mainLoop` instead of `doIteration` and must implement
+timed call functionality on top of `IOLoop.add_timeout` rather than
+using the implementation in `PosixReactorBase`.
diff --git a/website/sphinx/utilities.rst b/website/sphinx/utilities.rst
index 89c9908..775e46c 100644
--- a/website/sphinx/utilities.rst
+++ b/website/sphinx/utilities.rst
@@ -4,8 +4,10 @@
.. toctree::
autoreload
+ gen
httputil
options
+ process
stack_context
testing
diff --git a/website/sphinx/web.rst b/website/sphinx/web.rst
index fd1af5b..55d69a2 100644
--- a/website/sphinx/web.rst
+++ b/website/sphinx/web.rst
@@ -12,6 +12,7 @@
.. automethod:: RequestHandler.initialize
.. automethod:: RequestHandler.prepare
+ .. automethod:: RequestHandler.on_finish
Implement any of the following methods to handle the corresponding
HTTP method.
@@ -39,6 +40,8 @@
.. automethod:: RequestHandler.set_status
.. automethod:: RequestHandler.set_header
+ .. automethod:: RequestHandler.add_header
+ .. automethod:: RequestHandler.set_default_headers
.. automethod:: RequestHandler.write
.. automethod:: RequestHandler.flush
.. automethod:: RequestHandler.finish
@@ -46,7 +49,7 @@
.. automethod:: RequestHandler.render_string
.. automethod:: RequestHandler.redirect
.. automethod:: RequestHandler.send_error
- .. automethod:: RequestHandler.get_error_html
+ .. automethod:: RequestHandler.write_error
.. automethod:: RequestHandler.clear
diff --git a/website/sphinx/websocket.rst b/website/sphinx/websocket.rst
index c4bd0ed..be7bbc5 100644
--- a/website/sphinx/websocket.rst
+++ b/website/sphinx/websocket.rst
@@ -2,4 +2,30 @@
====================================================================
.. automodule:: tornado.websocket
- :members:
+
+ .. autoclass:: WebSocketHandler
+
+ Event handlers
+ --------------
+
+ .. automethod:: WebSocketHandler.open
+ .. automethod:: WebSocketHandler.on_message
+ .. automethod:: WebSocketHandler.on_close
+ .. automethod:: WebSocketHandler.select_subprotocol
+
+ Output
+ ------
+
+ .. automethod:: WebSocketHandler.write_message
+ .. automethod:: WebSocketHandler.close
+
+ Configuration
+ -------------
+
+ .. automethod:: WebSocketHandler.allow_draft76
+ .. automethod:: WebSocketHandler.get_websocket_scheme
+
+ Other
+ -----
+
+ .. automethod:: WebSocketHandler.async_callback
diff --git a/website/templates/index.html b/website/templates/index.html
index aad0915..40fb166 100644
--- a/website/templates/index.html
+++ b/website/templates/index.html
@@ -4,6 +4,14 @@
<p><a href="http://www.tornadoweb.org/">Tornado</a> is an open source version of the scalable, non-blocking web server and tools that power <a href="http://friendfeed.com/">FriendFeed</a>. The FriendFeed application is written using a web framework that looks a bit like <a href="http://webpy.org/">web.py</a> or <a href="http://code.google.com/appengine/docs/python/tools/webapp/">Google's webapp</a>, but with additional tools and optimizations to take advantage of the underlying non-blocking infrastructure.</p>
<p>The framework is distinct from most mainstream web server frameworks (and certainly most Python frameworks) because it is non-blocking and reasonably fast. Because it is non-blocking and uses <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/epoll.4.html"><code>epoll</code></a> or <code>kqueue</code>, it can handle thousands of simultaneous standing connections, which means it is ideal for real-time web services. We built the web server specifically to handle FriendFeed's real-time features — every active user of FriendFeed maintains an open connection to the FriendFeed servers. (For more information on scaling servers to support thousands of clients, see The <a href="http://www.kegel.com/c10k.html">C10K problem</a>.)</p>
+ <h2>Upgrading from Tornado 1.x</h2>
+
+ <p>Tornado 2.0 introduces several potentially backwards-incompatible changes,
+ including in particular automatic escaping of template output. Users
+ who are upgrading from Tornado 1.x should see the
+ <a href="/documentation/releases/v2.0.0.html">release notes</a> for
+ information about backwards compatibility.</p>
+
<h2>Quick links</h2>
<ul>
@@ -47,7 +55,7 @@
<ul>
<li>On Python 2.6 and 2.7, there are no dependencies outside the Python standard library, although <a href="http://pycurl.sourceforge.net/">PycURL</a> (version 7.18.2 or higher required; version 7.21.1 or higher recommended) may be used if desired.</li>
<li>On Python 2.5, PycURL is required, along with <a href="http://pypi.python.org/pypi/simplejson/">simplejson</a> and the Python development headers (typically obtained by installing a package named something like <code>python-dev</code> from your operating system).</li>
- <li>On Python 3.2, the <a href="http://pypi.python.org/pypi/distribute">distribute</a> package is required.</li>
+ <li>On Python 3.2, the <a href="http://pypi.python.org/pypi/distribute">distribute</a> package is required. Note that Python 3 support is relatively new and may have bugs.</li>
</ul></p>
<p><b>Platforms:</b> Tornado should run on any Unix-like platform, although
diff --git a/website/website.py b/website/website.py
index 8fe0c35..24acddc 100644
--- a/website/website.py
+++ b/website/website.py
@@ -16,7 +16,6 @@
import os
import os.path
-import time
import tornado.web
import tornado.wsgi
import wsgiref.handlers