From c152b784481bac99bb8395021382ae498ca9fb1f Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Thu, 9 Feb 2012 00:55:27 -0800 Subject: [PATCH] While I'm touching every file, run autopep8 too. Might as well get all the merge headaches over with at once :) Ran with $ autopep8 --ignore=E111,W602 i tornado/*.py tornado/platform/*.py tornado/test/*.py --- tornado/auth.py | 77 ++++++++++++++--------- tornado/autoreload.py | 20 ++++-- tornado/curl_httpclient.py | 13 ++-- tornado/database.py | 3 +- tornado/escape.py | 22 +++++-- tornado/gen.py | 48 +++++++++++---- tornado/httpclient.py | 23 ++++--- tornado/httpserver.py | 19 +++--- tornado/httputil.py | 10 ++- tornado/ioloop.py | 11 ++-- tornado/iostream.py | 10 +-- tornado/locale.py | 33 ++++++---- tornado/netutil.py | 9 ++- tornado/options.py | 24 ++++---- tornado/platform/interface.py | 6 +- tornado/platform/posix.py | 8 ++- tornado/platform/twisted.py | 5 +- tornado/platform/windows.py | 3 +- tornado/process.py | 13 +++- tornado/simple_httpclient.py | 12 ++-- tornado/stack_context.py | 11 +++- tornado/template.py | 23 ++++--- tornado/test/auth_test.py | 14 ++++- tornado/test/curl_httpclient_test.py | 1 + tornado/test/escape_test.py | 6 +- tornado/test/gen_test.py | 16 +++-- tornado/test/httpclient_test.py | 10 ++- tornado/test/httpserver_test.py | 40 +++++++++--- tornado/test/httputil_test.py | 17 +++--- tornado/test/import_test.py | 1 + tornado/test/ioloop_test.py | 1 + tornado/test/iostream_test.py | 14 ++++- tornado/test/process_test.py | 11 +++- tornado/test/run_pyversion_tests.py | 2 + tornado/test/runtests.py | 1 + tornado/test/simple_httpclient_test.py | 8 +++ tornado/test/stack_context_test.py | 5 ++ tornado/test/template_test.py | 28 ++++++--- tornado/test/testing_test.py | 4 +- tornado/test/twisted_test.py | 32 +++++++++- tornado/test/web_test.py | 33 +++++++--- tornado/test/wsgi_test.py | 3 + tornado/testing.py | 5 ++ tornado/util.py | 2 + tornado/web.py | 85 ++++++++++++++++---------- tornado/websocket.py | 18 +++--- tornado/wsgi.py | 18 +++--- 47 files changed, 538 insertions(+), 240 deletions(-) diff --git a/tornado/auth.py b/tornado/auth.py index e20685496..65be566bd 100644 --- a/tornado/auth.py +++ b/tornado/auth.py @@ -61,13 +61,14 @@ from tornado import escape from tornado.httputil import url_concat from tornado.util import bytes_type, b + class OpenIdMixin(object): """Abstract implementation of OpenID and Attribute Exchange. See GoogleMixin below for example implementations. """ def authenticate_redirect(self, callback_uri=None, - ax_attrs=["name","email","language","username"]): + ax_attrs=["name", "email", "language", "username"]): """Returns the authentication URL for this service. After authentication, the service will redirect back to the given @@ -93,7 +94,8 @@ class OpenIdMixin(object): args = dict((k, v[-1]) for k, v in self.request.arguments.iteritems()) args["openid.mode"] = u"check_authentication" url = self._OPENID_ENDPOINT - if http_client is None: http_client = httpclient.AsyncHTTPClient() + if http_client is None: + http_client = httpclient.AsyncHTTPClient() http_client.fetch(url, self.async_callback( self._on_authentication_verified, callback), method="POST", body=urllib.urlencode(args)) @@ -160,8 +162,10 @@ class OpenIdMixin(object): self.get_argument(name) == u"http://openid.net/srv/ax/1.0": ax_ns = name[10:] break + def get_ax_arg(uri): - if not ax_ns: return u"" + if not ax_ns: + return u"" prefix = "openid." + ax_ns + ".type." ax_name = None for name in self.request.arguments.iterkeys(): @@ -169,7 +173,8 @@ class OpenIdMixin(object): part = name[len(prefix):] ax_name = "openid." + ax_ns + ".value." + part break - if not ax_name: return u"" + if not ax_name: + return u"" return self.get_argument(ax_name, u"") email = get_ax_arg("http://axschema.org/contact/email") @@ -192,9 +197,12 @@ class OpenIdMixin(object): user["name"] = u" ".join(name_parts) elif email: user["name"] = email.split("@")[0] - if email: user["email"] = email - if locale: user["locale"] = locale - if username: user["username"] = username + if email: + user["email"] = email + if locale: + user["locale"] = locale + if username: + user["username"] = username callback(user) @@ -237,7 +245,6 @@ class OAuthMixin(object): self._on_request_token, self._OAUTH_AUTHORIZE_URL, callback_uri)) - def get_authenticated_user(self, callback, http_client=None): """Gets the OAuth authorized user and access token on callback. @@ -271,7 +278,7 @@ class OAuthMixin(object): http_client.fetch(self._oauth_access_token_url(token), self.async_callback(self._on_access_token, callback)) - def _oauth_request_token_url(self, callback_uri= None, extra_params=None): + def _oauth_request_token_url(self, callback_uri=None, extra_params=None): consumer_token = self._oauth_consumer_token() url = self._OAUTH_REQUEST_TOKEN_URL args = dict( @@ -285,7 +292,8 @@ class OAuthMixin(object): if callback_uri: args["oauth_callback"] = urlparse.urljoin( self.request.full_url(), callback_uri) - if extra_params: args.update(extra_params) + if extra_params: + args.update(extra_params) signature = _oauth10a_signature(consumer_token, "GET", url, args) else: signature = _oauth_signature(consumer_token, "GET", url, args) @@ -318,7 +326,7 @@ class OAuthMixin(object): oauth_version=getattr(self, "_OAUTH_VERSION", "1.0a"), ) if "verifier" in request_token: - args["oauth_verifier"]=request_token["verifier"] + args["oauth_verifier"] = request_token["verifier"] if getattr(self, "_OAUTH_VERSION", "1.0a") == "1.0a": signature = _oauth10a_signature(consumer_token, "GET", url, args, @@ -378,11 +386,12 @@ class OAuthMixin(object): base_args["oauth_signature"] = signature return base_args + class OAuth2Mixin(object): """Abstract implementation of OAuth v 2.""" def authorize_redirect(self, redirect_uri=None, client_id=None, - client_secret=None, extra_params=None ): + client_secret=None, extra_params=None): """Redirects the user to obtain OAuth authorization for this service. Some providers require that you register a Callback @@ -395,11 +404,12 @@ class OAuth2Mixin(object): "redirect_uri": redirect_uri, "client_id": client_id } - if extra_params: args.update(extra_params) + if extra_params: + args.update(extra_params) self.redirect( url_concat(self._OAUTH_AUTHORIZE_URL, args)) - def _oauth_request_token_url(self, redirect_uri= None, client_id = None, + def _oauth_request_token_url(self, redirect_uri=None, client_id=None, client_secret=None, code=None, extra_params=None): url = self._OAUTH_ACCESS_TOKEN_URL @@ -409,9 +419,11 @@ class OAuth2Mixin(object): client_id=client_id, client_secret=client_secret, ) - if extra_params: args.update(extra_params) + if extra_params: + args.update(extra_params) return url_concat(url, args) + class TwitterMixin(OAuthMixin): """Twitter OAuth authentication. @@ -452,15 +464,14 @@ class TwitterMixin(OAuthMixin): _OAUTH_AUTHENTICATE_URL = "http://api.twitter.com/oauth/authenticate" _OAUTH_NO_CALLBACKS = False - - def authenticate_redirect(self, callback_uri = None): + def authenticate_redirect(self, callback_uri=None): """Just like authorize_redirect(), but auto-redirects if authorized. This is generally the right interface to use if you are using Twitter for single-sign on. """ http = httpclient.AsyncHTTPClient() - http.fetch(self._oauth_request_token_url(callback_uri = callback_uri), self.async_callback( + http.fetch(self._oauth_request_token_url(callback_uri=callback_uri), self.async_callback( self._on_request_token, self._OAUTH_AUTHENTICATE_URL, None)) def twitter_request(self, path, callback, access_token=None, @@ -516,7 +527,8 @@ class TwitterMixin(OAuthMixin): oauth = self._oauth_request_parameters( url, access_token, all_args, method=method) args.update(oauth) - if args: url += "?" + urllib.urlencode(args) + if args: + url += "?" + urllib.urlencode(args) callback = self.async_callback(self._on_twitter_request, callback) http = httpclient.AsyncHTTPClient() if post_args is not None: @@ -592,7 +604,6 @@ class FriendFeedMixin(OAuthMixin): _OAUTH_NO_CALLBACKS = True _OAUTH_VERSION = "1.0" - def friendfeed_request(self, path, callback, access_token=None, post_args=None, **args): """Fetches the given relative API path, e.g., "/bret/friends" @@ -638,7 +649,8 @@ class FriendFeedMixin(OAuthMixin): oauth = self._oauth_request_parameters( url, access_token, all_args, method=method) args.update(oauth) - if args: url += "?" + urllib.urlencode(args) + if args: + url += "?" + urllib.urlencode(args) callback = self.async_callback(self._on_friendfeed_request, callback) http = httpclient.AsyncHTTPClient() if post_args is not None: @@ -703,7 +715,7 @@ class GoogleMixin(OpenIdMixin, OAuthMixin): _OAUTH_ACCESS_TOKEN_URL = "https://www.google.com/accounts/OAuthGetAccessToken" def authorize_redirect(self, oauth_scope, callback_uri=None, - ax_attrs=["name","email","language","username"]): + ax_attrs=["name", "email", "language", "username"]): """Authenticates and authorizes for the given Google resource. Some of the available resources are: @@ -748,6 +760,7 @@ class GoogleMixin(OpenIdMixin, OAuthMixin): def _oauth_get_user(self, access_token, callback): OpenIdMixin.get_authenticated_user(self, callback) + class FacebookMixin(object): """Facebook Connect authentication. @@ -928,9 +941,11 @@ class FacebookMixin(object): def _signature(self, args): parts = ["%s=%s" % (n, args[n]) for n in sorted(args.keys())] body = "".join(parts) + self.settings["facebook_secret"] - if isinstance(body, unicode): body = body.encode("utf-8") + if isinstance(body, unicode): + body = body.encode("utf-8") return hashlib.md5(body).hexdigest() + class FacebookGraphMixin(OAuth2Mixin): """Facebook authentication using the new Graph API and OAuth2.""" _OAUTH_ACCESS_TOKEN_URL = "https://graph.facebook.com/oauth/access_token?" @@ -974,7 +989,8 @@ class FacebookGraphMixin(OAuth2Mixin): fields = set(['id', 'name', 'first_name', 'last_name', 'locale', 'picture', 'link']) - if extra_fields: fields.update(extra_fields) + if extra_fields: + fields.update(extra_fields) http.fetch(self._oauth_request_token_url(**args), self.async_callback(self._on_access_token, redirect_uri, client_id, @@ -1001,7 +1017,6 @@ class FacebookGraphMixin(OAuth2Mixin): fields=",".join(fields) ) - def _on_get_user_info(self, callback, session, fields, user): if user is None: callback(None) @@ -1055,7 +1070,8 @@ class FacebookGraphMixin(OAuth2Mixin): all_args["access_token"] = access_token all_args.update(args) all_args.update(post_args or {}) - if all_args: url += "?" + urllib.urlencode(all_args) + if all_args: + url += "?" + urllib.urlencode(all_args) callback = self.async_callback(self._on_facebook_request, callback) http = httpclient.AsyncHTTPClient() if post_args is not None: @@ -1072,6 +1088,7 @@ class FacebookGraphMixin(OAuth2Mixin): return callback(escape.json_decode(response.body)) + def _oauth_signature(consumer_token, method, url, parameters={}, token=None): """Calculates the HMAC-SHA1 OAuth signature for the given request. @@ -1086,7 +1103,7 @@ def _oauth_signature(consumer_token, method, url, parameters={}, token=None): base_elems.append(normalized_url) base_elems.append("&".join("%s=%s" % (k, _oauth_escape(str(v))) for k, v in sorted(parameters.items()))) - base_string = "&".join(_oauth_escape(e) for e in base_elems) + base_string = "&".join(_oauth_escape(e) for e in base_elems) key_elems = [escape.utf8(consumer_token["secret"])] key_elems.append(escape.utf8(token["secret"] if token else "")) @@ -1095,6 +1112,7 @@ def _oauth_signature(consumer_token, method, url, parameters={}, token=None): hash = hmac.new(key, escape.utf8(base_string), hashlib.sha1) return binascii.b2a_base64(hash.digest())[:-1] + def _oauth10a_signature(consumer_token, method, url, parameters={}, token=None): """Calculates the HMAC-SHA1 OAuth 1.0a signature for the given request. @@ -1110,7 +1128,7 @@ def _oauth10a_signature(consumer_token, method, url, parameters={}, token=None): base_elems.append("&".join("%s=%s" % (k, _oauth_escape(str(v))) for k, v in sorted(parameters.items()))) - base_string = "&".join(_oauth_escape(e) for e in base_elems) + base_string = "&".join(_oauth_escape(e) for e in base_elems) key_elems = [escape.utf8(urllib.quote(consumer_token["secret"], safe='~'))] key_elems.append(escape.utf8(urllib.quote(token["secret"], safe='~') if token else "")) key = b("&").join(key_elems) @@ -1118,6 +1136,7 @@ def _oauth10a_signature(consumer_token, method, url, parameters={}, token=None): hash = hmac.new(key, escape.utf8(base_string), hashlib.sha1) return binascii.b2a_base64(hash.digest())[:-1] + def _oauth_escape(val): if isinstance(val, unicode): val = val.encode("utf-8") @@ -1132,5 +1151,3 @@ def _oauth_parse_response(body): special = (b("oauth_token"), b("oauth_token_secret")) token.update((k, p[k][0]) for k in p if k not in special) return token - - diff --git a/tornado/autoreload.py b/tornado/autoreload.py index 2464f3468..7da8df888 100644 --- a/tornado/autoreload.py +++ b/tornado/autoreload.py @@ -44,6 +44,7 @@ try: except ImportError: signal = None + def start(io_loop=None, check_time=500): """Restarts the process automatically when a module is modified. @@ -57,6 +58,7 @@ def start(io_loop=None, check_time=500): scheduler = ioloop.PeriodicCallback(callback, check_time, io_loop=io_loop) scheduler.start() + def wait(): """Wait for a watched file to change, then restart the process. @@ -70,6 +72,7 @@ def wait(): _watched_files = set() + def watch(filename): """Add a file to the watch list. @@ -79,6 +82,7 @@ def watch(filename): _reload_hooks = [] + def add_reload_hook(fn): """Add a function to be called before reloading the process. @@ -89,6 +93,7 @@ def add_reload_hook(fn): """ _reload_hooks.append(fn) + def _close_all_fds(io_loop): for fd in io_loop._handlers.keys(): try: @@ -98,6 +103,7 @@ def _close_all_fds(io_loop): _reload_attempted = False + def _reload_on_update(modify_times): if _reload_attempted: # We already tried to reload and it didn't work, so don't try again. @@ -112,15 +118,18 @@ def _reload_on_update(modify_times): # in the standard library), and occasionally this can cause strange # failures in getattr. Just ignore anything that's not an ordinary # module. - if not isinstance(module, types.ModuleType): continue + if not isinstance(module, types.ModuleType): + continue path = getattr(module, "__file__", None) - if not path: continue + if not path: + continue if path.endswith(".pyc") or path.endswith(".pyo"): path = path[:-1] _check_file(modify_times, path) for path in _watched_files: _check_file(modify_times, path) + def _check_file(modify_times, path): try: modified = os.stat(path).st_mtime @@ -133,6 +142,7 @@ def _check_file(modify_times, path): logging.info("%s modified; restarting server", path) _reload() + def _reload(): global _reload_attempted _reload_attempted = True @@ -173,9 +183,11 @@ Usage: python -m tornado.autoreload -m module.to.run [args...] python -m tornado.autoreload path/to/script.py [args...] """ + + def main(): """Command-line wrapper to re-run a script whenever its source changes. - + Scripts may be specified by filename or module name:: python -m tornado.autoreload -m tornado.test.runtests @@ -229,7 +241,7 @@ def main(): watch(pkgutil.get_loader(module).get_filename()) wait() - + if __name__ == "__main__": # If this module is run with "python -m tornado.autoreload", the current diff --git a/tornado/curl_httpclient.py b/tornado/curl_httpclient.py index c6213c05d..c3903e9bf 100644 --- a/tornado/curl_httpclient.py +++ b/tornado/curl_httpclient.py @@ -32,6 +32,7 @@ from tornado import stack_context from tornado.escape import utf8 from tornado.httpclient import HTTPRequest, HTTPResponse, HTTPError, AsyncHTTPClient, main + class CurlAsyncHTTPClient(AsyncHTTPClient): def initialize(self, io_loop=None, max_clients=10, max_simultaneous_connections=None): @@ -109,15 +110,17 @@ class CurlAsyncHTTPClient(AsyncHTTPClient): if self._timeout is not None: self.io_loop.remove_timeout(self._timeout) self._timeout = self.io_loop.add_timeout( - time.time() + msecs/1000.0, self._handle_timeout) + time.time() + msecs / 1000.0, self._handle_timeout) def _handle_events(self, fd, events): """Called by IOLoop when there is activity on one of our file descriptors. """ action = 0 - if events & ioloop.IOLoop.READ: action |= pycurl.CSELECT_IN - if events & ioloop.IOLoop.WRITE: action |= pycurl.CSELECT_OUT + if events & ioloop.IOLoop.READ: + action |= pycurl.CSELECT_IN + if events & ioloop.IOLoop.WRITE: + action |= pycurl.CSELECT_OUT while True: try: ret, num_handles = self._socket_action(fd, action) @@ -250,7 +253,6 @@ class CurlAsyncHTTPClient(AsyncHTTPClient): except Exception: self.handle_callback_exception(info["callback"]) - def handle_callback_exception(self, callback): self.io_loop.handle_callback_exception(callback) @@ -372,7 +374,7 @@ def _curl_setup_request(curl, request, buffer, headers): # Handle curl's cryptic options for every individual HTTP method if request.method in ("POST", "PUT"): - request_buffer = cStringIO.StringIO(utf8(request.body)) + request_buffer = cStringIO.StringIO(utf8(request.body)) curl.setopt(pycurl.READFUNCTION, request_buffer.read) if request.method == "POST": def ioctl(cmd): @@ -420,6 +422,7 @@ def _curl_header_callback(headers, header_line): return headers.parse_line(header_line) + def _curl_debug(debug_type, debug_msg): debug_types = ('I', '<', '>', '<', '>') if debug_type == 0: diff --git a/tornado/database.py b/tornado/database.py index a74879b03..e0abb9de5 100644 --- a/tornado/database.py +++ b/tornado/database.py @@ -26,6 +26,7 @@ import itertools import logging import time + class Connection(object): """A lightweight wrapper around MySQLdb DB-API connections. @@ -43,7 +44,7 @@ class Connection(object): UTF-8 on all connections to avoid time zone and encoding errors. """ def __init__(self, host, database, user=None, password=None, - max_idle_time=7*3600): + max_idle_time=7 * 3600): self.host = host self.database = database self.max_idle_time = max_idle_time diff --git a/tornado/escape.py b/tornado/escape.py index 2136e6499..422286498 100644 --- a/tornado/escape.py +++ b/tornado/escape.py @@ -28,8 +28,10 @@ import sys import urllib # Python3 compatibility: On python2.5, introduce the bytes alias from 2.6 -try: bytes -except Exception: bytes = str +try: + bytes +except Exception: + bytes = str try: from urlparse import parse_qs # Python 2.6+ @@ -64,6 +66,8 @@ except Exception: _XHTML_ESCAPE_RE = re.compile('[&<>"]') _XHTML_ESCAPE_DICT = {'&': '&', '<': '<', '>': '>', '"': '"'} + + def xhtml_escape(value): """Escapes a string so it is valid within XML or XHTML.""" return _XHTML_ESCAPE_RE.sub(lambda match: _XHTML_ESCAPE_DICT[match.group(0)], @@ -145,13 +149,14 @@ else: result = parse_qs(qs, keep_blank_values, strict_parsing, encoding='latin1', errors='strict') encoded = {} - for k,v in result.iteritems(): + for k, v in result.iteritems(): encoded[k] = [i.encode('latin1') for i in v] return encoded - _UTF8_TYPES = (bytes, type(None)) + + def utf8(value): """Converts a string argument to a byte string. @@ -164,6 +169,8 @@ def utf8(value): return value.encode("utf-8") _TO_UNICODE_TYPES = (unicode, type(None)) + + def to_unicode(value): """Converts a string argument to a unicode string. @@ -187,6 +194,8 @@ else: native_str = utf8 _BASESTRING_TYPES = (basestring, type(None)) + + def to_basestring(value): """Converts a string argument to a subclass of basestring. @@ -201,13 +210,14 @@ def to_basestring(value): assert isinstance(value, bytes) return value.decode("utf-8") + def recursive_unicode(obj): """Walks a simple data structure, converting byte strings to unicode. Supports lists, tuples, and dictionaries. """ if isinstance(obj, dict): - return dict((recursive_unicode(k), recursive_unicode(v)) for (k,v) in obj.iteritems()) + return dict((recursive_unicode(k), recursive_unicode(v)) for (k, v) in obj.iteritems()) elif isinstance(obj, list): return list(recursive_unicode(i) for i in obj) elif isinstance(obj, tuple): @@ -217,7 +227,7 @@ def recursive_unicode(obj): else: return obj -# I originally used the regex from +# I originally used the regex from # http://daringfireball.net/2010/07/improved_regex_for_matching_urls # but it gets all exponential on certain patterns (such as too many trailing # dots), causing the regex matcher to never return. diff --git a/tornado/gen.py b/tornado/gen.py index 04bc4039e..752c3f249 100644 --- a/tornado/gen.py +++ b/tornado/gen.py @@ -71,10 +71,22 @@ import types from tornado.stack_context import ExceptionStackContext -class KeyReuseError(Exception): pass -class UnknownKeyError(Exception): pass -class LeakedCallbackError(Exception): pass -class BadYieldError(Exception): pass + +class KeyReuseError(Exception): + pass + + +class UnknownKeyError(Exception): + pass + + +class LeakedCallbackError(Exception): + pass + + +class BadYieldError(Exception): + pass + def engine(func): """Decorator for asynchronous generators. @@ -92,6 +104,7 @@ def engine(func): @functools.wraps(func) def wrapper(*args, **kwargs): runner = None + def handle_exception(typ, value, tb): # if the function throws an exception before its first "yield" # (or is not a generator at all), the Runner won't exist yet. @@ -110,11 +123,12 @@ def engine(func): # no yield, so we're done return wrapper + class YieldPoint(object): """Base class for objects that may be yielded from the generator.""" def start(self, runner): """Called by the runner after the generator has yielded. - + No other methods will be called on this object before ``start``. """ raise NotImplementedError() @@ -128,12 +142,13 @@ class YieldPoint(object): def get_result(self): """Returns the value to use as the result of the yield expression. - + This method will only be called once, and only after `is_ready` has returned true. """ raise NotImplementedError() + class Callback(YieldPoint): """Returns a callable object that will allow a matching `Wait` to proceed. @@ -159,6 +174,7 @@ class Callback(YieldPoint): def get_result(self): return self.runner.result_callback(self.key) + class Wait(YieldPoint): """Returns the argument passed to the result of a previous `Callback`.""" def __init__(self, key): @@ -173,6 +189,7 @@ class Wait(YieldPoint): def get_result(self): return self.runner.pop_result(self.key) + class WaitAll(YieldPoint): """Returns the results of multiple previous `Callbacks`. @@ -189,10 +206,10 @@ class WaitAll(YieldPoint): def is_ready(self): return all(self.runner.is_ready(key) for key in self.keys) - + def get_result(self): return [self.runner.pop_result(key) for key in self.keys] - + class Task(YieldPoint): """Runs a single asynchronous operation. @@ -203,9 +220,9 @@ class Task(YieldPoint): A `Task` is equivalent to a `Callback`/`Wait` pair (with a unique key generated automatically):: - + result = yield gen.Task(func, args) - + func(args, callback=(yield gen.Callback(key))) result = yield gen.Wait(key) """ @@ -221,13 +238,14 @@ class Task(YieldPoint): runner.register_callback(self.key) self.kwargs["callback"] = runner.result_callback(self.key) self.func(*self.args, **self.kwargs) - + def is_ready(self): return self.runner.is_ready(self.key) def get_result(self): return self.runner.pop_result(self.key) + class Multi(YieldPoint): """Runs multiple asynchronous operations in parallel. @@ -239,7 +257,7 @@ class Multi(YieldPoint): def __init__(self, children): assert all(isinstance(i, YieldPoint) for i in children) self.children = children - + def start(self, runner): for i in self.children: i.start(runner) @@ -250,14 +268,18 @@ class Multi(YieldPoint): def get_result(self): return [i.get_result() for i in self.children] + class _NullYieldPoint(YieldPoint): def start(self, runner): pass + def is_ready(self): return True + def get_result(self): return None + class Runner(object): """Internal implementation of `tornado.gen.engine`. @@ -366,6 +388,8 @@ class Runner(object): return False # in python 2.6+ this could be a collections.namedtuple + + class Arguments(tuple): """The result of a yield expression whose callback had more than one argument (or keyword arguments). diff --git a/tornado/httpclient.py b/tornado/httpclient.py index 345b9b324..27edf15ef 100644 --- a/tornado/httpclient.py +++ b/tornado/httpclient.py @@ -42,6 +42,7 @@ from tornado import httputil from tornado.ioloop import IOLoop from tornado.util import import_object, bytes_type + class HTTPClient(object): """A blocking HTTP client. @@ -76,7 +77,7 @@ class HTTPClient(object): def fetch(self, request, **kwargs): """Executes a request, returning an `HTTPResponse`. - + The request may be either a string URL or an `HTTPRequest` object. If it is a string, we construct an `HTTPRequest` using any additional kwargs: ``HTTPRequest(request, **kwargs)`` @@ -93,6 +94,7 @@ class HTTPClient(object): response.rethrow() return response + class AsyncHTTPClient(object): """An non-blocking HTTP client. @@ -129,7 +131,7 @@ class AsyncHTTPClient(object): cls._async_client_dict = weakref.WeakKeyDictionary() return cls._async_client_dict - def __new__(cls, io_loop=None, max_clients=10, force_instance=False, + def __new__(cls, io_loop=None, max_clients=10, force_instance=False, **kwargs): io_loop = io_loop or IOLoop.instance() if cls is AsyncHTTPClient: @@ -202,6 +204,7 @@ class AsyncHTTPClient(object): AsyncHTTPClient._impl_class = impl AsyncHTTPClient._impl_kwargs = kwargs + class HTTPRequest(object): """HTTP client request object.""" def __init__(self, url, method="GET", headers=None, body=None, @@ -237,23 +240,23 @@ class HTTPRequest(object): :arg bool use_gzip: Request gzip encoding from the server :arg string network_interface: Network interface to use for request :arg callable streaming_callback: If set, `streaming_callback` will - be run with each chunk of data as it is received, and - `~HTTPResponse.body` and `~HTTPResponse.buffer` will be empty in + be run with each chunk of data as it is received, and + `~HTTPResponse.body` and `~HTTPResponse.buffer` will be empty in the final response. :arg callable header_callback: If set, `header_callback` will - be run with each header line as it is received, and + be run with each header line as it is received, and `~HTTPResponse.headers` will be empty in the final response. :arg callable prepare_curl_callback: If set, will be called with a `pycurl.Curl` object to allow the application to make additional `setopt` calls. - :arg string proxy_host: HTTP proxy hostname. To use proxies, - `proxy_host` and `proxy_port` must be set; `proxy_username` and - `proxy_pass` are optional. Proxies are currently only support + :arg string proxy_host: HTTP proxy hostname. To use proxies, + `proxy_host` and `proxy_port` must be set; `proxy_username` and + `proxy_pass` are optional. Proxies are currently only support with `curl_httpclient`. :arg int proxy_port: HTTP proxy port :arg string proxy_username: HTTP proxy username :arg string proxy_password: HTTP proxy password - :arg bool allow_nonstandard_methods: Allow unknown values for `method` + :arg bool allow_nonstandard_methods: Allow unknown values for `method` argument? :arg bool validate_cert: For HTTPS requests, validate the server's certificate? @@ -262,7 +265,7 @@ class HTTPRequest(object): any request uses a custom `ca_certs` file, they all must (they don't have to all use the same `ca_certs`, but it's not possible to mix requests with ca_certs and requests that use the defaults. - :arg bool allow_ipv6: Use IPv6 when available? Default is false in + :arg bool allow_ipv6: Use IPv6 when available? Default is false in `simple_httpclient` and true in `curl_httpclient` :arg string client_key: Filename for client SSL key, if any :arg string client_cert: Filename for client SSL certificate, if any diff --git a/tornado/httpserver.py b/tornado/httpserver.py index eabb5debe..2f70f2338 100644 --- a/tornado/httpserver.py +++ b/tornado/httpserver.py @@ -40,10 +40,11 @@ from tornado import stack_context from tornado.util import b, bytes_type try: - import ssl # Python 2.6+ + import ssl # Python 2.6+ except ImportError: ssl = None + class HTTPServer(TCPServer): r"""A non-blocking, single-threaded HTTP server. @@ -105,7 +106,7 @@ class HTTPServer(TCPServer): In many cases, `tornado.web.Application.listen` can be used to avoid the need to explicitly create the `HTTPServer`. - 2. `~tornado.netutil.TCPServer.bind`/`~tornado.netutil.TCPServer.start`: + 2. `~tornado.netutil.TCPServer.bind`/`~tornado.netutil.TCPServer.start`: simple multi-process:: server = HTTPServer(app) @@ -145,10 +146,12 @@ class HTTPServer(TCPServer): HTTPConnection(stream, address, self.request_callback, self.no_keep_alive, self.xheaders) + class _BadRequestException(Exception): """Exception class for malformed HTTP requests.""" pass + class HTTPConnection(object): """Handles a connection to an HTTP client, executing HTTP requests. @@ -191,7 +194,7 @@ class HTTPConnection(object): if self._write_callback is not None: callback = self._write_callback self._write_callback = None - callback() + callback() # _on_write_complete is enqueued on the IOLoop whenever the # IOStream's write buffer becomes empty, but it's possible for # another callback that runs on the IOLoop before it to @@ -338,8 +341,8 @@ class HTTPRequest(object): GET/POST arguments are available in the arguments property, which maps arguments names to lists of values (to support multiple values for individual names). Names are of type `str`, while arguments - are byte strings. Note that this is different from - `RequestHandler.get_argument`, which returns argument values as + are byte strings. Note that this is different from + `RequestHandler.get_argument`, which returns argument values as unicode strings. .. attribute:: files @@ -377,7 +380,7 @@ class HTTPRequest(object): self.remote_ip = remote_ip if protocol: self.protocol = protocol - elif connection and isinstance(connection.stream, + elif connection and isinstance(connection.stream, iostream.SSLIOStream): self.protocol = "https" else: @@ -395,7 +398,8 @@ class HTTPRequest(object): self.arguments = {} for name, values in arguments.iteritems(): values = [v for v in values if v] - if values: self.arguments[name] = values + if values: + self.arguments[name] = values def supports_http_1_1(self): """Returns True if this request supports HTTP/1.1 semantics""" @@ -475,4 +479,3 @@ class HTTPRequest(object): return False raise return True - diff --git a/tornado/httputil.py b/tornado/httputil.py index 4f86c3782..c44b735c5 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -24,6 +24,7 @@ import re from tornado.util import b, ObjectDict + class HTTPHeaders(dict): """A dictionary that maintains Http-Header-Case for all keys. @@ -174,7 +175,8 @@ def url_concat(url, args): >>> url_concat("http://example.com/foo?a=b", dict(c="d")) 'http://example.com/foo?a=b&c=d' """ - if not args: return url + if not args: + return url if url[-1] not in ('?', '&'): url += '&' if ('?' in url) else '?' return url + urllib.urlencode(args) @@ -212,7 +214,8 @@ def parse_multipart_form_data(boundary, data, arguments, files): footer_length = len(boundary) + 4 parts = data[:-footer_length].split(b("--") + boundary + b("\r\n")) for part in parts: - if not part: continue + if not part: + continue eoh = part.find(b("\r\n\r\n")) if eoh == -1: logging.warning("multipart/form-data missing headers") @@ -252,6 +255,7 @@ def _parseparam(s): yield f.strip() s = s[end:] + def _parse_header(line): """Parse a Content-type like header. @@ -265,7 +269,7 @@ def _parse_header(line): i = p.find('=') if i >= 0: name = p[:i].strip().lower() - value = p[i+1:].strip() + value = p[i + 1:].strip() if len(value) >= 2 and value[0] == value[-1] == '"': value = value[1:-1] value = value.replace('\\\\', '\\').replace('\\"', '"') diff --git a/tornado/ioloop.py b/tornado/ioloop.py index be294c45f..ea1a9b500 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -431,7 +431,7 @@ class _Timeout(object): @staticmethod def timedelta_to_seconds(td): """Equivalent to td.total_seconds() (introduced in python 2.7).""" - return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / float(10**6) + return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6) / float(10 ** 6) # Comparison methods to sort by deadline, with object id as a tiebreaker # to guarantee a consistent ordering. The heapq module uses __le__ @@ -474,7 +474,8 @@ class PeriodicCallback(object): self._timeout = None def _run(self): - if not self._running: return + if not self._running: + return try: self.callback() except Exception: @@ -591,8 +592,10 @@ class _Select(object): pass def register(self, fd, events): - if events & IOLoop.READ: self.read_fds.add(fd) - if events & IOLoop.WRITE: self.write_fds.add(fd) + if events & IOLoop.READ: + self.read_fds.add(fd) + if events & IOLoop.WRITE: + self.write_fds.add(fd) if events & IOLoop.ERROR: self.error_fds.add(fd) # Closed connections are reported as errors by epoll and kqueue, diff --git a/tornado/iostream.py b/tornado/iostream.py index b9ba13548..8f0b32f30 100644 --- a/tornado/iostream.py +++ b/tornado/iostream.py @@ -30,16 +30,17 @@ from tornado import stack_context from tornado.util import b, bytes_type try: - import ssl # Python 2.6+ + import ssl # Python 2.6+ except ImportError: ssl = None + class IOStream(object): r"""A utility class to write to and read from a non-blocking 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). + non-blocking and asynchronous). The socket parameter may either be connected or unconnected. For server operations the socket is the result of calling socket.accept(). @@ -147,7 +148,7 @@ class IOStream(object): if self._read_to_buffer() == 0: break self._add_io_state(self.io_loop.READ) - + def read_until(self, delimiter, callback): """Call callback when we read the given delimiter.""" assert not self._read_callback, "Already reading" @@ -655,7 +656,6 @@ class SSLIOStream(IOStream): # until we've completed the SSL handshake (so certificates are # available, etc). - def _read_from_socket(self): if self._ssl_accepting: # If the handshake hasn't finished yet, there can't be anything @@ -686,6 +686,7 @@ class SSLIOStream(IOStream): return None return chunk + def _merge_prefix(deque, size): """Replace the first entries in a deque of strings with a single string of up to size bytes. @@ -723,6 +724,7 @@ def _merge_prefix(deque, size): if not deque: deque.appendleft(b("")) + def doctests(): import doctest return doctest.DocTestSuite() diff --git a/tornado/locale.py b/tornado/locale.py index f36ed5a6b..fdfe0afcb 100644 --- a/tornado/locale.py +++ b/tornado/locale.py @@ -52,6 +52,7 @@ _translations = {} _supported_locales = frozenset([_default_locale]) _use_gettext = False + def get(*locale_codes): """Returns the closest match for the given locale codes. @@ -111,7 +112,8 @@ def load_translations(directory): global _supported_locales _translations = {} for path in os.listdir(directory): - if not path.endswith(".csv"): continue + if not path.endswith(".csv"): + continue locale, extension = path.split(".") if not re.match("[a-z]+(_[A-Z]+)?$", locale): logging.error("Unrecognized locale %r (path: %s)", locale, @@ -120,7 +122,8 @@ def load_translations(directory): f = open(os.path.join(directory, path), "r") _translations[locale] = {} for i, row in enumerate(csv.reader(f)): - if not row or len(row) < 2: continue + if not row or len(row) < 2: + continue row = [c.decode("utf-8").strip() for c in row] english, translation = row[:2] if len(row) > 2: @@ -136,6 +139,7 @@ def load_translations(directory): _supported_locales = frozenset(_translations.keys() + [_default_locale]) logging.info("Supported locales: %s", sorted(_supported_locales)) + def load_gettext_translations(directory, domain): """Loads translations from gettext's locale tree @@ -160,10 +164,12 @@ def load_gettext_translations(directory, domain): global _use_gettext _translations = {} for lang in os.listdir(directory): - if lang.startswith('.'): continue # skip .svn, etc - if os.path.isfile(os.path.join(directory, lang)): continue + if lang.startswith('.'): + continue # skip .svn, etc + if os.path.isfile(os.path.join(directory, lang)): + continue try: - os.stat(os.path.join(directory, lang, "LC_MESSAGES", domain+".mo")) + os.stat(os.path.join(directory, lang, "LC_MESSAGES", domain + ".mo")) _translations[lang] = gettext.translation(domain, directory, languages=[lang]) except Exception, e: @@ -189,7 +195,8 @@ class Locale(object): def get_closest(cls, *locale_codes): """Returns the closest match for the given locale code.""" for code in locale_codes: - if not code: continue + if not code: + continue code = code.replace("-", "_") parts = code.split("_") if len(parts) > 2: @@ -291,16 +298,16 @@ class Locale(object): if relative and days == 0: if seconds < 50: return _("1 second ago", "%(seconds)d seconds ago", - seconds) % { "seconds": seconds } + seconds) % {"seconds": seconds} if seconds < 50 * 60: minutes = round(seconds / 60.0) return _("1 minute ago", "%(minutes)d minutes ago", - minutes) % { "minutes": minutes } + minutes) % {"minutes": minutes} hours = round(seconds / (60.0 * 60)) return _("1 hour ago", "%(hours)d hours ago", - hours) % { "hours": hours } + hours) % {"hours": hours} if days == 0: format = _("%(time)s") @@ -366,8 +373,10 @@ class Locale(object): of size 1. """ _ = self.translate - if len(parts) == 0: return "" - if len(parts) == 1: return parts[0] + if len(parts) == 0: + return "" + if len(parts) == 1: + return parts[0] comma = u' \u0648 ' if self.code.startswith("fa") else u", " return _("%(commas)s and %(last)s") % { "commas": comma.join(parts[:-1]), @@ -385,6 +394,7 @@ class Locale(object): value = value[:-3] return ",".join(reversed(parts)) + class CSVLocale(Locale): """Locale implementation using tornado's CSV translation format.""" def translate(self, message, plural_message=None, count=None): @@ -399,6 +409,7 @@ class CSVLocale(Locale): message_dict = self.translations.get("unknown", {}) return message_dict.get(message, message) + class GettextLocale(Locale): """Locale implementation using the gettext module.""" def translate(self, message, plural_message=None, count=None): diff --git a/tornado/netutil.py b/tornado/netutil.py index 416d03062..d06a176b4 100644 --- a/tornado/netutil.py +++ b/tornado/netutil.py @@ -30,10 +30,11 @@ from tornado.iostream import IOStream, SSLIOStream from tornado.platform.auto import set_close_exec try: - import ssl # Python 2.6+ + import ssl # Python 2.6+ except ImportError: ssl = None + class TCPServer(object): r"""A non-blocking, single-threaded TCP server. @@ -233,7 +234,7 @@ def bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=128): or socket.AF_INET6 to restrict to ipv4 or ipv6 addresses, otherwise both will be used if available. - The ``backlog`` argument has the same meaning as for + The ``backlog`` argument has the same meaning as for ``socket.listen()``. """ sockets = [] @@ -277,7 +278,7 @@ if hasattr(socket, 'AF_UNIX'): If any other file with that name exists, an exception will be raised. - Returns a socket object (not a list of socket objects like + Returns a socket object (not a list of socket objects like `bind_sockets`) """ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) @@ -299,6 +300,7 @@ if hasattr(socket, 'AF_UNIX'): sock.listen(backlog) return sock + def add_accept_handler(sock, callback, io_loop=None): """Adds an ``IOLoop`` event handler to accept new connections on ``sock``. @@ -310,6 +312,7 @@ def add_accept_handler(sock, callback, io_loop=None): """ if io_loop is None: io_loop = IOLoop.instance() + def accept_handler(fd, events): while True: try: diff --git a/tornado/options.py b/tornado/options.py index db9521ea4..feae3292c 100644 --- a/tornado/options.py +++ b/tornado/options.py @@ -96,7 +96,8 @@ def define(name, default=None, type=None, help=None, metavar=None, frame = sys._getframe(0) options_file = frame.f_code.co_filename file_name = frame.f_back.f_code.co_filename - if file_name == options_file: file_name = "" + if file_name == options_file: + file_name = "" if type is None: if not multiple and default is not None: type = default.__class__ @@ -116,7 +117,8 @@ def parse_command_line(args=None): We return all command line arguments that are not options as a list. """ - if args is None: args = sys.argv + if args is None: + args = sys.argv remaining = [] for i in xrange(1, len(args)): # All things after the last option are command line arguments @@ -124,7 +126,7 @@ def parse_command_line(args=None): remaining = args[i:] break if args[i] == "--": - remaining = args[i+1:] + remaining = args[i + 1:] break arg = args[i].lstrip("-") name, equals, value = arg.partition("=") @@ -170,7 +172,8 @@ def print_help(file=sys.stdout): by_group.setdefault(option.group_name, []).append(option) for filename, o in sorted(by_group.items()): - if filename: print >> file, filename + if filename: + print >> file, filename o.sort(key=lambda option: option.name) for option in o: prefix = option.name @@ -228,7 +231,7 @@ class _Option(object): lo, _, hi = part.partition(":") lo = _parse(lo) hi = _parse(hi) if hi else lo - self._value.extend(range(lo, hi+1)) + self._value.extend(range(lo, hi + 1)) else: self._value.append(_parse(part)) else: @@ -322,7 +325,7 @@ class Error(Exception): def enable_pretty_logging(): """Turns on formatted logging output as configured. - + This is called automatically by `parse_command_line`. """ root_logger = logging.getLogger() @@ -350,7 +353,6 @@ def enable_pretty_logging(): root_logger.addHandler(channel) - class _LogFormatter(logging.Formatter): def __init__(self, color, *args, **kwargs): logging.Formatter.__init__(self, *args, **kwargs) @@ -368,13 +370,13 @@ class _LogFormatter(logging.Formatter): if (3, 0) < sys.version_info < (3, 2, 3): fg_color = unicode(fg_color, "ascii") self._colors = { - logging.DEBUG: unicode(curses.tparm(fg_color, 4), # Blue + logging.DEBUG: unicode(curses.tparm(fg_color, 4), # Blue "ascii"), - logging.INFO: unicode(curses.tparm(fg_color, 2), # Green + logging.INFO: unicode(curses.tparm(fg_color, 2), # Green "ascii"), - logging.WARNING: unicode(curses.tparm(fg_color, 3), # Yellow + logging.WARNING: unicode(curses.tparm(fg_color, 3), # Yellow "ascii"), - logging.ERROR: unicode(curses.tparm(fg_color, 1), # Red + logging.ERROR: unicode(curses.tparm(fg_color, 1), # Red "ascii"), } self._normal = unicode(curses.tigetstr("sgr0"), "ascii") diff --git a/tornado/platform/interface.py b/tornado/platform/interface.py index c08f8eaf3..21e72cd9e 100644 --- a/tornado/platform/interface.py +++ b/tornado/platform/interface.py @@ -23,10 +23,12 @@ implementation from `tornado.platform.auto`. from __future__ import absolute_import, division, with_statement + def set_close_exec(fd): """Sets the close-on-exec bit (``FD_CLOEXEC``)for a file descriptor.""" raise NotImplementedError() + class Waker(object): """A socket-like object that can wake another thread from ``select()``. @@ -38,7 +40,7 @@ class Waker(object): """ def fileno(self): """Returns a file descriptor for this waker. - + Must be suitable for use with ``select()`` or equivalent on the local platform. """ @@ -55,5 +57,3 @@ class Waker(object): def close(self): """Closes the waker's file descriptor(s).""" raise NotImplementedError() - - diff --git a/tornado/platform/posix.py b/tornado/platform/posix.py index ae5abed82..8d674c0e2 100644 --- a/tornado/platform/posix.py +++ b/tornado/platform/posix.py @@ -24,14 +24,17 @@ import os from tornado.platform import interface from tornado.util import b + def set_close_exec(fd): flags = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) + def _set_nonblocking(fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) - + + class Waker(interface.Waker): def __init__(self): r, w = os.pipe() @@ -55,7 +58,8 @@ class Waker(interface.Waker): try: while True: result = self.reader.read() - if not result: break; + if not result: + break except IOError: pass diff --git a/tornado/platform/twisted.py b/tornado/platform/twisted.py index 7aa1a805d..564c5f8f9 100644 --- a/tornado/platform/twisted.py +++ b/tornado/platform/twisted.py @@ -107,6 +107,7 @@ class TornadoDelayedCall(object): def active(self): return self._active + class TornadoReactor(PosixReactorBase): """Twisted reactor built on the Tornado IOLoop. @@ -125,7 +126,7 @@ class TornadoReactor(PosixReactorBase): self._io_loop = io_loop self._readers = {} # map of reader objects to fd self._writers = {} # map of writer objects to fd - self._fds = {} # a map of fd to a (reader, writer) tuple + self._fds = {} # a map of fd to a (reader, writer) tuple self._delayedCalls = {} PosixReactorBase.__init__(self) @@ -295,6 +296,7 @@ class TornadoReactor(PosixReactorBase): if self._stopped: self.fireSystemEvent("shutdown") + class _TestReactor(TornadoReactor): """Subclass of TornadoReactor for use in unittests. @@ -319,7 +321,6 @@ class _TestReactor(TornadoReactor): port, protocol, interface=interface, maxPacketSize=maxPacketSize) - def install(io_loop=None): """Install this package as the default Twisted reactor.""" if not io_loop: diff --git a/tornado/platform/windows.py b/tornado/platform/windows.py index 16fd66090..a39a83b78 100644 --- a/tornado/platform/windows.py +++ b/tornado/platform/windows.py @@ -90,7 +90,8 @@ class Waker(interface.Waker): try: while True: result = self.reader.recv(1024) - if not result: break + if not result: + break except IOError: pass diff --git a/tornado/process.py b/tornado/process.py index 5a33e696b..28a61bcd3 100644 --- a/tornado/process.py +++ b/tornado/process.py @@ -29,10 +29,11 @@ from binascii import hexlify from tornado import ioloop try: - import multiprocessing # Python 2.6+ + import multiprocessing # Python 2.6+ except ImportError: multiprocessing = None + def cpu_count(): """Returns the number of processors on this machine.""" if multiprocessing is not None: @@ -47,6 +48,7 @@ def cpu_count(): logging.error("Could not detect number of processors; assuming 1") return 1 + def _reseed_random(): if 'random' not in sys.modules: return @@ -63,6 +65,7 @@ def _reseed_random(): _task_id = None + def fork_processes(num_processes, max_restarts=100): """Starts multiple worker processes. @@ -97,6 +100,7 @@ def fork_processes(num_processes, max_restarts=100): "IOLoop.instance() before calling start_processes()") logging.info("Starting %d processes", num_processes) children = {} + def start_child(i): pid = os.fork() if pid == 0: @@ -110,7 +114,8 @@ def fork_processes(num_processes, max_restarts=100): return None for i in range(num_processes): id = start_child(i) - if id is not None: return id + if id is not None: + return id num_restarts = 0 while children: try: @@ -135,13 +140,15 @@ def fork_processes(num_processes, max_restarts=100): if num_restarts > max_restarts: raise RuntimeError("Too many child restarts, giving up") new_id = start_child(id) - if new_id is not None: return new_id + if new_id is not None: + return new_id # All child processes exited cleanly, so exit the master process # instead of just returning to right after the call to # fork_processes (which will probably just start up another IOLoop # unless the caller checks the return value). sys.exit(0) + def task_id(): """Returns the current task id, if any. diff --git a/tornado/simple_httpclient.py b/tornado/simple_httpclient.py index 1f35b0989..09505dc18 100644 --- a/tornado/simple_httpclient.py +++ b/tornado/simple_httpclient.py @@ -28,12 +28,13 @@ except ImportError: from cStringIO import StringIO as BytesIO # python 2 try: - import ssl # python 2.6+ + import ssl # python 2.6+ except ImportError: ssl = None _DEFAULT_CA_CERTS = os.path.dirname(__file__) + '/ca-certificates.crt' + class SimpleAsyncHTTPClient(AsyncHTTPClient): """Non-blocking HTTP client with no external dependencies. @@ -119,7 +120,6 @@ class SimpleAsyncHTTPClient(AsyncHTTPClient): self._process_queue() - class _HTTPConnection(object): _SUPPORTED_METHODS = set(["GET", "HEAD", "POST", "PUT", "DELETE"]) @@ -198,7 +198,7 @@ class _HTTPConnection(object): # compatibility with servers configured for TLSv1 only, # but nearly all servers support SSLv3: # http://blog.ivanristic.com/2011/09/ssl-survey-protocol-support.html - if sys.version_info >= (2,7): + if sys.version_info >= (2, 7): ssl_options["ciphers"] = "DEFAULT:!SSLv2" else: # This is really only necessary for pre-1.0 versions @@ -367,7 +367,7 @@ class _HTTPConnection(object): self.headers.get("Content-Encoding") == "gzip"): # Magic parameter makes zlib module understand gzip header # http://stackoverflow.com/questions/1838699/how-can-i-decompress-a-gzip-stream-with-zlib - self._decompressor = zlib.decompressobj(16+zlib.MAX_WBITS) + self._decompressor = zlib.decompressobj(16 + zlib.MAX_WBITS) if self.headers.get("Transfer-Encoding") == "chunked": self.chunks = [] self.stream.read_until(b("\r\n"), self._on_chunk_length) @@ -417,7 +417,7 @@ class _HTTPConnection(object): self.request.streaming_callback(data) buffer = BytesIO() else: - buffer = BytesIO(data) # TODO: don't require one big string? + buffer = BytesIO(data) # TODO: don't require one big string? response = HTTPResponse(original_request, self.code, headers=self.headers, request_time=time.time() - self.start_time, @@ -456,6 +456,7 @@ class _HTTPConnection(object): class CertificateError(ValueError): pass + def _dnsname_to_pat(dn): pats = [] for frag in dn.split(r'.'): @@ -469,6 +470,7 @@ def _dnsname_to_pat(dn): pats.append(frag.replace(r'\*', '[^.]*')) return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + def match_hostname(cert, hostname): """Verify that *cert* (in decoded format as returned by SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules diff --git a/tornado/stack_context.py b/tornado/stack_context.py index c3fb02234..5e744815d 100644 --- a/tornado/stack_context.py +++ b/tornado/stack_context.py @@ -74,11 +74,13 @@ import itertools import sys import threading + class _State(threading.local): def __init__(self): self.contexts = () _state = _State() + class StackContext(object): '''Establishes the given context as a StackContext that will be transferred. @@ -116,6 +118,7 @@ class StackContext(object): finally: _state.contexts = self.old_contexts + class ExceptionStackContext(object): '''Specialization of StackContext for exception handling. @@ -144,6 +147,7 @@ class ExceptionStackContext(object): finally: _state.contexts = self.old_contexts + class NullContext(object): '''Resets the StackContext. @@ -158,9 +162,11 @@ class NullContext(object): def __exit__(self, type, value, traceback): _state.contexts = self.old_contexts + class _StackContextWrapper(functools.partial): pass + def wrap(fn): '''Returns a callable object that will restore the current StackContext when executed. @@ -173,6 +179,7 @@ def wrap(fn): return fn # functools.wraps doesn't appear to work on functools.partial objects #@functools.wraps(fn) + def wrapped(callback, contexts, *args, **kwargs): if contexts is _state.contexts or not contexts: callback(*args, **kwargs) @@ -190,7 +197,7 @@ def wrap(fn): for a, b in itertools.izip(_state.contexts, contexts))): # contexts have been removed or changed, so start over new_contexts = ([NullContext()] + - [cls(arg) for (cls,arg) in contexts]) + [cls(arg) for (cls, arg) in contexts]) else: new_contexts = [cls(arg) for (cls, arg) in contexts[len(_state.contexts):]] @@ -207,6 +214,7 @@ def wrap(fn): else: return _StackContextWrapper(fn) + @contextlib.contextmanager def _nested(*managers): """Support multiple context managers in a single with-statement. @@ -241,4 +249,3 @@ def _nested(*managers): # the right information. Another exception may # have been raised and caught by an exit method raise exc[0], exc[1], exc[2] - diff --git a/tornado/template.py b/tornado/template.py index c3e3cccea..a211f3960 100644 --- a/tornado/template.py +++ b/tornado/template.py @@ -135,7 +135,7 @@ with ``{# ... #}``. ``{% for *var* in *expr* %}...{% end %}`` Same as the python ``for`` statement. - + ``{% from *x* import *y* %}`` Same as the python ``import`` statement. @@ -189,6 +189,7 @@ from tornado.util import bytes_type, ObjectDict _DEFAULT_AUTOESCAPE = "xhtml_escape" _UNSET = object() + class Template(object): """A compiled template. @@ -217,7 +218,7 @@ class Template(object): # the module name used in __name__ below. self.compiled = compile( escape.to_unicode(self.code), - "%s.generated.py" % self.name.replace('.','_'), + "%s.generated.py" % self.name.replace('.', '_'), "exec") except Exception: formatted_code = _format_code(self.code).rstrip() @@ -326,6 +327,7 @@ class BaseLoader(object): def _create_template(self, name): raise NotImplementedError() + class Loader(BaseLoader): """A template loader that loads from a single root directory. @@ -404,7 +406,6 @@ class _File(_Node): return (self.body,) - class _ChunkList(_Node): def __init__(self, chunks): self.chunks = chunks @@ -531,11 +532,13 @@ class _Expression(_Node): writer.current_template.autoescape, self.line) writer.write_line("_append(_tmp)", self.line) + class _Module(_Expression): def __init__(self, expression, line): super(_Module, self).__init__("_modules." + expression, line, raw=True) + class _Text(_Node): def __init__(self, value, line): self.value = value @@ -608,7 +611,7 @@ class _CodeWriter(object): ancestors = ["%s:%d" % (tmpl.name, lineno) for (tmpl, lineno) in self.include_stack] line_comment += ' (via %s)' % ', '.join(reversed(ancestors)) - print >> self.file, " "*indent + line + line_comment + print >> self.file, " " * indent + line + line_comment class _TemplateReader(object): @@ -651,9 +654,12 @@ class _TemplateReader(object): if type(key) is slice: size = len(self) start, stop, step = key.indices(size) - if start is None: start = self.pos - else: start += self.pos - if stop is not None: stop += self.pos + if start is None: + start = self.pos + else: + start += self.pos + if stop is not None: + stop += self.pos return self.text[slice(start, stop, step)] elif key < 0: return self.text[key] @@ -796,7 +802,8 @@ def _parse(reader, template, in_block=None): block = _Statement(suffix, line) elif operator == "autoescape": fn = suffix.strip() - if fn == "None": fn = None + if fn == "None": + fn = None template.autoescape = fn continue elif operator == "raw": diff --git a/tornado/test/auth_test.py b/tornado/test/auth_test.py index e77396d43..748c526f7 100644 --- a/tornado/test/auth_test.py +++ b/tornado/test/auth_test.py @@ -11,6 +11,7 @@ from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase from tornado.util import b from tornado.web import RequestHandler, Application, asynchronous + class OpenIdClientLoginHandler(RequestHandler, OpenIdMixin): def initialize(self, test): self._OPENID_ENDPOINT = test.get_url('/openid/server/authenticate') @@ -27,11 +28,13 @@ class OpenIdClientLoginHandler(RequestHandler, OpenIdMixin): assert user is not None self.finish(user) + class OpenIdServerAuthenticateHandler(RequestHandler): def post(self): assert self.get_argument('openid.mode') == 'check_authentication' self.write('is_valid:true') + class OAuth1ClientLoginHandler(RequestHandler, OAuthMixin): def initialize(self, test, version): self._OAUTH_VERSION = version @@ -58,6 +61,7 @@ class OAuth1ClientLoginHandler(RequestHandler, OAuthMixin): assert access_token == dict(key=b('uiop'), secret=b('5678')), access_token callback(dict(email='foo@example.com')) + class OAuth1ClientRequestParametersHandler(RequestHandler, OAuthMixin): def initialize(self, version): self._OAUTH_VERSION = version @@ -70,17 +74,21 @@ class OAuth1ClientRequestParametersHandler(RequestHandler, OAuthMixin): 'http://www.example.com/api/asdf', dict(key='uiop', secret='5678'), parameters=dict(foo='bar')) - import urllib; urllib.urlencode(params) + import urllib + urllib.urlencode(params) self.write(params) + class OAuth1ServerRequestTokenHandler(RequestHandler): def get(self): self.write('oauth_token=zxcv&oauth_token_secret=1234') + class OAuth1ServerAccessTokenHandler(RequestHandler): def get(self): self.write('oauth_token=uiop&oauth_token_secret=5678') + class OAuth2ClientLoginHandler(RequestHandler, OAuth2Mixin): def initialize(self, test): self._OAUTH_AUTHORIZE_URL = test.get_url('/oauth2/server/authorize') @@ -139,7 +147,7 @@ class AuthTest(AsyncHTTPTestCase, LogTrapTestCase): def test_oauth10_get_user(self): response = self.fetch( '/oauth10/client/login?oauth_token=zxcv', - headers={'Cookie':'_oauth_request_token=enhjdg==|MTIzNA=='}) + headers={'Cookie': '_oauth_request_token=enhjdg==|MTIzNA=='}) response.rethrow() parsed = json_decode(response.body) self.assertEqual(parsed['email'], 'foo@example.com') @@ -167,7 +175,7 @@ class AuthTest(AsyncHTTPTestCase, LogTrapTestCase): def test_oauth10a_get_user(self): response = self.fetch( '/oauth10a/client/login?oauth_token=zxcv', - headers={'Cookie':'_oauth_request_token=enhjdg==|MTIzNA=='}) + headers={'Cookie': '_oauth_request_token=enhjdg==|MTIzNA=='}) response.rethrow() parsed = json_decode(response.body) self.assertEqual(parsed['email'], 'foo@example.com') diff --git a/tornado/test/curl_httpclient_test.py b/tornado/test/curl_httpclient_test.py index 1085462cb..a6da39fb9 100644 --- a/tornado/test/curl_httpclient_test.py +++ b/tornado/test/curl_httpclient_test.py @@ -9,6 +9,7 @@ except ImportError: if pycurl is not None: from tornado.curl_httpclient import CurlAsyncHTTPClient + class CurlHTTPClientCommonTestCase(HTTPClientCommonTestCase): def get_http_client(self): client = CurlAsyncHTTPClient(io_loop=self.io_loop) diff --git a/tornado/test/escape_test.py b/tornado/test/escape_test.py index 37c938317..25b5dae1a 100644 --- a/tornado/test/escape_test.py +++ b/tornado/test/escape_test.py @@ -66,11 +66,11 @@ linkify_tests = [ ("http://www.example.com/wpstyle/?p=364.", {}, u'http://www.example.com/wpstyle/?p=364.'), - ("rdar://1234", + ("rdar://1234", {"permitted_protocols": ["http", "rdar"]}, u'rdar://1234'), - ("rdar:/1234", + ("rdar:/1234", {"permitted_protocols": ["rdar"]}, u'rdar:/1234'), @@ -99,7 +99,7 @@ linkify_tests = [ ("Just a www.example.com link.", {}, u'Just a www.example.com link.'), - ("Just a www.example.com link.", + ("Just a www.example.com link.", {"require_protocol": True}, u'Just a www.example.com link.'), diff --git a/tornado/test/gen_test.py b/tornado/test/gen_test.py index e4e018c73..86d7d0d60 100644 --- a/tornado/test/gen_test.py +++ b/tornado/test/gen_test.py @@ -8,6 +8,7 @@ from tornado.web import Application, RequestHandler, asynchronous from tornado import gen + class GenTest(AsyncTestCase): def run_gen(self, f): f() @@ -47,7 +48,7 @@ class GenTest(AsyncTestCase): def test_exception_phase1(self): @gen.engine def f(): - 1/0 + 1 / 0 self.assertRaises(ZeroDivisionError, self.run_gen, f) def test_exception_phase2(self): @@ -55,12 +56,12 @@ class GenTest(AsyncTestCase): def f(): self.io_loop.add_callback((yield gen.Callback("k1"))) yield gen.Wait("k1") - 1/0 + 1 / 0 self.assertRaises(ZeroDivisionError, self.run_gen, f) def test_exception_in_task_phase1(self): def fail_task(callback): - 1/0 + 1 / 0 @gen.engine def f(): @@ -74,7 +75,7 @@ class GenTest(AsyncTestCase): def test_exception_in_task_phase2(self): # This is the case that requires the use of stack_context in gen.engine def fail_task(callback): - self.io_loop.add_callback(lambda: 1/0) + self.io_loop.add_callback(lambda: 1 / 0) @gen.engine def f(): @@ -265,6 +266,7 @@ class GenSequenceHandler(RequestHandler): yield gen.Wait("k1") self.finish("3") + class GenTaskHandler(RequestHandler): @asynchronous @gen.engine @@ -275,6 +277,7 @@ class GenTaskHandler(RequestHandler): response.rethrow() self.finish(b("got response: ") + response.body) + class GenExceptionHandler(RequestHandler): @asynchronous @gen.engine @@ -284,20 +287,23 @@ class GenExceptionHandler(RequestHandler): yield gen.Task(io_loop.add_callback) raise Exception("oops") + class GenYieldExceptionHandler(RequestHandler): @asynchronous @gen.engine def get(self): io_loop = self.request.connection.stream.io_loop # Test the interaction of the two stack_contexts. + def fail_task(callback): - io_loop.add_callback(lambda: 1/0) + io_loop.add_callback(lambda: 1 / 0) try: yield gen.Task(fail_task) raise Exception("did not get expected exception") except ZeroDivisionError: self.finish('ok') + class GenWebTest(AsyncHTTPTestCase, LogTrapTestCase): def get_app(self): return Application([ diff --git a/tornado/test/httpclient_test.py b/tornado/test/httpclient_test.py index 8cfa8f91a..9ec967999 100644 --- a/tornado/test/httpclient_test.py +++ b/tornado/test/httpclient_test.py @@ -15,27 +15,32 @@ from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase, get_unused_port from tornado.util import b, bytes_type from tornado.web import Application, RequestHandler, url + class HelloWorldHandler(RequestHandler): def get(self): name = self.get_argument("name", "world") self.set_header("Content-Type", "text/plain") self.finish("Hello %s!" % name) + class PostHandler(RequestHandler): def post(self): self.finish("Post arg1: %s, arg2: %s" % ( self.get_argument("arg1"), self.get_argument("arg2"))) + class ChunkHandler(RequestHandler): def get(self): self.write("asdf") self.flush() self.write("qwer") + class AuthHandler(RequestHandler): def get(self): self.finish(self.request.headers["Authorization"]) + class CountdownHandler(RequestHandler): def get(self, count): count = int(count) @@ -44,6 +49,7 @@ class CountdownHandler(RequestHandler): else: self.write("Zero") + class EchoPostHandler(RequestHandler): def post(self): self.write(self.request.body) @@ -51,6 +57,8 @@ class EchoPostHandler(RequestHandler): # These tests end up getting run redundantly: once here with the default # HTTPClient implementation, and then again in each implementation's own # test suite. + + class HTTPClientCommonTestCase(AsyncHTTPTestCase, LogTrapTestCase): def get_http_client(self): """Returns AsyncHTTPClient instance. May be overridden in subclass.""" @@ -124,6 +132,7 @@ Transfer-Encoding: chunked 0 """).replace(b("\n"), b("\r\n")), callback=stream.close) + def accept_callback(conn, address): # fake an HTTP server using chunked encoding where the final chunks # and connection close all happen at once @@ -135,7 +144,6 @@ Transfer-Encoding: chunked resp = self.wait() resp.rethrow() self.assertEqual(resp.body, b("12")) - def test_basic_auth(self): self.assertEqual(self.fetch("/auth", auth_username="Aladdin", diff --git a/tornado/test/httpserver_test.py b/tornado/test/httpserver_test.py index 5f75c7525..01f8f837e 100644 --- a/tornado/test/httpserver_test.py +++ b/tornado/test/httpserver_test.py @@ -22,6 +22,7 @@ try: except ImportError: ssl = None + class HandlerBaseTestCase(AsyncHTTPTestCase, LogTrapTestCase): def get_app(self): return Application([('/', self.__class__.Handler)]) @@ -31,6 +32,7 @@ class HandlerBaseTestCase(AsyncHTTPTestCase, LogTrapTestCase): response.rethrow() return json_decode(response.body) + class HelloWorldRequestHandler(RequestHandler): def initialize(self, protocol="http"): self.expected_protocol = protocol @@ -42,6 +44,7 @@ class HelloWorldRequestHandler(RequestHandler): def post(self): self.finish("Got %d bytes in POST" % len(self.request.body)) + class BaseSSLTest(AsyncHTTPTestCase, LogTrapTestCase): def get_ssl_version(self): raise NotImplementedError() @@ -55,7 +58,7 @@ class BaseSSLTest(AsyncHTTPTestCase, LogTrapTestCase): force_instance=True) def get_app(self): - return Application([('/', HelloWorldRequestHandler, + return Application([('/', HelloWorldRequestHandler, dict(protocol="https"))]) def get_httpserver_options(self): @@ -74,6 +77,7 @@ class BaseSSLTest(AsyncHTTPTestCase, LogTrapTestCase): **kwargs) return self.wait() + class SSLTestMixin(object): def test_ssl(self): response = self.fetch('/') @@ -82,7 +86,7 @@ class SSLTestMixin(object): def test_large_post(self): response = self.fetch('/', method='POST', - body='A'*5000) + body='A' * 5000) self.assertEqual(response.body, b("Got 5000 bytes in POST")) def test_non_ssl_request(self): @@ -99,16 +103,26 @@ class SSLTestMixin(object): # For example, SSLv3 and TLSv1 throw an exception if you try to read # from the socket before the handshake is complete, but the default # of SSLv23 allows it. + + class SSLv23Test(BaseSSLTest, SSLTestMixin): - def get_ssl_version(self): return ssl.PROTOCOL_SSLv23 + def get_ssl_version(self): + return ssl.PROTOCOL_SSLv23 + + class SSLv3Test(BaseSSLTest, SSLTestMixin): - def get_ssl_version(self): return ssl.PROTOCOL_SSLv3 + def get_ssl_version(self): + return ssl.PROTOCOL_SSLv3 + + class TLSv1Test(BaseSSLTest, SSLTestMixin): - def get_ssl_version(self): return ssl.PROTOCOL_TLSv1 + def get_ssl_version(self): + return ssl.PROTOCOL_TLSv1 if hasattr(ssl, 'PROTOCOL_SSLv2'): class SSLv2Test(BaseSSLTest): - def get_ssl_version(self): return ssl.PROTOCOL_SSLv2 + def get_ssl_version(self): + return ssl.PROTOCOL_SSLv2 def test_sslv2_fail(self): # This is really more of a client test, but run it here since @@ -136,7 +150,7 @@ if ssl is None: del SSLv23Test del SSLv3Test del TLSv1Test -elif getattr(ssl, 'OPENSSL_VERSION_INFO', (0,0)) < (1,0): +elif getattr(ssl, 'OPENSSL_VERSION_INFO', (0, 0)) < (1, 0): # In pre-1.0 versions of openssl, SSLv23 clients always send SSLv2 # ClientHello messages, which are rejected by SSLv3 and TLSv1 # servers. Note that while the OPENSSL_VERSION_INFO was formally @@ -145,6 +159,7 @@ elif getattr(ssl, 'OPENSSL_VERSION_INFO', (0,0)) < (1,0): del SSLv3Test del TLSv1Test + class MultipartTestHandler(RequestHandler): def post(self): self.finish({"header": self.request.headers["X-Header-Encoding-Test"], @@ -153,6 +168,7 @@ class MultipartTestHandler(RequestHandler): "filebody": _unicode(self.request.files["files"][0]["body"]), }) + class RawRequestHTTPConnection(simple_httpclient._HTTPConnection): def set_request(self, request): self.__next_request = request @@ -163,6 +179,8 @@ class RawRequestHTTPConnection(simple_httpclient._HTTPConnection): self.stream.read_until(b("\r\n\r\n"), self._on_headers) # This test is also called from wsgi_test + + class HTTPConnectionTest(AsyncHTTPTestCase, LogTrapTestCase): def get_handlers(self): return [("/multipart", MultipartTestHandler), @@ -176,7 +194,7 @@ class HTTPConnectionTest(AsyncHTTPTestCase, LogTrapTestCase): conn = RawRequestHTTPConnection(self.io_loop, client, httpclient.HTTPRequest(self.get_url("/")), None, self.stop, - 1024*1024) + 1024 * 1024) conn.set_request( b("\r\n").join(headers + [utf8("Content-Length: %d\r\n" % len(body))]) + @@ -239,10 +257,12 @@ class HTTPConnectionTest(AsyncHTTPTestCase, LogTrapTestCase): self.assertEqual(body, b("Got 1024 bytes in POST")) stream.close() + class EchoHandler(RequestHandler): def get(self): self.write(recursive_unicode(self.request.arguments)) + class TypeCheckHandler(RequestHandler): def prepare(self): self.errors = {} @@ -279,9 +299,10 @@ class TypeCheckHandler(RequestHandler): def check_type(self, name, obj, expected_type): actual_type = type(obj) if expected_type != actual_type: - self.errors[name] = "expected %s, got %s" % (expected_type, + self.errors[name] = "expected %s, got %s" % (expected_type, actual_type) + class HTTPServerTest(AsyncHTTPTestCase, LogTrapTestCase): def get_app(self): return Application([("/echo", EchoHandler), @@ -303,6 +324,7 @@ class HTTPServerTest(AsyncHTTPTestCase, LogTrapTestCase): data = json_decode(response.body) self.assertEqual(data, {}) + class XHeaderTest(HandlerBaseTestCase): class Handler(RequestHandler): def get(self): diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py index 440f6e6e7..d0e58e910 100644 --- a/tornado/test/httputil_test.py +++ b/tornado/test/httputil_test.py @@ -15,42 +15,42 @@ class TestUrlConcat(unittest.TestCase): def test_url_concat_no_query_params(self): url = url_concat( "https://localhost/path", - [('y','y'), ('z','z')], + [('y', 'y'), ('z', 'z')], ) self.assertEqual(url, "https://localhost/path?y=y&z=z") def test_url_concat_encode_args(self): url = url_concat( "https://localhost/path", - [('y','/y'), ('z','z')], + [('y', '/y'), ('z', 'z')], ) self.assertEqual(url, "https://localhost/path?y=%2Fy&z=z") def test_url_concat_trailing_q(self): url = url_concat( "https://localhost/path?", - [('y','y'), ('z','z')], + [('y', 'y'), ('z', 'z')], ) self.assertEqual(url, "https://localhost/path?y=y&z=z") def test_url_concat_q_with_no_trailing_amp(self): url = url_concat( "https://localhost/path?x", - [('y','y'), ('z','z')], + [('y', 'y'), ('z', 'z')], ) self.assertEqual(url, "https://localhost/path?x&y=y&z=z") def test_url_concat_trailing_amp(self): url = url_concat( "https://localhost/path?x&", - [('y','y'), ('z','z')], + [('y', 'y'), ('z', 'z')], ) self.assertEqual(url, "https://localhost/path?x&y=y&z=z") def test_url_concat_mult_params(self): url = url_concat( "https://localhost/path?a=1&b=2", - [('y','y'), ('z','z')], + [('y', 'y'), ('z', 'z')], ) self.assertEqual(url, "https://localhost/path?a=1&b=2&y=y&z=z") @@ -61,6 +61,7 @@ class TestUrlConcat(unittest.TestCase): ) self.assertEqual(url, "https://localhost/path?r=1&t=2") + class MultipartFormDataTest(LogTrapTestCase): def test_file_upload(self): data = b("""\ @@ -75,7 +76,7 @@ Foo file = files["files"][0] self.assertEqual(file["filename"], "ab.txt") self.assertEqual(file["body"], b("Foo")) - + def test_unquoted_names(self): # quotes are optional unless special characters are present data = b("""\ @@ -90,7 +91,7 @@ Foo file = files["files"][0] self.assertEqual(file["filename"], "ab.txt") self.assertEqual(file["body"], b("Foo")) - + def test_special_filenames(self): filenames = ['a;b.txt', 'a"b.txt', diff --git a/tornado/test/import_test.py b/tornado/test/import_test.py index 030bb20eb..584f070d2 100644 --- a/tornado/test/import_test.py +++ b/tornado/test/import_test.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, with_statement import unittest + class ImportTest(unittest.TestCase): def test_import_everything(self): # Some of our modules are not otherwise tested. Import them diff --git a/tornado/test/ioloop_test.py b/tornado/test/ioloop_test.py index 159f91858..df2cd7771 100644 --- a/tornado/test/ioloop_test.py +++ b/tornado/test/ioloop_test.py @@ -8,6 +8,7 @@ import time from tornado.testing import AsyncTestCase, LogTrapTestCase + class TestIOLoop(AsyncTestCase, LogTrapTestCase): def test_add_callback_wakeup(self): # Make sure that add_callback from inside a running IOLoop diff --git a/tornado/test/iostream_test.py b/tornado/test/iostream_test.py index c58da56f4..01f9a984f 100644 --- a/tornado/test/iostream_test.py +++ b/tornado/test/iostream_test.py @@ -8,10 +8,12 @@ from tornado.web import RequestHandler, Application import socket import time + class HelloHandler(RequestHandler): def get(self): self.write("Hello") + class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase): def get_app(self): return Application([('/', HelloHandler)]) @@ -21,9 +23,11 @@ class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase): [listener] = netutil.bind_sockets(port, '127.0.0.1', family=socket.AF_INET) streams = [None, None] + def accept_callback(connection, address): streams[0] = IOStream(connection, io_loop=self.io_loop, **kwargs) self.stop() + def connect_callback(): streams[1] = client_stream self.stop() @@ -69,7 +73,7 @@ class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase): self.wait() # As a side effect, the stream is now listening for connection # close (if it wasn't already), but is not listening for writes - self.assertEqual(server._state, IOLoop.READ|IOLoop.ERROR) + self.assertEqual(server._state, IOLoop.READ | IOLoop.ERROR) server.close() client.close() @@ -80,6 +84,7 @@ class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase): port = get_unused_port() stream = IOStream(socket.socket(), self.io_loop) self.connect_called = False + def connect_callback(): self.connect_called = True stream.set_close_callback(self.stop) @@ -102,7 +107,7 @@ class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase): s.connect(("localhost", self.get_http_port())) stream = IOStream(s, io_loop=self.io_loop) stream.write(b("GET / HTTP/1.0\r\n\r\n")) - + stream.read_until_close(self.stop) data = self.wait() self.assertTrue(data.startswith(b("HTTP/1.0 200"))) @@ -113,9 +118,11 @@ class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase): try: chunks = [] final_called = [] + def streaming_callback(data): chunks.append(data) self.stop() + def final_callback(data): assert not data final_called.append(True) @@ -140,6 +147,7 @@ class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase): server, client = self.make_iostream_pair() try: chunks = [] + def callback(data): chunks.append(data) self.stop() @@ -166,10 +174,12 @@ class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase): client.set_close_callback(self.stop) server.write(b("12")) chunks = [] + def callback1(data): chunks.append(data) client.read_bytes(1, callback2) server.close() + def callback2(data): chunks.append(data) client.read_bytes(1, callback1) diff --git a/tornado/test/process_test.py b/tornado/test/process_test.py index 28f4e06d1..d49e3a572 100644 --- a/tornado/test/process_test.py +++ b/tornado/test/process_test.py @@ -18,6 +18,8 @@ from tornado.web import RequestHandler, Application # Not using AsyncHTTPTestCase because we need control over the IOLoop. # Logging is tricky here so you may want to replace LogTrapTestCase # with unittest.TestCase when debugging. + + class ProcessTest(LogTrapTestCase): def get_app(self): class ProcessHandler(RequestHandler): @@ -48,6 +50,7 @@ class ProcessTest(LogTrapTestCase): def test_multi_process(self): self.assertFalse(IOLoop.initialized()) port = get_unused_port() + def get_url(path): return "http://127.0.0.1:%d%s" % (port, path) sockets = bind_sockets(port, "127.0.0.1") @@ -60,7 +63,8 @@ class ProcessTest(LogTrapTestCase): # finished with status 0 self.assertEqual(e.code, 0) self.assertTrue(task_id() is None) - for sock in sockets: sock.close() + for sock in sockets: + sock.close() signal.alarm(0) return signal.alarm(5) # child process @@ -74,7 +78,8 @@ class ProcessTest(LogTrapTestCase): elif id == 2: signal.alarm(5) self.assertEqual(id, task_id()) - for sock in sockets: sock.close() + for sock in sockets: + sock.close() # Always use SimpleAsyncHTTPClient here; the curl # version appears to get confused sometimes if the # connection gets closed before it's had a chance to @@ -118,7 +123,7 @@ class ProcessTest(LogTrapTestCase): except Exception: logging.error("exception in child process %d", id, exc_info=True) raise - + if os.name != 'posix' or sys.platform == 'cygwin': # All sorts of unixisms here diff --git a/tornado/test/run_pyversion_tests.py b/tornado/test/run_pyversion_tests.py index c150656e0..16806456b 100755 --- a/tornado/test/run_pyversion_tests.py +++ b/tornado/test/run_pyversion_tests.py @@ -15,12 +15,14 @@ INTERPRETERS = [ "pypy", ] + def exists_on_path(filename): for dir in os.environ["PATH"].split(":"): if os.path.exists(os.path.join(dir, filename)): return True return False + def main(): for interpreter in INTERPRETERS: print "=================== %s =======================" % interpreter diff --git a/tornado/test/runtests.py b/tornado/test/runtests.py index 6827de0aa..91f8b25da 100755 --- a/tornado/test/runtests.py +++ b/tornado/test/runtests.py @@ -27,6 +27,7 @@ TEST_MODULES = [ 'tornado.test.wsgi_test', ] + def all(): return unittest.defaultTestLoader.loadTestsFromNames(TEST_MODULES) diff --git a/tornado/test/simple_httpclient_test.py b/tornado/test/simple_httpclient_test.py index 35b30877e..0f5d3151f 100644 --- a/tornado/test/simple_httpclient_test.py +++ b/tornado/test/simple_httpclient_test.py @@ -12,6 +12,7 @@ from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase from tornado.util import b from tornado.web import RequestHandler, Application, asynchronous, url + class SimpleHTTPClientCommonTestCase(HTTPClientCommonTestCase): def get_http_client(self): client = SimpleAsyncHTTPClient(io_loop=self.io_loop, @@ -23,6 +24,7 @@ class SimpleHTTPClientCommonTestCase(HTTPClientCommonTestCase): # try to run it again. del HTTPClientCommonTestCase + class TriggerHandler(RequestHandler): def initialize(self, queue, wake_callback): self.queue = queue @@ -35,32 +37,38 @@ class TriggerHandler(RequestHandler): if self.get_argument("wake", "true") == "true": self.wake_callback() + class HangHandler(RequestHandler): @asynchronous def get(self): pass + class ContentLengthHandler(RequestHandler): def get(self): self.set_header("Content-Length", self.get_argument("value")) self.write("ok") + class HeadHandler(RequestHandler): def head(self): self.set_header("Content-Length", "7") + class NoContentHandler(RequestHandler): def get(self): if self.get_argument("error", None): self.set_header("Content-Length", "7") self.set_status(204) + class SeeOther303PostHandler(RequestHandler): def post(self): assert self.request.body == b("blah") self.set_header("Location", "/303_get") self.set_status(303) + class SeeOther303GetHandler(RequestHandler): def get(self): assert not self.request.body diff --git a/tornado/test/stack_context_test.py b/tornado/test/stack_context_test.py index 73c844020..e682f6a53 100644 --- a/tornado/test/stack_context_test.py +++ b/tornado/test/stack_context_test.py @@ -10,6 +10,7 @@ import functools import logging import unittest + class TestRequestHandler(RequestHandler): def __init__(self, app, request, io_loop): super(TestRequestHandler, self).__init__(app, request) @@ -38,6 +39,7 @@ class TestRequestHandler(RequestHandler): else: return 'unexpected failure' + class HTTPStackContextTest(AsyncHTTPTestCase, LogTrapTestCase): def get_app(self): return Application([('/', TestRequestHandler, @@ -53,6 +55,7 @@ class HTTPStackContextTest(AsyncHTTPTestCase, LogTrapTestCase): self.response = response self.stop() + class StackContextTest(AsyncTestCase, LogTrapTestCase): def setUp(self): super(StackContextTest, self).setUp() @@ -73,10 +76,12 @@ class StackContextTest(AsyncTestCase, LogTrapTestCase): with StackContext(functools.partial(self.context, 'library')): self.io_loop.add_callback( functools.partial(library_inner_callback, callback)) + def library_inner_callback(callback): self.assertEqual(self.active_contexts[-2:], ['application', 'library']) callback() + def final_callback(): # implementation detail: the full context stack at this point # is ['application', 'library', 'application']. The 'library' diff --git a/tornado/test/template_test.py b/tornado/test/template_test.py index 546778848..c0175deea 100644 --- a/tornado/test/template_test.py +++ b/tornado/test/template_test.py @@ -7,6 +7,7 @@ from tornado.template import Template, DictLoader, ParseError from tornado.testing import LogTrapTestCase from tornado.util import b, bytes_type, ObjectDict + class TemplateTest(LogTrapTestCase): def test_simple(self): template = Template("Hello {{ name }}!") @@ -85,11 +86,12 @@ class TemplateTest(LogTrapTestCase): self.assertEqual(template.generate(), utf8(u"\u00e9")) def test_custom_namespace(self): - loader = DictLoader({"test.html": "{{ inc(5) }}"}, namespace={"inc": lambda x: x+1}) + loader = DictLoader({"test.html": "{{ inc(5) }}"}, namespace={"inc": lambda x: x + 1}) self.assertEqual(loader.load("test.html").generate(), b("6")) def test_apply(self): - def upper(s): return s.upper() + def upper(s): + return s.upper() template = Template(utf8("{% apply upper %}foo{% end %}")) self.assertEqual(template.generate(upper=upper), b("FOO")) @@ -102,6 +104,7 @@ class TemplateTest(LogTrapTestCase): template = Template(utf8("{% comment blah blah %}foo")) self.assertEqual(template.generate(), b("foo")) + class StackTraceTest(LogTrapTestCase): def test_error_line_number_expression(self): loader = DictLoader({"test.html": """one @@ -157,7 +160,6 @@ three{%end%} exc_stack = traceback.format_exc() self.assertTrue("# base.html:1" in exc_stack) - def test_error_line_number_extends_sub_error(self): loader = DictLoader({ "base.html": "{% block 'block' %}{% end %}", @@ -228,7 +230,7 @@ default: {% include 'default.html' %} expr: {{ name }} raw: {% raw name %}""", } - + def test_default_off(self): loader = DictLoader(self.templates, autoescape=None) name = "Bobby s" @@ -243,7 +245,7 @@ raw: {% raw name %}""", b("escaped: Bobby <table>s\n" "unescaped: Bobby
s\n" "default: Bobby
s\n")) - + def test_default_on(self): loader = DictLoader(self.templates, autoescape="xhtml_escape") name = "Bobby
s" @@ -253,7 +255,7 @@ raw: {% raw name %}""", b("Bobby
s")) self.assertEqual(loader.load("default.html").generate(name=name), b("Bobby <table>s")) - + self.assertEqual(loader.load("include.html").generate(name=name), b("escaped: Bobby <table>s\n" "unescaped: Bobby
s\n" @@ -269,7 +271,9 @@ raw: {% raw name %}""", def test_extended_block(self): loader = DictLoader(self.templates) - def render(name): return loader.load(name).generate(name="