Merge branch 'master' into http_refactor

Conflicts:
	tornado/iostream.py
This commit is contained in:
Ben Darnell 2014-04-26 20:32:10 -04:00
commit 500a605534
25 changed files with 277 additions and 106 deletions

View File

@ -62,6 +62,7 @@ class LoginHandler(BaseHandler, TwitterMixin):
def get(self):
if self.get_argument('oauth_token', None):
user = yield self.get_authenticated_user()
del user["description"]
self.set_secure_cookie(self.COOKIE_NAME, json_encode(user))
self.redirect(self.get_argument('next', '/'))
else:

View File

@ -51,10 +51,10 @@ var updater = {
start: function() {
var url = "ws://" + location.host + "/chatsocket";
updater.socket = new WebSocket(url);
updater.socket.onmessage = function(event) {
updater.showMessage(JSON.parse(event.data));
}
updater.socket = new WebSocket(url);
updater.socket.onmessage = function(event) {
updater.showMessage(JSON.parse(event.data));
}
},
showMessage: function(message) {

View File

@ -3,43 +3,25 @@
.. automodule:: tornado.concurrent
:members:
:exclude-members: Future, TracebackFuture
.. py:class:: Future
.. autoclass:: Future
A ``Future`` encapsulates the result of an asynchronous
operation. In synchronous applications ``Futures`` are used
to wait for the result from a thread or process pool; in
Tornado they are normally used with `.IOLoop.add_future` or by
yielding them in a `.gen.coroutine`.
Consumer methods
^^^^^^^^^^^^^^^^
If the `concurrent.futures` package is available,
`tornado.concurrent.Future` is simply an alias for
`concurrent.futures.Future`. Otherwise, we support the same
interface with a few limitations:
.. automethod:: Future.result
.. automethod:: Future.exception
.. automethod:: Future.exc_info
.. automethod:: Future.add_done_callback
.. automethod:: Future.done
.. automethod:: Future.running
.. automethod:: Future.cancel
.. automethod:: Future.cancelled
* It is an error to call `result` or `exception` before the
``Future`` has completed.
* Cancellation is not supported.
Producer methods
^^^^^^^^^^^^^^^^
.. py:method:: result()
If the operation succeeded, return its result. If it failed,
re-raise its exception.
.. py:method:: exception()
If the operation raised an exception, return the `Exception`
object. Otherwise returns None.
.. py:method:: add_done_callback(fn)
Attaches the given callback to the `Future`. It will be invoked
with the `Future` as its argument when it has finished running
and its result is available. In Tornado consider using
`.IOLoop.add_future` instead of calling `add_done_callback`
directly.
.. py:method:: done()
Returns True if the future has finished running and its
`result` and `exception` methods are available.
.. automethod:: Future.set_result
.. automethod:: Future.set_exception
.. automethod:: Future.set_exc_info

View File

@ -16,6 +16,7 @@
.. automethod:: IOLoop.instance
.. automethod:: IOLoop.initialized
.. automethod:: IOLoop.install
.. automethod:: IOLoop.clear_instance
.. automethod:: IOLoop.start
.. automethod:: IOLoop.stop
.. automethod:: IOLoop.run_sync

View File

@ -24,6 +24,12 @@ Backwards-compatibility notes
of the old ``TracebackFuture`` class. ``TracebackFuture`` is now
simply an alias for ``Future``.
`tornado.httpclient`
~~~~~~~~~~~~~~~~~~~~
* The command-line HTTP client (``python -m tornado.httpclient $URL``)
now works on Python 3.
`tornado.gen`
~~~~~~~~~~~~~
@ -40,6 +46,8 @@ Backwards-compatibility notes
* `.IOLoop.add_handler` and related methods now accept file-like objects
in addition to raw file descriptors. Passing the objects is recommended
(when possible) to avoid a garbage-collection-related problem in unit tests.
* New method `.IOLoop.clear_instance` makes it possible to uninstall the
singleton instance.
`tornado.iostream`
~~~~~~~~~~~~~~~~~~
@ -47,6 +55,16 @@ Backwards-compatibility notes
* The ``callback`` argument to most `.IOStream` methods is now optional.
When called without a callback the method will return a `.Future`
for use with coroutines.
* No longer gets confused when an ``IOError`` or ``OSError`` without
an ``errno`` attribute is raised.
`tornado.netutil`
~~~~~~~~~~~~~~~~~
* When `.bind_sockets` chooses a port automatically, it will now use
the same port for IPv4 and IPv6.
* TLS compression is now disabled by default on Python 3.3 and higher
(it is not possible to change this option in older versions.
`tornado.options`
~~~~~~~~~~~~~~~~~
@ -59,6 +77,12 @@ Backwards-compatibility notes
* Now works on Python 2.6.
``tornado.simple_httpclient``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Improved default cipher suite selection (Python 2.7+).
`tornado.stack_context`
~~~~~~~~~~~~~~~~~~~~~~~
@ -72,6 +96,7 @@ Backwards-compatibility notes
but were not run with ``@gen_test`` or any similar decorator (this would
previously result in the test silently being skipped).
* Better stack traces are now displayed when a test times out.
* Fixed the test suite when ``unittest2`` is installed on Python 3.
`tornado.web`
~~~~~~~~~~~~~
@ -89,3 +114,5 @@ Backwards-compatibility notes
values when the other side closes.
* The C speedup module now builds correctly with MSVC, and can support
messages larger than 2GB on 64-bit systems.
* The fallback mechanism for detecting a missing C compiler now
works correctly on Mac OS X.

View File

@ -32,7 +32,7 @@ New modules
`tornado.concurrent`
~~~~~~~~~~~~~~~~~~~~
* `.TracebackFuture` now accepts a ``timeout`` keyword argument (although
* ``TracebackFuture`` now accepts a ``timeout`` keyword argument (although
it is still incorrect to use a non-zero timeout in non-blocking code).
``tornado.curl_httpclient``

View File

@ -42,7 +42,8 @@ if sys.platform == 'win32' and sys.version_info > (2, 6):
build_errors = (CCompilerError, DistutilsExecError,
DistutilsPlatformError, IOError)
else:
build_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
build_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError,
SystemError)
class custom_build_ext(build_ext):
"""Allow C extension building to fail.

View File

@ -43,8 +43,15 @@ class ReturnValueIgnoredError(Exception):
class Future(object):
"""Placeholder for an asynchronous result.
Similar to `concurrent.futures.Future`, but not thread-safe (and
therefore faster for use with single-threaded event loops.
A ``Future`` encapsulates the result of an asynchronous
operation. In synchronous applications ``Futures`` are used
to wait for the result from a thread or process pool; in
Tornado they are normally used with `.IOLoop.add_future` or by
yielding them in a `.gen.coroutine`.
`tornado.concurrent.Future` is similar to
`concurrent.futures.Future`, but not thread-safe (and therefore
faster for use with single-threaded event loops).
In addition to ``exception`` and ``set_exception``, methods ``exc_info``
and ``set_exc_info`` are supported to capture tracebacks in Python 2.
@ -52,6 +59,14 @@ class Future(object):
Python 2 futures backport this information is discarded.
This functionality was previously available in a separate class
``TracebackFuture``, which is now a deprecated alias for this class.
.. versionchanged:: 3.3
`tornado.concurrent.Future` is always a thread-unsafe ``Future``
with support for the ``exc_info`` methods. Previously it would
be an alias for the thread-safe `concurrent.futures.Future`
if that package was available and fall back to the thread-unsafe
implementation if it was not.
"""
def __init__(self):
self._done = False
@ -61,18 +76,33 @@ class Future(object):
self._callbacks = []
def cancel(self):
"""Cancel the operation, if possible.
Tornado ``Futures`` do not support cancellation, so this method always
returns False.
"""
return False
def cancelled(self):
"""Returns True if the operation has been cancelled.
Tornado ``Futures`` do not support cancellation, so this method
always returns False.
"""
return False
def running(self):
"""Returns True if this operation is currently running."""
return not self._done
def done(self):
"""Returns True if the future has finished running."""
return self._done
def result(self, timeout=None):
"""If the operation succeeded, return its result. If it failed,
re-raise its exception.
"""
if self._result is not None:
return self._result
if self._exc_info is not None:
@ -83,6 +113,9 @@ class Future(object):
return self._result
def exception(self, timeout=None):
"""If the operation raised an exception, return the `Exception`
object. Otherwise returns None.
"""
if self._exception is not None:
return self._exception
else:
@ -90,25 +123,45 @@ class Future(object):
return None
def add_done_callback(self, fn):
"""Attaches the given callback to the `Future`.
It will be invoked with the `Future` as its argument when the Future
has finished running and its result is available. In Tornado
consider using `.IOLoop.add_future` instead of calling
`add_done_callback` directly.
"""
if self._done:
fn(self)
else:
self._callbacks.append(fn)
def set_result(self, result):
"""Sets the result of a ``Future``.
It is undefined to call any of the ``set`` methods more than once
on the same object.
"""
self._result = result
self._set_done()
def set_exception(self, exception):
"""Sets the exception of a ``Future.``"""
self._exception = exception
self._set_done()
def exc_info(self):
"""Returns a tuple in the same format as `sys.exc_info` or None.
.. versionadded:: 3.3
"""
return self._exc_info
def set_exc_info(self, exc_info):
"""Traceback-aware replacement for
`~concurrent.futures.Future.set_exception`.
"""Sets the exception information of a ``Future.``
Preserves tracebacks on Python 2.
.. versionadded:: 3.3
"""
self._exc_info = exc_info
self.set_exception(exc_info[1])

View File

@ -34,7 +34,7 @@ import time
import weakref
from tornado.concurrent import TracebackFuture
from tornado.escape import utf8
from tornado.escape import utf8, native_str
from tornado import httputil, stack_context
from tornado.ioloop import IOLoop
from tornado.util import Configurable
@ -166,7 +166,7 @@ class AsyncHTTPClient(Configurable):
kwargs: ``HTTPRequest(request, **kwargs)``
This method returns a `.Future` whose result is an
`HTTPResponse`. The ``Future`` wil raise an `HTTPError` if
`HTTPResponse`. The ``Future`` will raise an `HTTPError` if
the request returned a non-200 response code.
If a ``callback`` is given, it will be invoked with the `HTTPResponse`.
@ -581,7 +581,7 @@ def main():
if options.print_headers:
print(response.headers)
if options.print_body:
print(response.body)
print(native_str(response.body))
client.close()
if __name__ == "__main__":

View File

@ -45,6 +45,7 @@ from tornado.concurrent import TracebackFuture, is_future
from tornado.log import app_log, gen_log
from tornado import stack_context
from tornado.util import Configurable
from tornado.util import errno_from_exception
try:
import signal
@ -156,6 +157,12 @@ class IOLoop(Configurable):
assert not IOLoop.initialized()
IOLoop._instance = self
@staticmethod
def clear_instance():
"""Clear the global `IOLoop` instance."""
if hasattr(IOLoop, "_instance"):
del IOLoop._instance
@staticmethod
def current():
"""Returns the current thread's `IOLoop`.
@ -726,9 +733,7 @@ class PollIOLoop(IOLoop):
# two ways EINTR might be signaled:
# * e.errno == errno.EINTR
# * e.args is like (errno.EINTR, 'Interrupted system call')
if (getattr(e, 'errno', None) == errno.EINTR or
(isinstance(getattr(e, 'args', None), tuple) and
len(e.args) == 2 and e.args[0] == errno.EINTR)):
if errno_from_exception(e) == errno.EINTR:
continue
else:
raise
@ -748,7 +753,7 @@ class PollIOLoop(IOLoop):
fd_obj, handler_func = self._handlers[fd]
handler_func(fd_obj, events)
except (OSError, IOError) as e:
if e.args[0] == errno.EPIPE:
if errno_from_exception(e) == errno.EPIPE:
# Happens when the client closes the connection
pass
else:

View File

@ -40,7 +40,7 @@ from tornado import ioloop
from tornado.log import gen_log, app_log
from tornado.netutil import ssl_wrap_socket, ssl_match_hostname, SSLCertificateError
from tornado import stack_context
from tornado.util import bytes_type
from tornado.util import bytes_type, errno_from_exception
try:
from tornado.platform.posix import _set_nonblocking
@ -80,8 +80,13 @@ class BaseIOStream(object):
"""A utility class to write to and read from a non-blocking file or socket.
We support a non-blocking ``write()`` and a family of ``read_*()`` methods.
All of the methods take callbacks (since writing and reading are
non-blocking and asynchronous).
All of the methods take an optional ``callback`` argument and return a
`.Future` only if no callback is given. When the operation completes,
the callback will be run or the `.Future` will resolve with the data
read (or ``None`` for ``write()``). All outstanding ``Futures`` will
resolve with a `StreamClosedError` when the stream is closed; users
of the callback interface will be notified via
`.BaseIOStream.set_close_callback` instead.
When a stream is closed due to an error, the IOStream's ``error``
attribute contains the exception object.
@ -161,14 +166,20 @@ class BaseIOStream(object):
return None
def read_until_regex(self, regex, callback=None, max_bytes=None):
"""Run ``callback`` when we read the given regex pattern.
"""Asynchronously read until we have matched the given regex.
The callback will get the data read (including the data that
matched the regex and anything that came before it) as an argument.
The result includes the data that matches the regex and anything
that came before it. If a callback is given, it will be run
with the data as an argument; if not, this method returns a
`.Future`.
If ``max_bytes`` is not None, the connection will be closed
if more than ``max_bytes`` bytes have been read and the regex is
not satisfied.
.. versionchanged:: 3.3
Added the ``max_bytes`` argument. The ``callback`` argument is
now optional and a `.Future` will be returned if it is omitted.
"""
future = self._set_read_callback(callback)
self._read_regex = re.compile(regex)
@ -183,14 +194,19 @@ class BaseIOStream(object):
return future
def read_until(self, delimiter, callback=None, max_bytes=None):
"""Run ``callback`` when we read the given delimiter.
"""Asynchronously read until we have found the given delimiter.
The callback will get the data read (including the delimiter)
as an argument.
The result includes all the data read including the delimiter.
If a callback is given, it will be run with the data as an argument;
if not, this method returns a `.Future`.
If ``max_bytes`` is not None, the connection will be closed
if more than ``max_bytes`` bytes have been read and the delimiter
is not found.
.. versionchanged:: 3.3
Added the ``max_bytes`` argument. The ``callback`` argument is
now optional and a `.Future` will be returned if it is omitted.
"""
future = self._set_read_callback(callback)
self._read_delimiter = delimiter
@ -206,15 +222,20 @@ class BaseIOStream(object):
def read_bytes(self, num_bytes, callback=None, streaming_callback=None,
partial=False):
"""Run callback when we read the given number of bytes.
"""Asynchronously read a number of bytes.
If a ``streaming_callback`` is given, it will be called with chunks
of data as they become available, and the argument to the final
``callback`` will be empty. Otherwise, the ``callback`` gets
the data as an argument.
of data as they become available, and the final result will be empty.
Otherwise, the result is all the data that was read.
If a callback is given, it will be run with the data as an argument;
if not, this method returns a `.Future`.
If ``partial`` is true, the callback is run as soon as we have
any bytes to return (but never more than ``num_bytes``)
.. versionchanged:: 3.3
Added the ``partial`` argument. The callback argument is now
optional and a `.Future` will be returned if it is omitted.
"""
future = self._set_read_callback(callback)
assert isinstance(num_bytes, numbers.Integral)
@ -225,15 +246,17 @@ class BaseIOStream(object):
return future
def read_until_close(self, callback=None, streaming_callback=None):
"""Reads all data from the socket until it is closed.
"""Asynchronously reads all data from the socket until it is closed.
If a ``streaming_callback`` is given, it will be called with chunks
of data as they become available, and the argument to the final
``callback`` will be empty. Otherwise, the ``callback`` gets the
data as an argument.
of data as they become available, and the final result will be empty.
Otherwise, the result is all the data that was read.
If a callback is given, it will be run with the data as an argument;
if not, this method returns a `.Future`.
Subject to ``max_buffer_size`` limit from `IOStream` constructor if
a ``streaming_callback`` is not used.
.. versionchanged:: 3.3
The callback argument is now optional and a `.Future` will
be returned if it is omitted.
"""
future = self._set_read_callback(callback)
self._streaming_callback = stack_context.wrap(streaming_callback)
@ -243,17 +266,24 @@ class BaseIOStream(object):
self._run_read_callback(self._read_buffer_size, False)
return future
self._read_until_close = True
self._streaming_callback = stack_context.wrap(streaming_callback)
self._try_inline_read()
return future
def write(self, data, callback=None):
"""Write the given data to this stream.
"""Asynchronously write the given data to this stream.
If ``callback`` is given, we call it when all of the buffered write
data has been successfully written to the stream. If there was
previously buffered write data and an old write callback, that
callback is simply overwritten with this new callback.
If no ``callback`` is given, this method returns a `.Future` that
resolves (with a result of ``None``) when the write has been
completed. If `write` is called again before that `.Future` has
resolved, the previous future will be orphaned and will never resolve.
.. versionchanged:: 3.3
Now returns a `.Future` if no callback is given.
"""
assert isinstance(data, bytes_type)
self._check_closed()
@ -279,7 +309,12 @@ class BaseIOStream(object):
return future
def set_close_callback(self, callback):
"""Call the given callback when the stream is closed."""
"""Call the given callback when the stream is closed.
This is not necessary for applications that use the `.Future`
interface; all outstanding ``Futures`` will resolve with a
`StreamClosedError` when the stream is closed.
"""
self._close_callback = stack_context.wrap(callback)
def close(self, exc_info=False):
@ -847,7 +882,8 @@ class IOStream(BaseIOStream):
not previously connected. The address parameter is in the
same format as for `socket.connect <socket.socket.connect>`,
i.e. a ``(host, port)`` tuple. If ``callback`` is specified,
it will be called when the connection is completed.
it will be called when the connection is completed; if not
this method returns a `.Future`.
If specified, the ``server_hostname`` parameter will be used
in SSL connections for certificate validation (if requested in
@ -859,6 +895,9 @@ class IOStream(BaseIOStream):
which case the data will be written as soon as the connection
is ready. Calling `IOStream` read methods before the socket is
connected works on some platforms but is non-portable.
.. versionchanged:: 3.3
If no callback is given, returns a `.Future`.
"""
self._connecting = True
try:
@ -871,8 +910,8 @@ class IOStream(BaseIOStream):
# returned immediately when attempting to connect to
# localhost, so handle them the same way as an error
# reported later in _handle_connect.
if (e.args[0] != errno.EINPROGRESS and
e.args[0] not in _ERRNO_WOULDBLOCK):
if (errno_from_exception(e) != errno.EINPROGRESS and
errno_from_exception(e) not in _ERRNO_WOULDBLOCK):
gen_log.warning("Connect error on fd %s: %s",
self.socket.fileno(), e)
self.close(exc_info=True)
@ -1131,9 +1170,9 @@ class PipeIOStream(BaseIOStream):
try:
chunk = os.read(self.fd, self.read_chunk_size)
except (IOError, OSError) as e:
if e.args[0] in _ERRNO_WOULDBLOCK:
if errno_from_exception(e) in _ERRNO_WOULDBLOCK:
return None
elif e.args[0] == errno.EBADF:
elif errno_from_exception(e) == errno.EBADF:
# If the writing half of a pipe is closed, select will
# report it as readable but reads will fail with EBADF.
self.close(exc_info=True)

View File

@ -27,7 +27,7 @@ import stat
from tornado.concurrent import dummy_executor, run_on_executor
from tornado.ioloop import IOLoop
from tornado.platform.auto import set_close_exec
from tornado.util import u, Configurable
from tornado.util import u, Configurable, errno_from_exception
if hasattr(ssl, 'match_hostname') and hasattr(ssl, 'CertificateError'): # python 3.2+
ssl_match_hostname = ssl.match_hostname
@ -84,7 +84,7 @@ def bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=128, flags
try:
sock = socket.socket(af, socktype, proto)
except socket.error as e:
if e.args[0] == errno.EAFNOSUPPORT:
if errno_from_exception(e) == errno.EAFNOSUPPORT:
continue
raise
set_close_exec(sock.fileno())
@ -133,7 +133,7 @@ if hasattr(socket, 'AF_UNIX'):
try:
st = os.stat(file)
except OSError as err:
if err.errno != errno.ENOENT:
if errno_from_exception(err) != errno.ENOENT:
raise
else:
if stat.S_ISSOCK(st.st_mode):
@ -165,12 +165,12 @@ def add_accept_handler(sock, callback, io_loop=None):
except socket.error as e:
# EWOULDBLOCK and EAGAIN indicate we have accepted every
# connection that is available.
if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
if errno_from_exception(e) in (errno.EWOULDBLOCK, errno.EAGAIN):
return
# ECONNABORTED indicates that there was a connection
# but it was closed while still in the accept queue.
# (observed on FreeBSD).
if e.args[0] == errno.ECONNABORTED:
if errno_from_exception(e) == errno.ECONNABORTED:
continue
raise
callback(connection, address)
@ -390,6 +390,10 @@ def ssl_options_to_context(ssl_options):
context.load_verify_locations(ssl_options['ca_certs'])
if 'ciphers' in ssl_options:
context.set_ciphers(ssl_options['ciphers'])
if hasattr(ssl, 'OP_NO_COMPRESSION'):
# Disable TLS compression to avoid CRIME and related attacks.
# This constant wasn't added until python 3.3.
context.options |= ssl.OP_NO_COMPRESSION
return context

View File

@ -15,7 +15,8 @@ class Waker(interface.Waker):
and Jython.
"""
def __init__(self):
# Based on Zope async.py: http://svn.zope.org/zc.ngi/trunk/src/zc/ngi/async.py
# Based on Zope select_trigger.py:
# https://github.com/zopefoundation/Zope/blob/master/src/ZServer/medusa/thread/select_trigger.py
self.writer = socket.socket()
# Disable buffering -- pulling the trigger sends 1 byte,

View File

@ -35,6 +35,7 @@ from tornado.iostream import PipeIOStream
from tornado.log import gen_log
from tornado.platform.auto import set_close_exec
from tornado import stack_context
from tornado.util import errno_from_exception
try:
long # py2
@ -136,7 +137,7 @@ def fork_processes(num_processes, max_restarts=100):
try:
pid, status = os.wait()
except OSError as e:
if e.errno == errno.EINTR:
if errno_from_exception(e) == errno.EINTR:
continue
raise
if pid not in children:
@ -283,7 +284,7 @@ class Subprocess(object):
try:
ret_pid, status = os.waitpid(pid, os.WNOHANG)
except OSError as e:
if e.args[0] == errno.ECHILD:
if errno_from_exception(e) == errno.ECHILD:
return
if ret_pid == 0:
return

View File

@ -242,7 +242,9 @@ class _HTTPConnection(httputil.HTTPMessageDelegate):
# but nearly all servers support both SSLv3 and TLSv1:
# http://blog.ivanristic.com/2011/09/ssl-survey-protocol-support.html
if sys.version_info >= (2, 7):
ssl_options["ciphers"] = "DEFAULT:!SSLv2"
# In addition to disabling SSLv2, we also exclude certain
# classes of insecure ciphers.
ssl_options["ciphers"] = "DEFAULT:!SSLv2:!EXPORT:!DES"
else:
# This is really only necessary for pre-1.0 versions
# of openssl, but python 2.6 doesn't expose version

View File

@ -27,6 +27,7 @@ from tornado.ioloop import IOLoop
from tornado.iostream import IOStream, SSLIOStream
from tornado.netutil import bind_sockets, add_accept_handler, ssl_wrap_socket
from tornado import process
from tornado.util import errno_from_exception
class TCPServer(object):
@ -233,7 +234,7 @@ class TCPServer(object):
# SSLIOStream._do_ssl_handshake).
# To test this behavior, try nmap with the -sT flag.
# https://github.com/facebook/tornado/pull/750
if err.args[0] in (errno.ECONNABORTED, errno.EINVAL):
if errno_from_exception(err) in (errno.ECONNABORTED, errno.EINVAL):
return connection.close()
else:
raise

View File

@ -180,7 +180,7 @@ with ``{# ... #}``.
``{% set *x* = *y* %}``
Sets a local variable.
``{% try %}...{% except %}...{% finally %}...{% else %}...{% end %}``
``{% try %}...{% except %}...{% else %}...{% finally %}...{% end %}``
Same as the python ``try`` statement.
``{% while *condition* %}... {% end %}``
@ -367,10 +367,9 @@ class Loader(BaseLoader):
def _create_template(self, name):
path = os.path.join(self.root, name)
f = open(path, "rb")
template = Template(f.read(), name=name, loader=self)
f.close()
return template
with open(path, "rb") as f:
template = Template(f.read(), name=name, loader=self)
return template
class DictLoader(BaseLoader):

View File

@ -18,7 +18,7 @@ from tornado.log import gen_log
from tornado import netutil
from tornado.stack_context import ExceptionStackContext, NullContext
from tornado.testing import AsyncHTTPTestCase, bind_unused_port, gen_test, ExpectLog
from tornado.test.util import unittest
from tornado.test.util import unittest, skipOnTravis
from tornado.util import u, bytes_type
from tornado.web import Application, RequestHandler, url
@ -110,6 +110,7 @@ class HTTPClientCommonTestCase(AsyncHTTPTestCase):
url("/all_methods", AllMethodsHandler),
], gzip=True)
@skipOnTravis
def test_hello_world(self):
response = self.fetch("/hello")
self.assertEqual(response.code, 200)
@ -355,11 +356,10 @@ Transfer-Encoding: chunked
@gen_test
def test_future_http_error(self):
try:
with self.assertRaises(HTTPError) as context:
yield self.http_client.fetch(self.get_url('/notfound'))
except HTTPError as e:
self.assertEqual(e.code, 404)
self.assertEqual(e.response.code, 404)
self.assertEqual(context.exception.code, 404)
self.assertEqual(context.exception.response.code, 404)
@gen_test
def test_reuse_request_from_response(self):

View File

@ -26,6 +26,7 @@ else:
try:
import twisted
import twisted.names
except ImportError:
twisted = None
else:

View File

@ -182,6 +182,7 @@ three
"""})
try:
loader.load("test.html").generate()
self.fail("did not get expected exception")
except ZeroDivisionError:
self.assertTrue("# test.html:2" in traceback.format_exc())
@ -192,6 +193,7 @@ three{%end%}
"""})
try:
loader.load("test.html").generate()
self.fail("did not get expected exception")
except ZeroDivisionError:
self.assertTrue("# test.html:2" in traceback.format_exc())
@ -202,6 +204,7 @@ three{%end%}
}, namespace={"_tt_modules": ObjectDict({"Template": lambda path, **kwargs: loader.load(path).generate(**kwargs)})})
try:
loader.load("base.html").generate()
self.fail("did not get expected exception")
except ZeroDivisionError:
exc_stack = traceback.format_exc()
self.assertTrue('# base.html:1' in exc_stack)
@ -214,6 +217,7 @@ three{%end%}
})
try:
loader.load("base.html").generate()
self.fail("did not get expected exception")
except ZeroDivisionError:
self.assertTrue("# sub.html:1 (via base.html:1)" in
traceback.format_exc())
@ -225,6 +229,7 @@ three{%end%}
})
try:
loader.load("sub.html").generate()
self.fail("did not get expected exception")
except ZeroDivisionError:
exc_stack = traceback.format_exc()
self.assertTrue("# base.html:1" in exc_stack)
@ -240,6 +245,7 @@ three{%end%}
"""})
try:
loader.load("sub.html").generate()
self.fail("did not get expected exception")
except ZeroDivisionError:
self.assertTrue("# sub.html:4 (via base.html:1)" in
traceback.format_exc())
@ -252,6 +258,7 @@ three{%end%}
})
try:
loader.load("a.html").generate()
self.fail("did not get expected exception")
except ZeroDivisionError:
self.assertTrue("# c.html:1 (via b.html:1, a.html:1)" in
traceback.format_exc())

View File

@ -5,10 +5,13 @@ import sys
# Encapsulate the choice of unittest or unittest2 here.
# To be used as 'from tornado.test.util import unittest'.
if sys.version_info >= (2, 7):
import unittest
else:
if sys.version_info < (2, 7):
# In py26, we must always use unittest2.
import unittest2 as unittest
else:
# Otherwise, use whichever version of unittest was imported in
# tornado.testing.
from tornado.testing import unittest
skipIfNonUnix = unittest.skipIf(os.name != 'posix' or sys.platform == 'cygwin',
"non-unix platform")

View File

@ -49,10 +49,16 @@ except ImportError:
# (either py27+ or unittest2) so tornado.test.util enforces
# this requirement, but for other users of tornado.testing we want
# to allow the older version if unitest2 is not available.
try:
import unittest2 as unittest
except ImportError:
if sys.version_info >= (3,):
# On python 3, mixing unittest2 and unittest (including doctest)
# doesn't seem to work, so always use unittest.
import unittest
else:
# On python 2, prefer unittest2 when available.
try:
import unittest2 as unittest
except ImportError:
import unittest
_next_port = 10000

View File

@ -142,6 +142,24 @@ def exec_in(code, glob, loc=None):
""")
def errno_from_exception(e):
"""Provides the errno from an Exception object.
There are cases that the errno attribute was not set so we pull
the errno out of the args but if someone instatiates an Exception
without any args you will get a tuple error. So this function
abstracts all that behavior to give you a safe way to get the
errno.
"""
if hasattr(e, 'errno'):
return e.errno
elif e.args:
return e.args[0]
else:
return None
class Configurable(object):
"""Base class for configurable interfaces.

View File

@ -594,7 +594,8 @@ class RequestHandler(object):
Note that lists are not converted to JSON because of a potential
cross-site security vulnerability. All JSON output should be
wrapped in a dictionary. More details at
http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
http://haacked.com/archive/2009/06/25/json-hijacking.aspx/ and
https://github.com/facebook/tornado/issues/1009
"""
if self._finished:
raise RuntimeError("Cannot write() after finish(). May be caused "
@ -2381,6 +2382,11 @@ class GZipContentEncoding(OutputTransform):
"""Applies the gzip content encoding to the response.
See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
.. versionchanged:: 3.3
Now compresses all mime types beginning with ``text/``, instead
of just a whitelist. (the whitelist is still used for certain
non-text mime types).
"""
# Whitelist of compressible mime types (in addition to any types
# beginning with "text/").

13
tox.ini
View File

@ -36,6 +36,7 @@ envlist =
py2-opt, py3-opt,
py3-utf8,
py2-locale,
py27-unittest2, py33-unittest2,
# Ensure the sphinx build has no errors or warnings
py2-docs
@ -78,6 +79,12 @@ deps =
pycurl
twisted>=11.0.0
# unittest2 doesn't add anything we need on 2.7, but we should ensure that
# its existence doesn't break anything due to conditional imports.
[testenv:py27-unittest2]
basepython = python2.7
deps = unittest2
# In python 3, opening files in text mode uses a system-dependent encoding by
# default. Run the tests with "C" (ascii) and "utf-8" locales to ensure
# we don't have hidden dependencies on this setting.
@ -104,6 +111,12 @@ setenv = TORNADO_EXTENSION=1
deps =
pycurl>=7.19.3
# See comment on py27-unittest2.
[testenv:py33-unittest2]
basepython = python3.3
setenv = TORNADO_EXTENSION=1
deps = unittest2py3k
[testenv:py34-full]
basepython = python3.4
setenv = TORNADO_EXTENSION=1