diff --git a/libs/tornado/__init__.py b/libs/tornado/__init__.py index 41ba7a6..bec636f 100755 --- a/libs/tornado/__init__.py +++ b/libs/tornado/__init__.py @@ -25,5 +25,5 @@ from __future__ import absolute_import, division, print_function, with_statement # is zero for an official release, positive for a development branch, # or negative for a release candidate or beta (after the base version # number has been incremented) -version = "3.2.dev2" -version_info = (3, 2, 0, -99) +version = "3.2b1" +version_info = (3, 2, 0, -98) diff --git a/libs/tornado/auth.py b/libs/tornado/auth.py index a2cef35..f2080f1 100755 --- a/libs/tornado/auth.py +++ b/libs/tornado/auth.py @@ -988,8 +988,8 @@ class GoogleOAuth2Mixin(OAuth2Mixin): }) http.fetch(self._OAUTH_ACCESS_TOKEN_URL, - self.async_callback(self._on_access_token, callback), - method="POST", headers={'Content-Type': 'application/x-www-form-urlencoded'}, body=body) + self.async_callback(self._on_access_token, callback), + method="POST", headers={'Content-Type': 'application/x-www-form-urlencoded'}, body=body) def _on_access_token(self, future, response): """Callback function for the exchange to the access token.""" diff --git a/libs/tornado/concurrent.py b/libs/tornado/concurrent.py index 8a4f228..a9002b1 100755 --- a/libs/tornado/concurrent.py +++ b/libs/tornado/concurrent.py @@ -124,11 +124,11 @@ class TracebackFuture(Future): self.__exc_info = exc_info self.set_exception(exc_info[1]) - def result(self): + def result(self, timeout=None): if self.__exc_info is not None: raise_exc_info(self.__exc_info) else: - return super(TracebackFuture, self).result() + return super(TracebackFuture, self).result(timeout=timeout) class DummyExecutor(object): @@ -151,6 +151,9 @@ def run_on_executor(fn): The decorated method may be called with a ``callback`` keyword argument and returns a future. + + This decorator should be used only on methods of objects with attributes + ``executor`` and ``io_loop``. """ @functools.wraps(fn) def wrapper(self, *args, **kwargs): diff --git a/libs/tornado/curl_httpclient.py b/libs/tornado/curl_httpclient.py index cb97710..0df7a7e 100755 --- a/libs/tornado/curl_httpclient.py +++ b/libs/tornado/curl_httpclient.py @@ -318,10 +318,12 @@ def _curl_setup_request(curl, request, buffer, headers): [native_str("%s: %s" % i) for i in request.headers.items()]) if request.header_callback: - curl.setopt(pycurl.HEADERFUNCTION, request.header_callback) + curl.setopt(pycurl.HEADERFUNCTION, + lambda line: request.header_callback(native_str(line))) else: curl.setopt(pycurl.HEADERFUNCTION, - lambda line: _curl_header_callback(headers, line)) + lambda line: _curl_header_callback(headers, + native_str(line))) if request.streaming_callback: write_function = request.streaming_callback else: diff --git a/libs/tornado/escape.py b/libs/tornado/escape.py index 4e2d20d..302e556 100755 --- a/libs/tornado/escape.py +++ b/libs/tornado/escape.py @@ -189,8 +189,10 @@ def utf8(value): """ if isinstance(value, _UTF8_TYPES): return value - assert isinstance(value, unicode_type), \ - "Expected bytes, unicode, or None; got %r" % type(value) + if not isinstance(value, unicode_type): + raise TypeError( + "Expected bytes, unicode, or None; got %r" % type(value) + ) return value.encode("utf-8") _TO_UNICODE_TYPES = (unicode_type, type(None)) @@ -204,8 +206,10 @@ def to_unicode(value): """ if isinstance(value, _TO_UNICODE_TYPES): return value - assert isinstance(value, bytes_type), \ - "Expected bytes, unicode, or None; got %r" % type(value) + if not isinstance(value, bytes_type): + raise TypeError( + "Expected bytes, unicode, or None; got %r" % type(value) + ) return value.decode("utf-8") # to_unicode was previously named _unicode not because it was private, @@ -233,8 +237,10 @@ def to_basestring(value): """ if isinstance(value, _BASESTRING_TYPES): return value - assert isinstance(value, bytes_type), \ - "Expected bytes, unicode, or None; got %r" % type(value) + if not isinstance(value, bytes_type): + raise TypeError( + "Expected bytes, unicode, or None; got %r" % type(value) + ) return value.decode("utf-8") diff --git a/libs/tornado/gen.py b/libs/tornado/gen.py index 217ebdf..21f692a 100755 --- a/libs/tornado/gen.py +++ b/libs/tornado/gen.py @@ -64,7 +64,7 @@ For more complicated interfaces, `Task` can be split into two parts: def get(self): http_client = AsyncHTTPClient() http_client.fetch("http://example.com", - callback=(yield gen.Callback("key")) + callback=(yield gen.Callback("key"))) response = yield gen.Wait("key") do_something_with_response(response) self.render("template.html") @@ -390,16 +390,26 @@ class YieldFuture(YieldPoint): self.io_loop = io_loop or IOLoop.current() def start(self, runner): - self.runner = runner - self.key = object() - runner.register_callback(self.key) - self.io_loop.add_future(self.future, runner.result_callback(self.key)) + if not self.future.done(): + self.runner = runner + self.key = object() + runner.register_callback(self.key) + self.io_loop.add_future(self.future, runner.result_callback(self.key)) + else: + self.runner = None + self.result = self.future.result() def is_ready(self): - return self.runner.is_ready(self.key) + if self.runner is not None: + return self.runner.is_ready(self.key) + else: + return True def get_result(self): - return self.runner.pop_result(self.key).result() + if self.runner is not None: + return self.runner.pop_result(self.key).result() + else: + return self.result class Multi(YieldPoint): diff --git a/libs/tornado/httpclient.py b/libs/tornado/httpclient.py index b58a834..9b42d40 100755 --- a/libs/tornado/httpclient.py +++ b/libs/tornado/httpclient.py @@ -335,10 +335,11 @@ class HTTPRequest(object): .. versionadded:: 3.1 The ``auth_mode`` argument. """ - if headers is None: - headers = httputil.HTTPHeaders() + # Note that some of these attributes go through property setters + # defined below. + self.headers = headers if if_modified_since: - headers["If-Modified-Since"] = httputil.format_timestamp( + self.headers["If-Modified-Since"] = httputil.format_timestamp( if_modified_since) self.proxy_host = proxy_host self.proxy_port = proxy_port @@ -346,8 +347,7 @@ class HTTPRequest(object): self.proxy_password = proxy_password self.url = url self.method = method - self.headers = headers - self.body = utf8(body) + self.body = body self.auth_username = auth_username self.auth_password = auth_password self.auth_mode = auth_mode @@ -358,9 +358,9 @@ class HTTPRequest(object): self.user_agent = user_agent self.use_gzip = use_gzip self.network_interface = network_interface - self.streaming_callback = stack_context.wrap(streaming_callback) - self.header_callback = stack_context.wrap(header_callback) - self.prepare_curl_callback = stack_context.wrap(prepare_curl_callback) + self.streaming_callback = streaming_callback + self.header_callback = header_callback + self.prepare_curl_callback = prepare_curl_callback self.allow_nonstandard_methods = allow_nonstandard_methods self.validate_cert = validate_cert self.ca_certs = ca_certs @@ -369,6 +369,49 @@ class HTTPRequest(object): self.client_cert = client_cert self.start_time = time.time() + @property + def headers(self): + return self._headers + + @headers.setter + def headers(self, value): + if value is None: + self._headers = httputil.HTTPHeaders() + else: + self._headers = value + + @property + def body(self): + return self._body + + @body.setter + def body(self, value): + self._body = utf8(value) + + @property + def streaming_callback(self): + return self._streaming_callback + + @streaming_callback.setter + def streaming_callback(self, value): + self._streaming_callback = stack_context.wrap(value) + + @property + def header_callback(self): + return self._header_callback + + @header_callback.setter + def header_callback(self, value): + self._header_callback = stack_context.wrap(value) + + @property + def prepare_curl_callback(self): + return self._prepare_curl_callback + + @prepare_curl_callback.setter + def prepare_curl_callback(self, value): + self._prepare_curl_callback = stack_context.wrap(value) + class HTTPResponse(object): """HTTP Response object. diff --git a/libs/tornado/ioloop.py b/libs/tornado/ioloop.py index a36ab7a..0477ade 100755 --- a/libs/tornado/ioloop.py +++ b/libs/tornado/ioloop.py @@ -598,100 +598,103 @@ class PollIOLoop(IOLoop): except ValueError: # non-main thread pass - while True: - poll_timeout = _POLL_TIMEOUT - - # Prevent IO event starvation by delaying new callbacks - # to the next iteration of the event loop. - with self._callback_lock: - callbacks = self._callbacks - self._callbacks = [] - for callback in callbacks: - self._run_callback(callback) - # Closures may be holding on to a lot of memory, so allow - # them to be freed before we go into our poll wait. - callbacks = callback = None - - if self._timeouts: - now = self.time() - while self._timeouts: - if self._timeouts[0].callback is None: - # the timeout was cancelled - heapq.heappop(self._timeouts) - self._cancellations -= 1 - elif self._timeouts[0].deadline <= now: - timeout = heapq.heappop(self._timeouts) - self._run_callback(timeout.callback) - del timeout - else: - seconds = self._timeouts[0].deadline - now - poll_timeout = min(seconds, poll_timeout) - break - if (self._cancellations > 512 - and self._cancellations > (len(self._timeouts) >> 1)): - # Clean up the timeout queue when it gets large and it's - # more than half cancellations. - self._cancellations = 0 - self._timeouts = [x for x in self._timeouts - if x.callback is not None] - heapq.heapify(self._timeouts) - - if self._callbacks: - # If any callbacks or timeouts called add_callback, - # we don't want to wait in poll() before we run them. - poll_timeout = 0.0 - - if not self._running: - break - - if self._blocking_signal_threshold is not None: - # clear alarm so it doesn't fire while poll is waiting for - # events. - signal.setitimer(signal.ITIMER_REAL, 0, 0) - - try: - event_pairs = self._impl.poll(poll_timeout) - except Exception as e: - # Depending on python version and IOLoop implementation, - # different exception types may be thrown and there are - # 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)): - continue - else: - raise + try: + while True: + poll_timeout = _POLL_TIMEOUT + + # Prevent IO event starvation by delaying new callbacks + # to the next iteration of the event loop. + with self._callback_lock: + callbacks = self._callbacks + self._callbacks = [] + for callback in callbacks: + self._run_callback(callback) + # Closures may be holding on to a lot of memory, so allow + # them to be freed before we go into our poll wait. + callbacks = callback = None + + if self._timeouts: + now = self.time() + while self._timeouts: + if self._timeouts[0].callback is None: + # the timeout was cancelled + heapq.heappop(self._timeouts) + self._cancellations -= 1 + elif self._timeouts[0].deadline <= now: + timeout = heapq.heappop(self._timeouts) + self._run_callback(timeout.callback) + del timeout + else: + seconds = self._timeouts[0].deadline - now + poll_timeout = min(seconds, poll_timeout) + break + if (self._cancellations > 512 + and self._cancellations > (len(self._timeouts) >> 1)): + # Clean up the timeout queue when it gets large and it's + # more than half cancellations. + self._cancellations = 0 + self._timeouts = [x for x in self._timeouts + if x.callback is not None] + heapq.heapify(self._timeouts) + + if self._callbacks: + # If any callbacks or timeouts called add_callback, + # we don't want to wait in poll() before we run them. + poll_timeout = 0.0 + + if not self._running: + break + + if self._blocking_signal_threshold is not None: + # clear alarm so it doesn't fire while poll is waiting for + # events. + signal.setitimer(signal.ITIMER_REAL, 0, 0) - if self._blocking_signal_threshold is not None: - signal.setitimer(signal.ITIMER_REAL, - self._blocking_signal_threshold, 0) - - # Pop one fd at a time from the set of pending fds and run - # its handler. Since that handler may perform actions on - # other file descriptors, there may be reentrant calls to - # this IOLoop that update self._events - self._events.update(event_pairs) - while self._events: - fd, events = self._events.popitem() try: - self._handlers[fd](fd, events) - except (OSError, IOError) as e: - if e.args[0] == errno.EPIPE: - # Happens when the client closes the connection - pass + event_pairs = self._impl.poll(poll_timeout) + except Exception as e: + # Depending on python version and IOLoop implementation, + # different exception types may be thrown and there are + # 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)): + continue else: + raise + + if self._blocking_signal_threshold is not None: + signal.setitimer(signal.ITIMER_REAL, + self._blocking_signal_threshold, 0) + + # Pop one fd at a time from the set of pending fds and run + # its handler. Since that handler may perform actions on + # other file descriptors, there may be reentrant calls to + # this IOLoop that update self._events + self._events.update(event_pairs) + while self._events: + fd, events = self._events.popitem() + try: + self._handlers[fd](fd, events) + except (OSError, IOError) as e: + if e.args[0] == errno.EPIPE: + # Happens when the client closes the connection + pass + else: + self.handle_callback_exception(self._handlers.get(fd)) + except Exception: self.handle_callback_exception(self._handlers.get(fd)) - except Exception: - self.handle_callback_exception(self._handlers.get(fd)) - # reset the stopped flag so another start/stop pair can be issued - self._stopped = False - if self._blocking_signal_threshold is not None: - signal.setitimer(signal.ITIMER_REAL, 0, 0) - IOLoop._current.instance = old_current - if old_wakeup_fd is not None: - signal.set_wakeup_fd(old_wakeup_fd) + + finally: + # reset the stopped flag so another start/stop pair can be issued + self._stopped = False + if self._blocking_signal_threshold is not None: + signal.setitimer(signal.ITIMER_REAL, 0, 0) + IOLoop._current.instance = old_current + if old_wakeup_fd is not None: + signal.set_wakeup_fd(old_wakeup_fd) def stop(self): self._running = False diff --git a/libs/tornado/iostream.py b/libs/tornado/iostream.py index 08430ce..5d4d08a 100755 --- a/libs/tornado/iostream.py +++ b/libs/tornado/iostream.py @@ -55,6 +55,7 @@ _ERRNO_WOULDBLOCK = (errno.EWOULDBLOCK, errno.EAGAIN) # They should be caught and handled less noisily than other errors. _ERRNO_CONNRESET = (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE) + class StreamClosedError(IOError): """Exception raised by `IOStream` methods when the stream is closed. @@ -738,7 +739,7 @@ class IOStream(BaseIOStream): # 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): + e.args[0] not in _ERRNO_WOULDBLOCK): gen_log.warning("Connect error on fd %d: %s", self.socket.fileno(), e) self.close(exc_info=True) diff --git a/libs/tornado/locale.py b/libs/tornado/locale.py index 310a517..07c6d58 100755 --- a/libs/tornado/locale.py +++ b/libs/tornado/locale.py @@ -286,8 +286,6 @@ class Locale(object): This method is primarily intended for dates in the past. For dates in the future, we fall back to full format. """ - if self.code.startswith("ru"): - relative = False if isinstance(date, numbers.Real): date = datetime.datetime.utcfromtimestamp(date) now = datetime.datetime.utcnow() diff --git a/libs/tornado/log.py b/libs/tornado/log.py index 648db5c..bc6898c 100755 --- a/libs/tornado/log.py +++ b/libs/tornado/log.py @@ -33,7 +33,6 @@ from __future__ import absolute_import, division, print_function, with_statement import logging import logging.handlers import sys -import time from tornado.escape import _unicode from tornado.util import unicode_type, basestring_type @@ -74,8 +73,21 @@ class LogFormatter(logging.Formatter): `tornado.options.parse_command_line` (unless ``--logging=none`` is used). """ - def __init__(self, color=True, *args, **kwargs): - logging.Formatter.__init__(self, *args, **kwargs) + DEFAULT_PREFIX_FORMAT = '[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]' + DEFAULT_DATE_FORMAT = '%y%m%d %H:%M:%S' + + def __init__(self, color=True, prefix_fmt=None, datefmt=None): + r""" + :arg bool color: Enables color support + :arg string prefix_fmt: Log message prefix format. + Prefix is a part of the log message, directly preceding the actual + message text. + :arg string datefmt: Datetime format. + Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``. + """ + self.__prefix_fmt = prefix_fmt if prefix_fmt is not None else self.DEFAULT_PREFIX_FORMAT + datefmt = datefmt if datefmt is not None else self.DEFAULT_DATE_FORMAT + logging.Formatter.__init__(self, datefmt=datefmt) self._color = color and _stderr_supports_color() if self._color: # The curses module has some str/bytes confusion in @@ -107,10 +119,8 @@ class LogFormatter(logging.Formatter): except Exception as e: record.message = "Bad message (%r): %r" % (e, record.__dict__) assert isinstance(record.message, basestring_type) # guaranteed by logging - record.asctime = time.strftime( - "%y%m%d %H:%M:%S", self.converter(record.created)) - prefix = '[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]' % \ - record.__dict__ + record.asctime = self.formatTime(record, self.datefmt) + prefix = self.__prefix_fmt % record.__dict__ if self._color: prefix = (self._colors.get(record.levelno, self._normal) + prefix + self._normal) diff --git a/libs/tornado/netutil.py b/libs/tornado/netutil.py index 21db475..8ebe604 100755 --- a/libs/tornado/netutil.py +++ b/libs/tornado/netutil.py @@ -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 Configurable +from tornado.util import u, Configurable if hasattr(ssl, 'match_hostname') and hasattr(ssl, 'CertificateError'): # python 3.2+ ssl_match_hostname = ssl.match_hostname @@ -37,6 +37,14 @@ else: ssl_match_hostname = backports.ssl_match_hostname.match_hostname SSLCertificateError = backports.ssl_match_hostname.CertificateError +# ThreadedResolver runs getaddrinfo on a thread. If the hostname is unicode, +# getaddrinfo attempts to import encodings.idna. If this is done at +# module-import time, the import lock is already held by the main thread, +# leading to deadlock. Avoid it by caching the idna encoder on the main +# thread now. +u('foo').encode('idna') + + def bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=128, flags=None): """Creates listening sockets bound to the given port and address. diff --git a/libs/tornado/platform/asyncio.py b/libs/tornado/platform/asyncio.py index a8f5bad..09b2bf3 100644 --- a/libs/tornado/platform/asyncio.py +++ b/libs/tornado/platform/asyncio.py @@ -16,6 +16,7 @@ import os from tornado.ioloop import IOLoop from tornado import stack_context + class BaseAsyncIOLoop(IOLoop): def initialize(self, asyncio_loop, close_loop=False): self.asyncio_loop = asyncio_loop @@ -104,7 +105,7 @@ class BaseAsyncIOLoop(IOLoop): else: raise TypeError("Unsupported deadline %r", deadline) return self.asyncio_loop.call_later(delay, self._run_callback, - stack_context.wrap(callback)) + stack_context.wrap(callback)) def remove_timeout(self, timeout): timeout.cancel() @@ -114,8 +115,8 @@ class BaseAsyncIOLoop(IOLoop): raise RuntimeError("IOLoop is closing") if kwargs: self.asyncio_loop.call_soon_threadsafe(functools.partial( - self._run_callback, stack_context.wrap(callback), - *args, **kwargs)) + self._run_callback, stack_context.wrap(callback), + *args, **kwargs)) else: self.asyncio_loop.call_soon_threadsafe( self._run_callback, stack_context.wrap(callback), *args) @@ -128,6 +129,7 @@ class AsyncIOMainLoop(BaseAsyncIOLoop): super(AsyncIOMainLoop, self).initialize(asyncio.get_event_loop(), close_loop=False) + class AsyncIOLoop(BaseAsyncIOLoop): def initialize(self): super(AsyncIOLoop, self).initialize(asyncio.new_event_loop(), diff --git a/libs/tornado/platform/twisted.py b/libs/tornado/platform/twisted.py index c0ee595..86ef71b 100755 --- a/libs/tornado/platform/twisted.py +++ b/libs/tornado/platform/twisted.py @@ -527,8 +527,10 @@ class TwistedResolver(Resolver): resolved_family = socket.AF_INET6 else: deferred = self.resolver.getHostByName(utf8(host)) - resolved = yield gen.Task(deferred.addCallback) - if twisted.internet.abstract.isIPAddress(resolved): + resolved = yield gen.Task(deferred.addBoth) + if isinstance(resolved, failure.Failure): + resolved.raiseException() + elif twisted.internet.abstract.isIPAddress(resolved): resolved_family = socket.AF_INET elif twisted.internet.abstract.isIPv6Address(resolved): resolved_family = socket.AF_INET6 diff --git a/libs/tornado/simple_httpclient.py b/libs/tornado/simple_httpclient.py index 2558ada..73bfee8 100755 --- a/libs/tornado/simple_httpclient.py +++ b/libs/tornado/simple_httpclient.py @@ -94,9 +94,9 @@ class SimpleAsyncHTTPClient(AsyncHTTPClient): self.queue.append((key, request, callback)) if not len(self.active) < self.max_clients: timeout_handle = self.io_loop.add_timeout( - self.io_loop.time() + min(request.connect_timeout, - request.request_timeout), - functools.partial(self._on_timeout, key)) + self.io_loop.time() + min(request.connect_timeout, + request.request_timeout), + functools.partial(self._on_timeout, key)) else: timeout_handle = None self.waiting[key] = (request, callback, timeout_handle) @@ -136,8 +136,8 @@ class SimpleAsyncHTTPClient(AsyncHTTPClient): request, callback, timeout_handle = self.waiting[key] self.queue.remove((key, request, callback)) timeout_response = HTTPResponse( - request, 599, error=HTTPError(599, "Timeout"), - request_time=self.io_loop.time() - request.start_time) + request, 599, error=HTTPError(599, "Timeout"), + request_time=self.io_loop.time() - request.start_time) self.io_loop.add_callback(callback, timeout_response) del self.waiting[key] diff --git a/libs/tornado/template.py b/libs/tornado/template.py index 77fd579..db5a528 100755 --- a/libs/tornado/template.py +++ b/libs/tornado/template.py @@ -21,7 +21,7 @@ Basic usage looks like:: t = template.Template("{{ myvalue }}") print t.generate(myvalue="XXX") -Loader is a class that loads templates from a root directory and caches +`Loader` is a class that loads templates from a root directory and caches the compiled templates:: loader = template.Loader("/home/btaylor") @@ -56,16 +56,17 @@ interesting. Syntax for the templates:: {% end %} Unlike most other template systems, we do not put any restrictions on the -expressions you can include in your statements. if and for blocks get -translated exactly into Python, you can do complex expressions like:: +expressions you can include in your statements. ``if`` and ``for`` blocks get +translated exactly into Python, so you can do complex expressions like:: {% for student in [p for p in people if p.student and p.age > 23] %}
  • {{ escape(student.name) }}
  • {% end %} Translating directly to Python means you can apply functions to expressions -easily, like the escape() function in the examples above. You can pass -functions in to your template just like any other variable:: +easily, like the ``escape()`` function in the examples above. You can pass +functions in to your template just like any other variable +(In a `.RequestHandler`, override `.RequestHandler.get_template_namespace`):: ### Python code def add(x, y): @@ -75,8 +76,8 @@ functions in to your template just like any other variable:: ### The template {{ add(1, 2) }} -We provide the functions escape(), url_escape(), json_encode(), and squeeze() -to all templates by default. +We provide the functions `escape() <.xhtml_escape>`, `.url_escape()`, +`.json_encode()`, and `.squeeze()` to all templates by default. Typical applications do not create `Template` or `Loader` instances by hand, but instead use the `~.RequestHandler.render` and diff --git a/libs/tornado/web.py b/libs/tornado/web.py index b6d7e97..65a76cd 100755 --- a/libs/tornado/web.py +++ b/libs/tornado/web.py @@ -447,7 +447,11 @@ class RequestHandler(object): The name of the argument is provided if known, but may be None (e.g. for unnamed groups in the url regex). """ - return _unicode(value) + try: + return _unicode(value) + except UnicodeDecodeError: + raise HTTPError(400, "Invalid unicode in %s: %r" % + (name or "url", value[:40])) @property def cookies(self): @@ -1908,7 +1912,7 @@ class StaticFileHandler(RequestHandler): # content, or when a suffix with length 0 is specified self.set_status(416) # Range Not Satisfiable self.set_header("Content-Type", "text/plain") - self.set_header("Content-Range", "bytes */%s" %(size, )) + self.set_header("Content-Range", "bytes */%s" % (size, )) return if start is not None and start < 0: start += size diff --git a/libs/tornado/websocket.py b/libs/tornado/websocket.py index 8c2f5a6..9bec9bb 100755 --- a/libs/tornado/websocket.py +++ b/libs/tornado/websocket.py @@ -381,12 +381,12 @@ class WebSocketProtocol76(WebSocketProtocol): "Sec-WebSocket-Location: %(scheme)s://%(host)s%(uri)s\r\n" "%(subprotocol)s" "\r\n" % (dict( - version=tornado.version, - origin=self.request.headers["Origin"], - scheme=scheme, - host=self.request.host, - uri=self.request.uri, - subprotocol=subprotocol_header)))) + version=tornado.version, + origin=self.request.headers["Origin"], + scheme=scheme, + host=self.request.host, + uri=self.request.uri, + subprotocol=subprotocol_header)))) self.stream.read_bytes(8, self._handle_challenge) def challenge_response(self, challenge): @@ -891,6 +891,7 @@ def websocket_connect(url, io_loop=None, callback=None, connect_timeout=None): io_loop.add_future(conn.connect_future, callback) return conn.connect_future + def _websocket_mask_python(mask, data): """Websocket masking function. diff --git a/libs/tornado/wsgi.py b/libs/tornado/wsgi.py index 8e5dded..615f2e1 100755 --- a/libs/tornado/wsgi.py +++ b/libs/tornado/wsgi.py @@ -294,7 +294,7 @@ class WSGIContainer(object): "REQUEST_METHOD": request.method, "SCRIPT_NAME": "", "PATH_INFO": to_wsgi_str(escape.url_unescape( - request.path, encoding=None, plus=False)), + request.path, encoding=None, plus=False)), "QUERY_STRING": request.query, "REMOTE_ADDR": request.remote_ip, "SERVER_NAME": host,