Merge branch 'master' into http_refactor
Conflicts: tornado/iostream.py
This commit is contained in:
commit
500a605534
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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``
|
||||
|
|
3
setup.py
3
setup.py
|
@ -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.
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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__":
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -26,6 +26,7 @@ else:
|
|||
|
||||
try:
|
||||
import twisted
|
||||
import twisted.names
|
||||
except ImportError:
|
||||
twisted = None
|
||||
else:
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
13
tox.ini
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue