From: Ben Darnell Date: Mon, 14 Jan 2013 02:31:55 +0000 (-0500) Subject: Get all the tests passing under py3 without 2to3 X-Git-Tag: v3.0.0~178 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=900d5a269730935dbd318f797653162272a65796;p=thirdparty%2Ftornado.git Get all the tests passing under py3 without 2to3 --- diff --git a/setup.py b/setup.py index 7bfc903e1..efa27ac00 100644 --- a/setup.py +++ b/setup.py @@ -35,10 +35,6 @@ if "linux" in sys.platform.lower() and not python_26: version = "2.4.post2" -if major >= 3: - import setuptools # setuptools is required for use_2to3 - kwargs["use_2to3"] = True - distutils.core.setup( name="tornado", version=version, diff --git a/tornado/auth.py b/tornado/auth.py index d1ddbc52a..e25889fcb 100644 --- a/tornado/auth.py +++ b/tornado/auth.py @@ -58,13 +58,18 @@ from tornado import httpclient from tornado import escape from tornado.httputil import url_concat from tornado.log import gen_log -from tornado.util import bytes_type, b, u +from tornado.util import bytes_type, b, u, unicode_type try: import urlparse # py2 except ImportError: import urllib.parse as urlparse # py3 +try: + import urllib.parse as urllib_parse # py3 +except ImportError: + import urllib as urllib_parse # py2 + class OpenIdMixin(object): """Abstract implementation of OpenID and Attribute Exchange. @@ -85,7 +90,7 @@ class OpenIdMixin(object): """ callback_uri = callback_uri or self.request.uri args = self._openid_args(callback_uri, ax_attrs=ax_attrs) - self.redirect(self._OPENID_ENDPOINT + "?" + urllib.urlencode(args)) + self.redirect(self._OPENID_ENDPOINT + "?" + urllib_parse.urlencode(args)) def get_authenticated_user(self, callback, http_client=None): """Fetches the authenticated user data upon redirect. @@ -95,14 +100,14 @@ class OpenIdMixin(object): methods. """ # Verify the OpenID response via direct request to the OP - args = dict((k, v[-1]) for k, v in self.request.arguments.iteritems()) + args = dict((k, v[-1]) for k, v in self.request.arguments.items()) args["openid.mode"] = u("check_authentication") url = self._OPENID_ENDPOINT if http_client is None: http_client = self.get_auth_http_client() http_client.fetch(url, self.async_callback( self._on_authentication_verified, callback), - method="POST", body=urllib.urlencode(args)) + method="POST", body=urllib_parse.urlencode(args)) def _openid_args(self, callback_uri, ax_attrs=[], oauth_scope=None): url = urlparse.urljoin(self.request.full_url(), callback_uri) @@ -161,7 +166,7 @@ class OpenIdMixin(object): # Make sure we got back at least an email from attribute exchange ax_ns = None - for name in self.request.arguments.iterkeys(): + for name in self.request.arguments.keys(): if name.startswith("openid.ns.") and \ self.get_argument(name) == u("http://openid.net/srv/ax/1.0"): ax_ns = name[10:] @@ -172,7 +177,7 @@ class OpenIdMixin(object): return u("") prefix = "openid." + ax_ns + ".type." ax_name = None - for name in self.request.arguments.iterkeys(): + for name in self.request.arguments.keys(): if self.get_argument(name) == uri and name.startswith(prefix): part = name[len(prefix):] ax_name = "openid." + ax_ns + ".value." + part @@ -316,7 +321,7 @@ class OAuthMixin(object): signature = _oauth_signature(consumer_token, "GET", url, args) args["oauth_signature"] = signature - return url + "?" + urllib.urlencode(args) + return url + "?" + urllib_parse.urlencode(args) def _on_request_token(self, authorize_url, callback_uri, response): if response.error: @@ -327,12 +332,12 @@ class OAuthMixin(object): self.set_cookie("_oauth_request_token", data) args = dict(oauth_token=request_token["key"]) if callback_uri == "oob": - self.finish(authorize_url + "?" + urllib.urlencode(args)) + self.finish(authorize_url + "?" + urllib_parse.urlencode(args)) return elif callback_uri: args["oauth_callback"] = urlparse.urljoin( self.request.full_url(), callback_uri) - self.redirect(authorize_url + "?" + urllib.urlencode(args)) + self.redirect(authorize_url + "?" + urllib_parse.urlencode(args)) def _oauth_access_token_url(self, request_token): consumer_token = self._oauth_consumer_token() @@ -356,7 +361,7 @@ class OAuthMixin(object): request_token) args["oauth_signature"] = signature - return url + "?" + urllib.urlencode(args) + return url + "?" + urllib_parse.urlencode(args) def _on_access_token(self, callback, response): if response.error: @@ -557,11 +562,11 @@ class TwitterMixin(OAuthMixin): url, access_token, all_args, method=method) args.update(oauth) if args: - url += "?" + urllib.urlencode(args) + url += "?" + urllib_parse.urlencode(args) callback = self.async_callback(self._on_twitter_request, callback) http = self.get_auth_http_client() if post_args is not None: - http.fetch(url, method="POST", body=urllib.urlencode(post_args), + http.fetch(url, method="POST", body=urllib_parse.urlencode(post_args), callback=callback) else: http.fetch(url, callback=callback) @@ -679,11 +684,11 @@ class FriendFeedMixin(OAuthMixin): url, access_token, all_args, method=method) args.update(oauth) if args: - url += "?" + urllib.urlencode(args) + url += "?" + urllib_parse.urlencode(args) callback = self.async_callback(self._on_friendfeed_request, callback) http = self.get_auth_http_client() if post_args is not None: - http.fetch(url, method="POST", body=urllib.urlencode(post_args), + http.fetch(url, method="POST", body=urllib_parse.urlencode(post_args), callback=callback) else: http.fetch(url, callback=callback) @@ -759,7 +764,7 @@ class GoogleMixin(OpenIdMixin, OAuthMixin): callback_uri = callback_uri or self.request.uri args = self._openid_args(callback_uri, ax_attrs=ax_attrs, oauth_scope=oauth_scope) - self.redirect(self._OPENID_ENDPOINT + "?" + urllib.urlencode(args)) + self.redirect(self._OPENID_ENDPOINT + "?" + urllib_parse.urlencode(args)) def get_authenticated_user(self, callback): """Fetches the authenticated user data upon redirect.""" @@ -841,11 +846,11 @@ class FacebookMixin(object): args["cancel_url"] = urlparse.urljoin( self.request.full_url(), cancel_uri) if extended_permissions: - if isinstance(extended_permissions, (unicode, bytes_type)): + if isinstance(extended_permissions, (unicode_type, bytes_type)): extended_permissions = [extended_permissions] args["req_perms"] = ",".join(extended_permissions) self.redirect("http://www.facebook.com/login.php?" + - urllib.urlencode(args)) + urllib_parse.urlencode(args)) def authorize_redirect(self, extended_permissions, callback_uri=None, cancel_uri=None): @@ -927,7 +932,7 @@ class FacebookMixin(object): args["format"] = "json" args["sig"] = self._signature(args) url = "http://api.facebook.com/restserver.php?" + \ - urllib.urlencode(args) + urllib_parse.urlencode(args) http = self.get_auth_http_client() http.fetch(url, callback=self.async_callback( self._parse_response, callback)) @@ -970,7 +975,7 @@ 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): + if isinstance(body, unicode_type): body = body.encode("utf-8") return hashlib.md5(body).hexdigest() @@ -1108,11 +1113,11 @@ class FacebookGraphMixin(OAuth2Mixin): all_args.update(args) if all_args: - url += "?" + urllib.urlencode(all_args) + url += "?" + urllib_parse.urlencode(all_args) callback = self.async_callback(self._on_facebook_request, callback) http = self.get_auth_http_client() if post_args is not None: - http.fetch(url, method="POST", body=urllib.urlencode(post_args), + http.fetch(url, method="POST", body=urllib_parse.urlencode(post_args), callback=callback) else: http.fetch(url, callback=callback) @@ -1174,8 +1179,8 @@ def _oauth10a_signature(consumer_token, method, url, parameters={}, token=None): for k, v in sorted(parameters.items()))) 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_elems = [escape.utf8(urllib_parse.quote(consumer_token["secret"], safe='~'))] + key_elems.append(escape.utf8(urllib_parse.quote(token["secret"], safe='~') if token else "")) key = b("&").join(key_elems) hash = hmac.new(key, escape.utf8(base_string), hashlib.sha1) @@ -1183,9 +1188,9 @@ def _oauth10a_signature(consumer_token, method, url, parameters={}, token=None): def _oauth_escape(val): - if isinstance(val, unicode): + if isinstance(val, unicode_type): val = val.encode("utf-8") - return urllib.quote(val, safe="~") + return urllib_parse.quote(val, safe="~") def _oauth_parse_response(body): diff --git a/tornado/escape.py b/tornado/escape.py index 4b94e95b0..6a2cbbb01 100644 --- a/tornado/escape.py +++ b/tornado/escape.py @@ -24,20 +24,27 @@ from __future__ import absolute_import, division, print_function, with_statement import re import sys -import urllib from tornado.util import bytes_type, unicode_type, basestring_type, u try: - from urlparse import parse_qs # Python 2.6+ + from urllib.parse import parse_qs # py3 except ImportError: - from cgi import parse_qs + try: + from urlparse import parse_qs # Python 2.6+ + except ImportError: + from cgi import parse_qs try: import htmlentitydefs # py2 except ImportError: import html.entities as htmlentitydefs # py3 +try: + import urllib.parse as urllib_parse # py3 +except ImportError: + import urllib as urllib_parse # py2 + # json module is in the standard library as of python 2.6; fall back to # simplejson if present for older versions. try: @@ -106,7 +113,7 @@ def squeeze(value): def url_escape(value): """Returns a valid URL-encoded version of the given value.""" - return urllib.quote_plus(utf8(value)) + return urllib_parse.quote_plus(utf8(value)) # python 3 changed things around enough that we need two separate # implementations of url_unescape. We also need our own implementation @@ -121,9 +128,9 @@ if sys.version_info[0] < 3: the result is a unicode string in the specified encoding. """ if encoding is None: - return urllib.unquote_plus(utf8(value)) + return urllib_parse.unquote_plus(utf8(value)) else: - return unicode_type(urllib.unquote_plus(utf8(value)), encoding) + return unicode_type(urllib_parse.unquote_plus(utf8(value)), encoding) parse_qs_bytes = parse_qs else: @@ -136,9 +143,9 @@ else: the result is a unicode string in the specified encoding. """ if encoding is None: - return urllib.parse.unquote_to_bytes(value) + return urllib_parse.unquote_to_bytes(value) else: - return urllib.unquote_plus(to_basestring(value), encoding=encoding) + return urllib_parse.unquote_plus(to_basestring(value), encoding=encoding) def parse_qs_bytes(qs, keep_blank_values=False, strict_parsing=False): """Parses a query string like urlparse.parse_qs, but returns the @@ -153,7 +160,7 @@ 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.items(): encoded[k] = [i.encode('latin1') for i in v] return encoded @@ -221,7 +228,7 @@ def recursive_unicode(obj): 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.items()) elif isinstance(obj, list): return list(recursive_unicode(i) for i in obj) elif isinstance(obj, tuple): diff --git a/tornado/httputil.py b/tornado/httputil.py index 8c7f684fa..1aea20822 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -30,6 +30,11 @@ try: except ImportError: from http.client import responses # py3 +try: + from urllib import urlencode # py2 +except ImportError: + from urllib.parse import urlencode # py3 + class HTTPHeaders(dict): """A dictionary that maintains Http-Header-Case for all keys. @@ -38,7 +43,7 @@ class HTTPHeaders(dict): value per key, with multiple values joined by a comma. >>> h = HTTPHeaders({"content-type": "text/html"}) - >>> h.keys() + >>> list(h.keys()) ['Content-Type'] >>> h["Content-Type"] 'text/html' @@ -96,7 +101,7 @@ class HTTPHeaders(dict): If a header has multiple values, multiple pairs will be returned with the same name. """ - for name, list in self._as_list.iteritems(): + for name, list in self._as_list.items(): for value in list: yield (name, value) @@ -123,7 +128,7 @@ class HTTPHeaders(dict): """Returns a dictionary from HTTP header text. >>> h = HTTPHeaders.parse("Content-Type: text/html\\r\\nContent-Length: 42\\r\\n") - >>> sorted(h.iteritems()) + >>> sorted(h.items()) [('Content-Length', '42'), ('Content-Type', 'text/html')] """ h = cls() @@ -156,7 +161,7 @@ class HTTPHeaders(dict): def update(self, *args, **kwargs): # dict.update bypasses our __setitem__ - for k, v in dict(*args, **kwargs).iteritems(): + for k, v in dict(*args, **kwargs).items(): self[k] = v def copy(self): @@ -195,7 +200,7 @@ def url_concat(url, args): return url if url[-1] not in ('?', '&'): url += '&' if ('?' in url) else '?' - return url + urllib.urlencode(args) + return url + urlencode(args) class HTTPFile(ObjectDict): @@ -220,7 +225,7 @@ def parse_body_arguments(content_type, body, arguments, files): """ if content_type.startswith("application/x-www-form-urlencoded"): uri_arguments = parse_qs_bytes(native_str(body)) - for name, values in uri_arguments.iteritems(): + for name, values in uri_arguments.items(): values = [v for v in values if v] if values: arguments.setdefault(name, []).extend(values) @@ -304,7 +309,7 @@ def _parse_header(line): """ parts = _parseparam(';' + line) - key = parts.next() + key = next(parts) pdict = {} for p in parts: i = p.find('=') diff --git a/tornado/ioloop.py b/tornado/ioloop.py index 3b6f27696..9154d11be 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -426,7 +426,7 @@ class PollIOLoop(IOLoop): self._closing = True self.remove_handler(self._waker.fileno()) if all_fds: - for fd in self._handlers.keys()[:]: + for fd in self._handlers.keys(): try: os.close(fd) except Exception: diff --git a/tornado/iostream.py b/tornado/iostream.py index 9bc1269f5..3059dd309 100644 --- a/tornado/iostream.py +++ b/tornado/iostream.py @@ -28,6 +28,7 @@ from __future__ import absolute_import, division, print_function, with_statement import collections import errno +import numbers import os import socket import sys @@ -146,7 +147,7 @@ class BaseIOStream(object): ``callback`` will be empty. """ self._set_read_callback(callback) - assert isinstance(num_bytes, (int, long)) + assert isinstance(num_bytes, numbers.Integral) self._read_bytes = num_bytes self._streaming_callback = stack_context.wrap(streaming_callback) self._try_inline_read() diff --git a/tornado/locale.py b/tornado/locale.py index b4abd5fe9..c6d3a8bb5 100644 --- a/tornado/locale.py +++ b/tornado/locale.py @@ -81,7 +81,7 @@ def set_default_locale(code): global _default_locale global _supported_locales _default_locale = code - _supported_locales = frozenset(_translations.keys() + [_default_locale]) + _supported_locales = frozenset(list(_translations.keys()) + [_default_locale]) def load_translations(directory): @@ -148,7 +148,7 @@ def load_translations(directory): continue _translations[locale].setdefault(plural, {})[english] = translation f.close() - _supported_locales = frozenset(_translations.keys() + [_default_locale]) + _supported_locales = frozenset(list(_translations.keys()) + [_default_locale]) gen_log.debug("Supported locales: %s", sorted(_supported_locales)) @@ -187,7 +187,7 @@ def load_gettext_translations(directory, domain): except Exception as e: gen_log.error("Cannot load translation for '%s': %s", lang, str(e)) continue - _supported_locales = frozenset(_translations.keys() + [_default_locale]) + _supported_locales = frozenset(list(_translations.keys()) + [_default_locale]) _use_gettext = True gen_log.debug("Supported locales: %s", sorted(_supported_locales)) diff --git a/tornado/netutil.py b/tornado/netutil.py index 6321013f3..ed768cc96 100644 --- a/tornado/netutil.py +++ b/tornado/netutil.py @@ -202,7 +202,7 @@ class TCPServer(object): Requests currently in progress may still continue after the server is stopped. """ - for fd, sock in self._sockets.iteritems(): + for fd, sock in self._sockets.items(): self.io_loop.remove_handler(fd) sock.close() diff --git a/tornado/options.py b/tornado/options.py index 102a55467..11b286472 100644 --- a/tornado/options.py +++ b/tornado/options.py @@ -222,7 +222,7 @@ class OptionParser(object): print("Usage: %s [OPTIONS]" % sys.argv[0], file=file) print("\nOptions:\n", file=file) by_group = {} - for option in self._options.itervalues(): + for option in self._options.values(): by_group.setdefault(option.group_name, []).append(option) for filename, o in sorted(by_group.items()): diff --git a/tornado/process.py b/tornado/process.py index 229a0d879..90294b4c3 100644 --- a/tornado/process.py +++ b/tornado/process.py @@ -38,6 +38,10 @@ try: except ImportError: multiprocessing = None +try: + long # py2 +except NameError: + long = int # py3 def cpu_count(): """Returns the number of processors on this machine.""" @@ -253,7 +257,7 @@ class Subprocess(object): @classmethod def _cleanup(cls): - for pid in cls._waiting.keys(): + for pid in list(cls._waiting.keys()): # make a copy cls._try_cleanup_process(pid) @classmethod diff --git a/tornado/template.py b/tornado/template.py index 78a523924..1c30e4579 100644 --- a/tornado/template.py +++ b/tornado/template.py @@ -190,7 +190,7 @@ import threading from tornado import escape from tornado.log import app_log -from tornado.util import bytes_type, ObjectDict, exec_in +from tornado.util import bytes_type, ObjectDict, exec_in, unicode_type try: from cStringIO import StringIO # py2 @@ -250,7 +250,7 @@ class Template(object): "linkify": escape.linkify, "datetime": datetime, "_utf8": escape.utf8, # for internal use - "_string_types": (unicode, bytes_type), + "_string_types": (unicode_type, bytes_type), # __name__ and __loader__ allow the traceback mechanism to find # the generated source code. "__name__": self.name.replace('.', '_'), diff --git a/tornado/test/auth_test.py b/tornado/test/auth_test.py index 138684d46..26249cffe 100644 --- a/tornado/test/auth_test.py +++ b/tornado/test/auth_test.py @@ -78,8 +78,6 @@ class OAuth1ClientRequestParametersHandler(RequestHandler, OAuthMixin): 'http://www.example.com/api/asdf', dict(key='uiop', secret='5678'), parameters=dict(foo='bar')) - import urllib - urllib.urlencode(params) self.write(params) diff --git a/tornado/test/escape_test.py b/tornado/test/escape_test.py index 71a4fa1ba..410d1c5f1 100644 --- a/tornado/test/escape_test.py +++ b/tornado/test/escape_test.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, print_function, with_statement import tornado.escape from tornado.escape import utf8, xhtml_escape, xhtml_unescape, url_escape, url_unescape, to_unicode, json_decode, json_encode -from tornado.util import b, u +from tornado.util import b, u, unicode_type from tornado.test.util import unittest linkify_tests = [ @@ -180,7 +180,7 @@ class EscapeTestCase(unittest.TestCase): # On python2 the escape methods should generally return the same # type as their argument self.assertEqual(type(xhtml_escape("foo")), str) - self.assertEqual(type(xhtml_escape(u("foo"))), unicode) + self.assertEqual(type(xhtml_escape(u("foo"))), unicode_type) def test_json_decode(self): # json_decode accepts both bytes and unicode, but strings it returns diff --git a/tornado/test/gen_test.py b/tornado/test/gen_test.py index 0c547fc46..4e270002c 100644 --- a/tornado/test/gen_test.py +++ b/tornado/test/gen_test.py @@ -262,7 +262,7 @@ class GenTest(AsyncTestCase): @gen.engine def outer(): - for i in xrange(10): + for i in range(10): yield gen.Task(inner) stack_increase = len(stack_context._state.contexts) - initial_stack_depth self.assertTrue(stack_increase <= 2) @@ -281,7 +281,7 @@ class GenTest(AsyncTestCase): @gen.engine def outer(): - for i in xrange(10): + for i in range(10): try: yield gen.Task(inner) except ZeroDivisionError: diff --git a/tornado/test/httpserver_test.py b/tornado/test/httpserver_test.py index fde10684b..8587bfd97 100644 --- a/tornado/test/httpserver_test.py +++ b/tornado/test/httpserver_test.py @@ -270,15 +270,15 @@ class TypeCheckHandler(RequestHandler): for field, expected_type in fields: self.check_type(field, getattr(self.request, field), expected_type) - self.check_type('header_key', self.request.headers.keys()[0], str) - self.check_type('header_value', self.request.headers.values()[0], str) + self.check_type('header_key', list(self.request.headers.keys())[0], str) + self.check_type('header_value', list(self.request.headers.values())[0], str) - self.check_type('cookie_key', self.request.cookies.keys()[0], str) - self.check_type('cookie_value', self.request.cookies.values()[0].value, str) + self.check_type('cookie_key', list(self.request.cookies.keys())[0], str) + self.check_type('cookie_value', list(self.request.cookies.values())[0].value, str) # secure cookies - self.check_type('arg_key', self.request.arguments.keys()[0], str) - self.check_type('arg_value', self.request.arguments.values()[0][0], bytes_type) + self.check_type('arg_key', list(self.request.arguments.keys())[0], str) + self.check_type('arg_value', list(self.request.arguments.values())[0][0], bytes_type) def post(self): self.check_type('body', self.request.body, bytes_type) @@ -441,7 +441,7 @@ class KeepAliveTest(AsyncHTTPTestCase): def get(self): # 512KB should be bigger than the socket buffers so it will # be written out in chunks. - self.write(''.join(chr(i % 256) * 1024 for i in xrange(512))) + self.write(''.join(chr(i % 256) * 1024 for i in range(512))) class FinishOnCloseHandler(RequestHandler): @asynchronous diff --git a/tornado/test/iostream_test.py b/tornado/test/iostream_test.py index 13bb5917a..e2b985133 100644 --- a/tornado/test/iostream_test.py +++ b/tornado/test/iostream_test.py @@ -375,7 +375,7 @@ class TestIOStreamMixin(object): # but there was no pypy for 2.5 pass NUM_KB = 4096 - for i in xrange(NUM_KB): + for i in range(NUM_KB): client.write(b("A") * 1024) client.write(b("\r\n")) server.read_until(b("\r\n"), self.stop) diff --git a/tornado/test/locale_test.py b/tornado/test/locale_test.py index 5527a63b4..8e7f6e019 100644 --- a/tornado/test/locale_test.py +++ b/tornado/test/locale_test.py @@ -4,7 +4,7 @@ import os import tornado.locale from tornado.escape import utf8 from tornado.test.util import unittest -from tornado.util import b, u +from tornado.util import b, u, unicode_type class TranslationLoaderTest(unittest.TestCase): @@ -45,6 +45,6 @@ class TranslationLoaderTest(unittest.TestCase): class LocaleDataTest(unittest.TestCase): def test_non_ascii_name(self): name = tornado.locale.LOCALE_NAMES['es_LA']['name'] - self.assertTrue(isinstance(name, unicode)) + self.assertTrue(isinstance(name, unicode_type)) self.assertEqual(name, u('Espa\u00f1ol')) self.assertEqual(utf8(name), b('Espa\xc3\xb1ol')) diff --git a/tornado/test/log_test.py b/tornado/test/log_test.py index 3bcf4a1a8..7100807ea 100644 --- a/tornado/test/log_test.py +++ b/tornado/test/log_test.py @@ -27,7 +27,7 @@ from tornado.escape import utf8 from tornado.log import LogFormatter, define_logging_options, enable_pretty_logging from tornado.options import OptionParser from tornado.test.util import unittest -from tornado.util import b, u, bytes_type +from tornado.util import b, u, bytes_type, basestring_type @contextlib.contextmanager def ignore_bytes_warning(): @@ -97,7 +97,7 @@ class LogFormatterTest(unittest.TestCase): def test_utf8_logging(self): self.logger.error(u("\u00e9").encode("utf8")) - if issubclass(bytes_type, basestring): + if issubclass(bytes_type, basestring_type): # on python 2, utf8 byte strings (and by extension ascii byte # strings) are passed through as-is. self.assertEqual(self.get_output(), utf8(u("\u00e9"))) diff --git a/tornado/test/template_test.py b/tornado/test/template_test.py index 2ad9876a9..f2cd1be95 100644 --- a/tornado/test/template_test.py +++ b/tornado/test/template_test.py @@ -7,7 +7,7 @@ from tornado.escape import utf8, native_str, to_unicode from tornado.template import Template, DictLoader, ParseError, Loader from tornado.testing import ExpectLog from tornado.test.util import unittest -from tornado.util import b, u, bytes_type, ObjectDict +from tornado.util import b, u, bytes_type, ObjectDict, unicode_type class TemplateTest(unittest.TestCase): @@ -79,7 +79,7 @@ class TemplateTest(unittest.TestCase): # test simulates unicode characters appearing directly in the # template file (with utf8 encoding), i.e. \u escapes would not # be used in the template file itself. - if str is unicode: + if str is unicode_type: # python 3 needs a different version of this test since # 2to3 doesn't run on template internals template = Template(utf8(u('{{ "\u00e9" }}'))) diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index 5a3ebd1bb..aaf71e8fa 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -7,7 +7,7 @@ from tornado.simple_httpclient import SimpleAsyncHTTPClient from tornado.template import DictLoader from tornado.testing import AsyncHTTPTestCase, ExpectLog from tornado.test.util import unittest -from tornado.util import b, u, bytes_type, ObjectDict +from tornado.util import b, u, bytes_type, ObjectDict, unicode_type from tornado.web import RequestHandler, authenticated, Application, asynchronous, url, HTTPError, StaticFileHandler, _create_signature, create_signed_value, ErrorHandler import binascii @@ -278,11 +278,11 @@ class EchoHandler(RequestHandler): raise Exception("incorrect type for value: %r" % type(value)) for value in self.get_arguments(key): - if type(value) != unicode: + if type(value) != unicode_type: raise Exception("incorrect type for value: %r" % type(value)) for arg in path_args: - if type(arg) != unicode: + if type(arg) != unicode_type: raise Exception("incorrect type for path arg: %r" % type(arg)) self.write(dict(path=self.request.path, path_args=path_args, @@ -333,13 +333,13 @@ class TypeCheckHandler(RequestHandler): # get_argument is an exception from the general rule of using # type str for non-body data mainly for historical reasons. - self.check_type('argument', self.get_argument('foo'), unicode) - self.check_type('cookie_key', self.cookies.keys()[0], str) - self.check_type('cookie_value', self.cookies.values()[0].value, str) + self.check_type('argument', self.get_argument('foo'), unicode_type) + self.check_type('cookie_key', list(self.cookies.keys())[0], str) + self.check_type('cookie_value', list(self.cookies.values())[0].value, str) # Secure cookies return bytes because they can contain arbitrary # data, but regular cookies are native strings. - if self.cookies.keys() != ['asdf']: + if list(self.cookies.keys()) != ['asdf']: raise Exception("unexpected values for cookie keys: %r" % self.cookies.keys()) self.check_type('get_secure_cookie', self.get_secure_cookie('asdf'), bytes_type) @@ -355,11 +355,11 @@ class TypeCheckHandler(RequestHandler): def get(self, path_component): # path_component uses type unicode instead of str for consistency # with get_argument() - self.check_type('path_component', path_component, unicode) + self.check_type('path_component', path_component, unicode_type) self.write(self.errors) def post(self, path_component): - self.check_type('path_component', path_component, unicode) + self.check_type('path_component', path_component, unicode_type) self.write(self.errors) def check_type(self, name, obj, expected_type): @@ -383,7 +383,7 @@ class DecodeArgHandler(RequestHandler): def describe(s): if type(s) == bytes_type: return ["bytes", native_str(binascii.b2a_hex(s))] - elif type(s) == unicode: + elif type(s) == unicode_type: return ["unicode", s] raise Exception("unknown type") self.write({'path': describe(arg), diff --git a/tornado/testing.py b/tornado/testing.py index 63ececf94..0e3db883a 100644 --- a/tornado/testing.py +++ b/tornado/testing.py @@ -36,7 +36,7 @@ except ImportError: SimpleAsyncHTTPClient = None from tornado.log import gen_log from tornado.stack_context import ExceptionStackContext -from tornado.util import raise_exc_info +from tornado.util import raise_exc_info, basestring_type import logging import os import re @@ -414,7 +414,7 @@ class ExpectLog(logging.Filter): :param required: If true, an exeption will be raised if the end of the ``with`` statement is reached without matching any log entries. """ - if isinstance(logger, basestring): + if isinstance(logger, basestring_type): logger = logging.getLogger(logger) self.logger = logger self.regex = re.compile(regex) diff --git a/tornado/web.py b/tornado/web.py index bc6d944f4..fbc7e2dfa 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -62,6 +62,7 @@ import hashlib import hmac import itertools import mimetypes +import numbers import os.path import re import stat @@ -71,7 +72,6 @@ import time import tornado import traceback import types -import urllib import uuid from tornado import escape @@ -81,7 +81,7 @@ from tornado.log import access_log, app_log, gen_log from tornado import stack_context from tornado import template from tornado.escape import utf8, _unicode -from tornado.util import b, bytes_type, import_object, ObjectDict, raise_exc_info +from tornado.util import b, bytes_type, import_object, ObjectDict, raise_exc_info, unicode_type try: from io import BytesIO # python 3 @@ -98,6 +98,11 @@ try: except ImportError: import urllib.parse as urlparse # py3 +try: + from urllib import urlencode # py2 +except ImportError: + from urllib.parse import urlencode # py3 + class RequestHandler(object): """Subclass this class and define get() or post() to make a handler. @@ -123,14 +128,14 @@ class RequestHandler(object): self.path_args = None self.path_kwargs = None self.ui = ObjectDict((n, self._ui_method(m)) for n, m in - application.ui_methods.iteritems()) + application.ui_methods.items()) # UIModules are available as both `modules` and `_modules` in the # template namespace. Historically only `modules` was available # but could be clobbered by user additions to the namespace. # The template {% module %} directive looks in `_modules` to avoid # possible conflicts. self.ui["_modules"] = ObjectDict((n, self._ui_module(n, m)) for n, m in - application.ui_modules.iteritems()) + application.ui_modules.items()) self.ui["modules"] = self.ui["_modules"] self.clear() # Check since connection is not available in WSGI @@ -302,9 +307,9 @@ class RequestHandler(object): def _convert_header_value(self, value): if isinstance(value, bytes_type): pass - elif isinstance(value, unicode): + elif isinstance(value, unicode_type): value = value.encode('utf-8') - elif isinstance(value, (int, long)): + elif isinstance(value, numbers.Integral): # return immediately since we know the converted value will be safe return str(value) elif isinstance(value, datetime.datetime): @@ -349,7 +354,7 @@ class RequestHandler(object): values = [] for v in self.request.arguments.get(name, []): v = self.decode_argument(v, name=name) - if isinstance(v, unicode): + if isinstance(v, unicode_type): # Get rid of any weird control chars (unless decoding gave # us bytes, in which case leave it alone) v = re.sub(r"[\x00-\x08\x0e-\x1f]", " ", v) @@ -415,7 +420,7 @@ class RequestHandler(object): timestamp, localtime=False, usegmt=True) if path: morsel["path"] = path - for k, v in kwargs.iteritems(): + for k, v in kwargs.items(): if k == 'max_age': k = 'max-age' morsel[k] = v @@ -530,13 +535,13 @@ class RequestHandler(object): css_files = [] html_heads = [] html_bodies = [] - for module in getattr(self, "_active_modules", {}).itervalues(): + for module in getattr(self, "_active_modules", {}).values(): embed_part = module.embedded_javascript() if embed_part: js_embed.append(utf8(embed_part)) file_part = module.javascript_files() if file_part: - if isinstance(file_part, (unicode, bytes_type)): + if isinstance(file_part, (unicode_type, bytes_type)): js_files.append(file_part) else: js_files.extend(file_part) @@ -545,7 +550,7 @@ class RequestHandler(object): css_embed.append(utf8(embed_part)) file_part = module.css_files() if file_part: - if isinstance(file_part, (unicode, bytes_type)): + if isinstance(file_part, (unicode_type, bytes_type)): css_files.append(file_part) else: css_files.extend(file_part) @@ -1062,7 +1067,7 @@ class RequestHandler(object): raise HTTPError(405) self.path_args = [self.decode_argument(arg) for arg in args] self.path_kwargs = dict((k, self.decode_argument(v, name=k)) - for (k, v) in kwargs.iteritems()) + for (k, v) in kwargs.items()) # If XSRF cookies are turned on, reject form submissions without # the proper cookie if self.request.method not in ("GET", "HEAD", "OPTIONS") and \ @@ -1083,7 +1088,7 @@ class RequestHandler(object): str(self._status_code) + " " + reason)] lines.extend([(utf8(n) + b(": ") + utf8(v)) for n, v in - itertools.chain(self._headers.iteritems(), self._list_headers)]) + itertools.chain(self._headers.items(), self._list_headers)]) if hasattr(self, "_new_cookie"): for cookie in self._new_cookie.values(): lines.append(utf8("Set-Cookie: " + cookie.OutputString(None))) @@ -1389,7 +1394,7 @@ class Application(object): for m in methods: self._load_ui_methods(m) else: - for name, fn in methods.iteritems(): + for name, fn in methods.items(): if not name.startswith("_") and hasattr(fn, "__call__") \ and name[0].lower() == name[0]: self.ui_methods[name] = fn @@ -1403,7 +1408,7 @@ class Application(object): self._load_ui_modules(m) else: assert isinstance(modules, dict) - for name, cls in modules.iteritems(): + for name, cls in modules.items(): try: if issubclass(cls, UIModule): self.ui_modules[name] = cls @@ -1441,7 +1446,7 @@ class Application(object): if spec.regex.groupindex: kwargs = dict( (str(k), unquote(v)) - for (k, v) in match.groupdict().iteritems()) + for (k, v) in match.groupdict().items()) else: args = [unquote(s) for s in match.groups()] break @@ -1853,7 +1858,7 @@ def authenticated(method): next_url = self.request.full_url() else: next_url = self.request.uri - url += "?" + urllib.urlencode(dict(next=next_url)) + url += "?" + urlencode(dict(next=next_url)) self.redirect(url) return raise HTTPError(403) @@ -1961,7 +1966,7 @@ class TemplateModule(UIModule): def javascript_files(self): result = [] for f in self._get_resources("javascript_files"): - if isinstance(f, (unicode, bytes_type)): + if isinstance(f, (unicode_type, bytes_type)): result.append(f) else: result.extend(f) @@ -1973,7 +1978,7 @@ class TemplateModule(UIModule): def css_files(self): result = [] for f in self._get_resources("css_files"): - if isinstance(f, (unicode, bytes_type)): + if isinstance(f, (unicode_type, bytes_type)): result.append(f) else: result.extend(f) @@ -2058,7 +2063,7 @@ class URLSpec(object): return self._path converted_args = [] for a in args: - if not isinstance(a, (unicode, bytes_type)): + if not isinstance(a, (unicode_type, bytes_type)): a = str(a) converted_args.append(escape.url_escape(utf8(a))) return self._path % tuple(converted_args) diff --git a/tornado/wsgi.py b/tornado/wsgi.py index 3f38f67a3..e64d48138 100644 --- a/tornado/wsgi.py +++ b/tornado/wsgi.py @@ -53,6 +53,11 @@ try: except ImportError: import http.cookies as Cookie # py3 +try: + import urllib.parse as urllib_parse # py3 +except ImportError: + import urllib as urllib_parse + # PEP 3333 specifies that WSGI on python 3 generally deals with byte strings # that are smuggled inside objects of type unicode (via the latin1 encoding). # These functions are like those in the tornado.escape module, but defined @@ -118,7 +123,7 @@ class WSGIApplication(web.Application): assert handler._finished reason = handler._reason status = str(handler._status_code) + " " + reason - headers = handler._headers.items() + handler._list_headers + headers = list(handler._headers.items()) + handler._list_headers if hasattr(handler, "_new_cookie"): for cookie in handler._new_cookie.values(): headers.append(("Set-Cookie", cookie.OutputString(None))) @@ -132,8 +137,8 @@ class HTTPRequest(object): def __init__(self, environ): """Parses the given WSGI environ to construct the request.""" self.method = environ["REQUEST_METHOD"] - self.path = urllib.quote(from_wsgi_str(environ.get("SCRIPT_NAME", ""))) - self.path += urllib.quote(from_wsgi_str(environ.get("PATH_INFO", ""))) + self.path = urllib_parse.quote(from_wsgi_str(environ.get("SCRIPT_NAME", ""))) + self.path += urllib_parse.quote(from_wsgi_str(environ.get("PATH_INFO", ""))) self.uri = self.path self.arguments = {} self.query = environ.get("QUERY_STRING", "") @@ -296,7 +301,7 @@ class WSGIContainer(object): environ["CONTENT_TYPE"] = request.headers.pop("Content-Type") if "Content-Length" in request.headers: environ["CONTENT_LENGTH"] = request.headers.pop("Content-Length") - for key, value in request.headers.iteritems(): + for key, value in request.headers.items(): environ["HTTP_" + key.replace("-", "_").upper()] = value return environ