diff --git a/.travis.yml b/.travis.yml
index bf850b3c58..114f0be9af 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,35 @@
language: python
+
+# because python3.7 is not available on trusty
+# @see https://github.com/travis-ci/travis-ci/issues/9815
+dist: xenial
+
+# Test on python3
python:
-- '2.7'
+ - 3.5 # debian
+ - 3.6 # ubuntu / amazonlinux
+ - 3.7 # fedora
+ - nightly
+
+# Allow failures on cpython nightly build
+matrix:
+ fast_finish: true
+ allow_failures:
+ - python: nightly
+
install:
- pip install -r cyclone/tests/test_requirements.txt
- pip install coveralls
+
+# cyclone has to be installed
+# so as trial (twisted test framework) could run
+before_script: python setup.py install
+
script: coverage run `which trial` cyclone
after_success: coveralls
+
+# deploy on pypi
+# only on 3.6 (avoid multiple deployment)
deploy:
provider: pypi
user: fiorix
@@ -14,4 +38,5 @@ deploy:
on:
tags: true
branch: master
+ condition: $TRAVIS_PYTHON_VERSION = 3.6
distributions: "sdist bdist_wheel"
diff --git a/README.md b/README.md
index 2240b200b1..a252e41fcf 100644
--- a/README.md
+++ b/README.md
@@ -2,10 +2,14 @@ Cyclone
=======
[![Build Status](https://travis-ci.org/fiorix/cyclone.svg?branch=master)](https://travis-ci.org/fiorix/cyclone)
+[![Coverage Status](https://coveralls.io/repos/github/fiorix/cyclone/badge.svg?branch=master)](https://coveralls.io/github/fiorix/cyclone?branch=master)
+[![Supported Python versions](https://pypi.org/project/cyclone)](https://img.shields.io/pypi/pyversions/cyclone.svg)
Cyclone is a web server framework for Python, that implements the Tornado API
as a Twisted protocol.
+:warning: `cyclone` does not support `python` **2.x** anymore :warning:
+
See http://cyclone.io for details.
Installation
diff --git a/appskel/default/modname/config.py b/appskel/default/modname/config.py
index 0ec1d93487..b130352ba1 100644
--- a/appskel/default/modname/config.py
+++ b/appskel/default/modname/config.py
@@ -79,6 +79,6 @@ def my_parse_config(filename):
def parse_config(filename):
try:
return my_parse_config(filename)
- except Exception as e:
+ except Exception, e:
print("Error parsing %s: %s" % (filename, e))
sys.exit(1)
diff --git a/appskel/default/modname/storage.py b/appskel/default/modname/storage.py
index 23d7df9f1d..26abbeaf3c 100644
--- a/appskel/default/modname/storage.py
+++ b/appskel/default/modname/storage.py
@@ -68,7 +68,7 @@ def setup(cls, conf):
def _ping_mysql():
try:
yield cls.mysql.runQuery("select 1")
- except Exception as e:
+ except Exception, e:
log.msg("MySQL ping error:", e)
else:
if conf["mysql_settings"].debug:
diff --git a/appskel/default/modname/views.py b/appskel/default/modname/views.py
index 332c832b4e..9dae22cc1d 100644
--- a/appskel/default/modname/views.py
+++ b/appskel/default/modname/views.py
@@ -50,7 +50,7 @@ def get(self):
if self.redis:
try:
response = yield self.redis.get("foo")
- except Exception as e:
+ except Exception, e:
log.msg("Redis query failed: %s" % str(e))
raise cyclone.web.HTTPError(503) # Service Unavailable
else:
@@ -65,7 +65,7 @@ def get(self):
if self.mysql:
try:
response = yield self.mysql.runQuery("select now()")
- except Exception as e:
+ except Exception, e:
log.msg("MySQL query failed: %s" % str(e))
raise cyclone.web.HTTPError(503) # Service Unavailable
else:
diff --git a/appskel/signup/modname/config.py b/appskel/signup/modname/config.py
index 285acd50f1..dbcc3a2d31 100644
--- a/appskel/signup/modname/config.py
+++ b/appskel/signup/modname/config.py
@@ -88,6 +88,6 @@ def my_parse_config(filename):
def parse_config(filename):
try:
return my_parse_config(filename)
- except Exception as e:
+ except Exception, e:
print("Error parsing %s: %s" % (filename, e))
sys.exit(1)
diff --git a/appskel/signup/modname/storage.py b/appskel/signup/modname/storage.py
index 4209a584c7..1d41f15d4c 100644
--- a/appskel/signup/modname/storage.py
+++ b/appskel/signup/modname/storage.py
@@ -104,7 +104,7 @@ def setup(cls, conf):
def _ping_mysql():
try:
yield cls.mysql.runQuery("select 1")
- except Exception as e:
+ except Exception, e:
log.msg("MySQL ping error:", e)
else:
if conf["mysql_settings"].debug:
diff --git a/appskel/signup/modname/views.py b/appskel/signup/modname/views.py
index f6cb07cacd..4e85fa010a 100644
--- a/appskel/signup/modname/views.py
+++ b/appskel/signup/modname/views.py
@@ -181,7 +181,7 @@ def post(self):
try:
r = yield cyclone.mail.sendmail(self.settings.email_settings, msg)
- except Exception as e:
+ except Exception, e:
# delete password from redis
yield self.redis.delete(k)
@@ -337,7 +337,7 @@ def post(self):
try:
r = yield cyclone.mail.sendmail(self.settings.email_settings, msg)
- except Exception as e:
+ except Exception, e:
# do not delete from redis
# yield self.redis.delete(k)
diff --git a/cyclone/auth.py b/cyclone/auth.py
index d9aeb2bbb7..c87499d62f 100644
--- a/cyclone/auth.py
+++ b/cyclone/auth.py
@@ -57,23 +57,10 @@ def _on_auth(self, user):
import hashlib
import hmac
import time
+import urllib
+from urllib import parse as urllib_parse
import uuid
-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
-
-try:
- long # py2
-except NameError:
- long = int # py3
-
class OpenIdMixin(object):
"""Abstract implementation of OpenID and Attribute Exchange.
@@ -114,7 +101,7 @@ def get_authenticated_user(self, callback):
postdata=urllib_parse.urlencode(args)).addBoth(callback)
def _openid_args(self, callback_uri, ax_attrs=[], oauth_scope=None):
- url = urlparse.urljoin(self.request.full_url(), callback_uri)
+ url = urllib_parse.urljoin(self.request.full_url(), callback_uri)
args = {
"openid.ns": "http://specs.openid.net/auth/2.0",
"openid.claimed_id":
@@ -122,7 +109,7 @@ def _openid_args(self, callback_uri, ax_attrs=[], oauth_scope=None):
"openid.identity":
"http://specs.openid.net/auth/2.0/identifier_select",
"openid.return_to": url,
- "openid.realm": urlparse.urljoin(url, '/'),
+ "openid.realm": urllib_parse.urljoin(url, '/'),
"openid.mode": "checkid_setup",
}
if ax_attrs:
@@ -291,7 +278,7 @@ def _oauth_request_token_url(self, callback_uri=None, extra_params=None):
if callback_uri == "oob":
args["oauth_callback"] = "oob"
elif callback_uri:
- args["oauth_callback"] = urlparse.urljoin(
+ args["oauth_callback"] = urllib_parse.urljoin(
self.request.full_url(), callback_uri)
if extra_params:
args.update(extra_params)
@@ -314,7 +301,7 @@ def _on_request_token(self, authorize_url, callback_uri, response):
self.finish(authorize_url + "?" + urllib_parse.urlencode(args))
return
elif callback_uri:
- args["oauth_callback"] = urlparse.urljoin(
+ args["oauth_callback"] = urllib_parse.urljoin(
self.request.full_url(), callback_uri)
self.redirect(authorize_url + "?" + urllib_parse.urlencode(args))
@@ -820,11 +807,11 @@ def authenticate_redirect(self, callback_uri=None, cancel_uri=None,
"v": "1.0",
"fbconnect": "true",
"display": "page",
- "next": urlparse.urljoin(self.request.full_url(), callback_uri),
+ "next": urllib_parse.urljoin(self.request.full_url(), callback_uri),
"return_session": "true",
}
if cancel_uri:
- args["cancel_url"] = urlparse.urljoin(
+ args["cancel_url"] = urllib_parse.urljoin(
self.request.full_url(), cancel_uri)
if extended_permissions:
if isinstance(extended_permissions, (unicode_type, bytes_type)):
@@ -908,7 +895,7 @@ def _on_stream(self, stream):
args["api_key"] = self.settings["facebook_api_key"]
args["v"] = "1.0"
args["method"] = method
- args["call_id"] = str(long(time.time() * 1e6))
+ args["call_id"] = str(int(time.time() * 1e6))
args["format"] = "json"
args["sig"] = self._signature(args)
url = "http://api.facebook.com/restserver.php?" + \
@@ -940,7 +927,7 @@ def _parse_response(self, callback, response):
return
try:
json = escape.json_decode(response.body)
- except:
+ except Exception:
log.msg("Invalid JSON from Facebook: %r" % response.body)
callback(None)
return
@@ -954,7 +941,7 @@ def _parse_response(self, callback, response):
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, str):
body = body.encode("utf-8")
return hashlib.md5(body).hexdigest()
@@ -966,7 +953,7 @@ class FacebookGraphMixin(OAuth2Mixin):
_OAUTH_NO_CALLBACKS = False
def get_authenticated_user(self, redirect_uri, client_id, client_secret,
- code, callback, extra_fields=None):
+ code, callback, extra_fields=None):
"""Handles the login for the Facebook user, returning a user object.
Example usage::
@@ -1014,7 +1001,7 @@ def _on_login(self, user):
def _on_access_token(self, redirect_uri, client_id, client_secret,
callback, fields, response):
if response.error:
- log.warning('Facebook auth error: %s' % str(response))
+ log.msg('Facebook auth error: %s' % str(response))
callback(None)
return
@@ -1045,7 +1032,7 @@ def _on_get_user_info(self, callback, session, fields, user):
callback(fieldmap)
def facebook_request(self, path, callback, access_token=None,
- post_args=None, **args):
+ post_args=None, **args):
"""Fetches the given relative API path, e.g., "/btaylor/picture"
If the request is a POST, post_args should be provided. Query
@@ -1090,14 +1077,13 @@ def _on_post(self, new_entry):
callback = self.async_callback(self._on_facebook_request, callback)
if post_args is not None:
httpclient.fetch(url, method="POST",
- postdata=urllib_parse.urlencode(post_args)).addCallback(callback)
+ postdata=urllib_parse.urlencode(post_args)).addCallback(callback)
else:
httpclient.fetch(url).addCallback(callback)
def _on_facebook_request(self, callback, response):
if response.error:
- log.warning("Error response %s fetching %s", response.error,
- response.request.url)
+ log.msg("Error response %s fetching %s", response.error, response.request.url)
callback(None)
return
callback(escape.json_decode(response.body))
@@ -1108,7 +1094,7 @@ def _oauth_signature(consumer_token, method, url, parameters={}, token=None):
See http://oauth.net/core/1.0/#signing_process
"""
- parts = urlparse.urlparse(url)
+ parts = urllib_parse.urlparse(url)
scheme, netloc, path = parts[:3]
normalized_url = scheme.lower() + "://" + netloc.lower() + path
@@ -1133,7 +1119,7 @@ def _oauth10a_signature(consumer_token,
See http://oauth.net/core/1.0a/#signing_process
"""
- parts = urlparse.urlparse(url)
+ parts = urllib_parse.urlparse(url)
scheme, netloc, path = parts[:3]
normalized_url = scheme.lower() + "://" + netloc.lower() + path
@@ -1144,10 +1130,8 @@ def _oauth10a_signature(consumer_token,
for k, v in sorted(parameters.items())))
base_string = "&".join(_oauth_escape(e) for e in base_elems)
- 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_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 = "&".join(key_elems)
hash = hmac.new(key, escape.utf8(base_string), hashlib.sha1)
@@ -1161,7 +1145,7 @@ def _oauth_escape(val):
def _oauth_parse_response(body):
- p = escape.parse_qs(body, keep_blank_values=False)
+ p = urllib_parse.parse_qs(body, keep_blank_values=False)
token = dict(key=p["oauth_token"][0], secret=p["oauth_token_secret"][0])
# Add the extra parameters the Provider included to the token
diff --git a/cyclone/escape.py b/cyclone/escape.py
index 61d491e40f..f5f8b1e93d 100644
--- a/cyclone/escape.py
+++ b/cyclone/escape.py
@@ -22,59 +22,18 @@
"""
from __future__ import absolute_import, division, with_statement
-
-try:
- from urllib.parse import parse_qs as _parse_qs # py3
-except ImportError:
- from urlparse import parse_qs as _parse_qs # Python 2.6+
-
-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
-
+from html import entities as html_entities
import re
-import sys
-
+import json
+from urllib import parse as urllib_parse
from cyclone.util import basestring_type
from cyclone.util import bytes_type
from cyclone.util import unicode_type
-from cyclone.util import unicode_char_type
-
-try:
- from urlparse import parse_qs # Python 2.6+
-except ImportError:
- from cgi import parse_qs
-
-# json module is in the standard library as of python 2.6; fall back to
-# simplejson if present for older versions.
-try:
- import json
- assert hasattr(json, "loads") and hasattr(json, "dumps")
- _json_decode = json.loads
- _json_encode = json.dumps
-except Exception: # pragma: nocover
- try:
- import simplejson
- _json_decode = lambda s: simplejson.loads(_unicode(s))
- _json_encode = lambda v: simplejson.dumps(v)
- except ImportError:
- try:
- # For Google AppEngine
- from django.utils import simplejson
- _json_decode = lambda s: simplejson.loads(_unicode(s))
- _json_encode = lambda v: simplejson.dumps(v)
- except ImportError:
- def _json_decode(s):
- raise NotImplementedError(
- "A JSON parser is required, e.g., simplejson at "
- "http://pypi.python.org/pypi/simplejson/")
- _json_encode = _json_decode
+
+
+_json_decode = json.loads
+_json_encode = json.dumps
+
_XHTML_ESCAPE_RE = re.compile('[&<>"\']')
@@ -83,8 +42,7 @@ def _json_decode(s):
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)], to_basestring(value))
+ return _XHTML_ESCAPE_RE.sub(lambda match: _XHTML_ESCAPE_DICT[match.group(0)], to_basestring(value))
def xhtml_unescape(value):
@@ -116,55 +74,22 @@ def squeeze(value):
def url_escape(value):
"""Returns a valid URL-encoded version of the given value."""
- return urllib_parse.quote_plus(utf8(value))
+ return urllib_parse.quote_plus(value)
-if sys.version_info[0] < 3:
- def url_unescape(value, encoding='utf-8'):
- """Decodes the given value from a URL.
+def url_unescape(value, encoding='utf-8'):
+ """Decodes the given value from a URL.
- The argument may be either a byte or unicode string.
+ The argument may be either a byte or unicode string.
- If encoding is None, the result will be a byte string. Otherwise,
- the result is a unicode string in the specified encoding.
- """
- if encoding is None:
- return urllib_parse.unquote_plus(utf8(value))
- else:
- return unicode_type(urllib_parse.unquote_plus(utf8(value)), encoding)
+ If encoding is None, the result will be a byte string. Otherwise,
+ the result is a unicode string in the specified encoding.
+ """
+ return urllib_parse.unquote_plus(value)
- parse_qs_bytes = parse_qs
-else:
- def url_unescape(value, encoding='utf-8'):
- """Decodes the given value from a URL.
- The argument may be either a byte or unicode string.
+parse_qs_bytes = urllib_parse.parse_qs
- If encoding is None, the result will be a byte string. Otherwise,
- the result is a unicode string in the specified encoding.
- """
- if encoding is None:
- value = to_basestring(value).replace('+', ' ')
- return urllib_parse.unquote_to_bytes(value)
- else:
- 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
- values as byte strings.
-
- Keys still become type str (interpreted as latin1 in python3!)
- because it's too painful to keep them as byte strings in
- python3 and in practice they're nearly always ascii anyway.
- """
- # This is gross, but python3 doesn't give us another way.
- # Latin1 is the universal donor of character encodings.
- result = _parse_qs(qs, keep_blank_values, strict_parsing,
- encoding='latin1', errors='strict')
- encoded = {}
- for k, v in result.items():
- encoded[k] = [i.encode('latin1') for i in v]
- return encoded
_UTF8_TYPES = (bytes, type(None))
@@ -180,6 +105,7 @@ def utf8(value):
assert isinstance(value, unicode_type)
return value.encode("utf-8")
+
_TO_UNICODE_TYPES = (unicode_type, type(None))
@@ -191,7 +117,7 @@ def to_unicode(value):
"""
if isinstance(value, _TO_UNICODE_TYPES):
return value
- assert isinstance(value, bytes_type)
+ #assert isinstance(value, bytes_type)
return value.decode("utf-8")
# to_unicode was previously named _unicode not because it was private,
@@ -230,7 +156,7 @@ def recursive_unicode(obj):
"""
if isinstance(obj, dict):
return dict((recursive_unicode(k), recursive_unicode(v))
- for (k, v) in obj.iteritems())
+ for (k, v) in obj.items())
elif isinstance(obj, list):
return list(recursive_unicode(i) for i in obj)
elif isinstance(obj, tuple):
@@ -247,10 +173,12 @@ def recursive_unicode(obj):
# This regex should avoid those problems.
# Use to_unicode instead of tornado.util.u - we don't want backslashes getting
# processed as escapes.
-_URL_RE = re.compile(to_unicode(r"""\b((?:([\w-]+):(/{1,3})|www[.])"""
- r"""(?:(?:(?:[^\s&()]|&|")*"""
- r"""(?:[^!"#$%&'()*+,.:;<=>?@\[\]^`{|}~\s]))"""
- r"""|(?:\((?:[^\s&()]|&|")*\)))+)"""))
+
+
+_URL_RE = re.compile(r"""\b((?:([\w-]+):(/{1,3})|www[.])"""
+ r"""(?:(?:(?:[^\s&()]|&|")*"""
+ r"""(?:[^!"#$%&'()*+,.:;<=>?@\[\]^`{|}~\s]))"""
+ r"""|(?:\((?:[^\s&()]|&|")*\)))+)""")
def linkify(text, shorten=False, extra_params="",
@@ -339,8 +267,7 @@ def make_link(m):
# have a status bar, such as Safari by default)
params += ' title="%s"' % href
- return ('%s'.decode("unicode_escape") %
- (href, params, url))
+ return '%s' % (href, params, url)
# First HTML-escape so that our strings are all safe.
# The regex is modified to avoid character entites other than & so
@@ -352,7 +279,7 @@ def make_link(m):
def _convert_entity(m):
if m.group(1) == "#":
try:
- return unicode_char_type(int(m.group(2)))
+ return chr(int(m.group(2)))
except ValueError:
return "%s;" % m.group(2)
try:
@@ -363,8 +290,9 @@ def _convert_entity(m):
def _build_unicode_map():
unicode_map = {}
- for name, value in htmlentitydefs.name2codepoint.items():
- unicode_map[name] = unicode_char_type(value)
+ for name, value in html_entities.name2codepoint.items():
+ unicode_map[name] = chr(value)
return unicode_map
+
_HTML_UNICODE_MAP = _build_unicode_map()
diff --git a/cyclone/httpclient.py b/cyclone/httpclient.py
index 616556dd44..865dd2fc6e 100644
--- a/cyclone/httpclient.py
+++ b/cyclone/httpclient.py
@@ -18,19 +18,6 @@
"""Non-blocking HTTP client"""
import functools
-
-try:
- from types import ListType
-except ImportError:
- # python 3 compatibility
- ListType = list
-
-try:
- from types import DictType
-except ImportError:
- # python 3 compatibility
- DictType = dict
-
from cyclone import escape
from cyclone.web import HTTPError
@@ -49,6 +36,7 @@
agent = Agent(reactor)
proxy_agent = ProxyAgent(None, reactor)
+
@implementer(IBodyProducer)
class StringProducer(object):
def __init__(self, body):
@@ -127,7 +115,7 @@ def fetch(self):
headers = dict(response.headers.getAllRawHeaders())
location = headers.get("Location")
if location:
- if isinstance(location, ListType):
+ if isinstance(location, list):
location = location[0]
#print("redirecting to:", location)
@@ -140,7 +128,6 @@ def fetch(self):
break
else:
break
-
response.error = response.code >= 400
response.headers = dict(response.headers.getAllRawHeaders())
# HTTP 204 and 304 responses have no body
@@ -203,7 +190,7 @@ def fetch(url, *args, **kwargs):
port string as second member;
describing which proxy to use when making request
"""
- return HTTPClient(escape.utf8(url), *args, **kwargs).fetch()
+ return HTTPClient(url, *args, **kwargs).fetch()
class JsonRPC:
@@ -250,7 +237,7 @@ def _success(response, deferred):
data = escape.json_decode(response.body)
error = data.get("error")
if error:
- if isinstance(error, DictType) and 'message' in error:
+ if isinstance(error, dict) and 'message' in error:
# JSON-RPC spec is not very verbose about error schema,
# but it should look like {'code': 0, 'message': 'msg'}
deferred.errback(Exception(error['message']))
diff --git a/cyclone/httpserver.py b/cyclone/httpserver.py
index 763b97e883..23a3482a84 100644
--- a/cyclone/httpserver.py
+++ b/cyclone/httpserver.py
@@ -31,12 +31,7 @@
from __future__ import absolute_import, division, with_statement
-try:
- import http.cookies as Cookie
-except ImportError:
- # python 2 compatibility
- import Cookie
-
+from http import cookies as http_cookies
import socket
import time
@@ -48,7 +43,7 @@
from twisted.internet import defer
from twisted.internet import interfaces
-from cyclone.escape import utf8, native_str, parse_qs_bytes
+from cyclone.escape import utf8, native_str, parse_qs_bytes, to_unicode
from cyclone import httputil
from cyclone.util import bytes_type
@@ -62,14 +57,13 @@ class HTTPConnection(basic.LineReceiver):
"""Handles a connection to an HTTP client, executing HTTP requests.
We parse HTTP headers and bodies, and execute the request callback
- until the HTTP conection is closed.
+ until the HTTP connection is closed.
If ``xheaders`` is ``True``, we support the ``X-Real-Ip`` and ``X-Scheme``
headers, which override the remote IP and HTTP scheme for all requests.
These headers are useful when running Tornado behind a reverse proxy or
load balancer.
"""
- delimiter = "\r\n"
def connectionMade(self):
self._headersbuffer = []
@@ -96,7 +90,7 @@ def lineReceived(self, line):
if line:
self._headersbuffer.append(line + self.delimiter)
else:
- buff = "".join(self._headersbuffer)
+ buff = b"".join(self._headersbuffer)
self._headersbuffer = []
self._on_headers(buff)
@@ -105,7 +99,7 @@ def rawDataReceived(self, data):
data, rest = data[:self.content_length], data[self.content_length:]
self.content_length -= len(data)
else:
- rest = ''
+ rest = b''
self._contentbuffer.write(data)
if self.content_length == 0:
@@ -150,29 +144,27 @@ def _finish_request(self):
def _on_headers(self, data):
try:
- data = native_str(data.decode("latin1"))
- eol = data.find("\r\n")
+ eol = data.find(b"\r\n")
start_line = data[:eol]
try:
- method, uri, version = start_line.split(" ")
+ method, uri, version = start_line.split(b" ")
except ValueError:
raise _BadRequestException("Malformed HTTP request line")
- if not version.startswith("HTTP/"):
- raise _BadRequestException(
- "Malformed HTTP version in HTTP Request-Line")
+ if not version.startswith(b"HTTP/"):
+ raise _BadRequestException("Malformed HTTP version in HTTP Request-Line")
try:
- headers = httputil.HTTPHeaders.parse(data[eol:])
+ headers = httputil.HTTPHeaders.parse(to_unicode(data[eol:]))
content_length = int(headers.get("Content-Length", 0))
except ValueError:
- raise _BadRequestException(
- "Malformed HTTP headers")
+ raise _BadRequestException("Malformed HTTP headers")
self._request = HTTPRequest(
- connection=self, method=method, uri=uri, version=version,
- headers=headers, remote_ip=self._remote_ip)
+ connection=self, method=to_unicode(method), uri=to_unicode(uri),
+ version=to_unicode(version),
+ headers=headers, remote_ip=to_unicode(self._remote_ip))
if content_length:
if headers.get("Expect") == "100-continue":
- self.transport.write("HTTP/1.1 100 (Continue)\r\n\r\n")
+ self.transport.write(b"HTTP/1.1 100 (Continue)\r\n\r\n")
if content_length < 100000:
self._contentbuffer = StringIO()
@@ -182,7 +174,6 @@ def _on_headers(self, data):
self.content_length = content_length
self.setRawMode()
return
-
self.request_callback(self._request)
except _BadRequestException as e:
log.msg("Malformed HTTP request from %s: %s", self._remote_ip, e)
@@ -194,7 +185,7 @@ def _on_request_body(self, data):
if self._request.method in ("POST", "PATCH", "PUT"):
if content_type.startswith("application/x-www-form-urlencoded"):
arguments = parse_qs_bytes(native_str(self._request.body))
- for name, values in arguments.iteritems():
+ for name, values in arguments.items():
values = [v for v in values if v]
if values:
self._request.arguments.setdefault(name,
@@ -303,7 +294,7 @@ def __init__(self, method, uri, version="HTTP/1.0", headers=None,
self.uri = uri
self.version = version
self.headers = headers or httputil.HTTPHeaders()
- self.body = body or ""
+ self.body = body or b""
if connection and connection.xheaders:
# Squid uses X-Forwarded-For, others use X-Real-Ip
self.remote_ip = self.headers.get(
@@ -340,11 +331,10 @@ def supports_http_1_1(self):
def cookies(self):
"""A dictionary of Cookie.Morsel objects."""
if not hasattr(self, "_cookies"):
- self._cookies = Cookie.SimpleCookie()
+ self._cookies = http_cookies.SimpleCookie()
if "Cookie" in self.headers:
try:
- self._cookies.load(
- native_str(self.headers["Cookie"]))
+ self._cookies.load(native_str(self.headers["Cookie"]))
except Exception:
self._cookies = {}
return self._cookies
@@ -393,4 +383,3 @@ def _valid_ip(self, ip):
if e.args[0] == socket.EAI_NONAME:
return False
raise
- return True
diff --git a/cyclone/httputil.py b/cyclone/httputil.py
index 3b0cac7680..7714d8ae2a 100644
--- a/cyclone/httputil.py
+++ b/cyclone/httputil.py
@@ -27,12 +27,7 @@
from cyclone.escape import utf8
from twisted.python import log
-
-try:
- from urllib.parse import urlencode
-except ImportError:
- # python 2 compatibility
- from urllib import urlencode
+from urllib import parse as urllib_parse
class HTTPHeaders(dict):
@@ -113,14 +108,15 @@ def parse_line(self, line):
>>> h.get('content-type')
'text/html'
"""
- if line[0].isspace():
+ temp = native_str(line)
+ if temp[0].isspace():
# continuation of a multi-line header
- new_part = ' ' + line.lstrip()
+ new_part = ' ' + temp.lstrip()
self._as_list[self._last_key][-1] += new_part
dict.__setitem__(self, self._last_key,
self[self._last_key] + new_part)
else:
- name, value = line.split(":", 1)
+ name, value = temp.split(":", 1)
self.add(name, value.strip())
@classmethod
@@ -203,7 +199,7 @@ def url_concat(url, args):
return url
if url[-1] not in ('?', '&'):
url += '&' if ('?' in url) else '?'
- return url + urlencode(args)
+ return url + urllib_parse.urlencode(args)
class HTTPFile(ObjectDict):
@@ -255,24 +251,24 @@ def parse_multipart_form_data(boundary, data, arguments, files):
# xmpp). I think we're also supposed to handle backslash-escapes
# here but I'll save that until we see a client that uses them
# in the wild.
- if boundary.startswith('"') and boundary.endswith('"'):
+ if boundary.startswith(b'"') and boundary.endswith(b'"'):
boundary = boundary[1:-1]
- final_boundary_index = data.rfind("--" + boundary + "--")
+ final_boundary_index = data.rfind(b"--" + boundary + b"--")
if final_boundary_index == -1:
log.msg("Invalid multipart/form-data: no final boundary")
return
- parts = data[:final_boundary_index].split("--" + boundary + "\r\n")
+ parts = data[:final_boundary_index].split(b"--" + boundary + b"\r\n")
for part in parts:
if not part:
continue
- eoh = part.find("\r\n\r\n")
+ eoh = part.find(b"\r\n\r\n")
if eoh == -1:
log.msg("multipart/form-data missing headers")
continue
- headers = HTTPHeaders.parse(part[:eoh].decode("utf-8"))
+ headers = HTTPHeaders.parse(part[:eoh])
disp_header = headers.get("Content-Disposition", "")
disposition, disp_params = _parse_header(disp_header)
- if disposition != "form-data" or not part.endswith("\r\n"):
+ if disposition != "form-data" or not part.endswith(b"\r\n"):
log.msg("Invalid multipart/form-data")
continue
value = part[eoh + 4:-2]
diff --git a/cyclone/jsonrpc.py b/cyclone/jsonrpc.py
index 32412fc275..673468b09c 100644
--- a/cyclone/jsonrpc.py
+++ b/cyclone/jsonrpc.py
@@ -24,8 +24,6 @@
`_.
"""
-import types
-
import cyclone.escape
from cyclone.web import HTTPError, RequestHandler
@@ -57,11 +55,9 @@ def post(self, *args):
req = cyclone.escape.json_decode(self.request.body)
jsonid = req["id"]
method = req["method"]
- assert isinstance(method, types.StringTypes), \
- "Invalid method type: %s" % type(method)
+ assert isinstance(method, str), "Invalid method type: %s" % type(method)
params = req.get("params", [])
- assert isinstance(params, (types.ListType, types.TupleType)), \
- "Invalid params type: %s" % type(params)
+ assert isinstance(params, (list, tuple)), "Invalid params type: %s" % type(params)
except Exception as e:
log.msg("Bad Request: %s" % str(e))
raise HTTPError(400)
diff --git a/cyclone/mail.py b/cyclone/mail.py
index 2ac1fafb00..e362d886a4 100644
--- a/cyclone/mail.py
+++ b/cyclone/mail.py
@@ -25,14 +25,15 @@
import types
import os.path
-from cStringIO import StringIO
+from io import StringIO
from OpenSSL.SSL import OP_NO_SSLv3
-from email import Encoders
-from email.MIMEText import MIMEText
-from email.MIMEBase import MIMEBase
-from email.MIMEMultipart import MIMEMultipart
-from email.Utils import COMMASPACE, formatdate
+from email import encoders as email_encoders
+from email.mime import text as email_mime_text
+from email.mime import base as email_mime_base
+from email.mime import multipart as email_mime_multipart
+from email import utils as email_utils
+
from twisted.internet import reactor
from twisted.internet.defer import Deferred
@@ -69,14 +70,14 @@ def __init__(self, from_addr, to_addrs, subject, message,
self.subject = subject
self.from_addr = from_addr
- if isinstance(to_addrs, types.StringType):
+ if isinstance(to_addrs, str):
self.to_addrs = [to_addrs]
else:
self.to_addrs = to_addrs
self.msg = None
self.__cache = None
- self.message = MIMEText(message, _charset=charset)
+ self.message = email_mime_text.MIMEText(message, _charset=charset)
self.message.set_type(mime)
def attach(self, filename, mime=None, charset=None, content=None):
@@ -95,13 +96,13 @@ def attach(self, filename, mime=None, charset=None, content=None):
fd = open(filename)
content = fd.read()
fd.close()
- elif not isinstance(content, types.StringType):
+ elif not isinstance(content, str):
raise TypeError("Don't know how to attach content: %s" %
repr(content))
- part = MIMEBase("application", "octet-stream")
+ part = email_mime_base.MIMEBase("application", "octet-stream")
part.set_payload(content)
- Encoders.encode_base64(part)
+ email_encoders.encode_base64(part)
part.add_header("Content-Disposition",
"attachment", filename=base)
@@ -112,7 +113,7 @@ def attach(self, filename, mime=None, charset=None, content=None):
part.set_charset(charset)
if self.msg is None:
- self.msg = MIMEMultipart()
+ self.msg = email_mime_multipart.MIMEMultipart()
self.msg.attach(self.message)
self.msg.attach(part)
@@ -126,8 +127,8 @@ def render(self):
self.msg["Subject"] = self.subject
self.msg["From"] = self.from_addr
- self.msg["To"] = COMMASPACE.join(self.to_addrs)
- self.msg["Date"] = formatdate(localtime=True)
+ self.msg["To"] = email_utils.COMMASPACE.join(self.to_addrs)
+ self.msg["Date"] = email_utils.formatdate(localtime=True)
if self.__cache is None:
self.__cache = self.msg.as_string()
@@ -163,7 +164,7 @@ def sendmail(mailconf, message):
d = mail.sendmail(mailconf, msg)
d.addCallback(on_response)
"""
- if not isinstance(mailconf, types.DictType):
+ if not isinstance(mailconf, dict):
raise TypeError("mailconf must be a regular python dictionary")
if not isinstance(message, Message):
@@ -171,10 +172,10 @@ def sendmail(mailconf, message):
host = mailconf.get("host")
- if isinstance(host, unicode):
- host = str(unicode)
+ #if isinstance(host, unicode):
+ # host = str(unicode)
- if not isinstance(host, types.StringType):
+ if not isinstance(host, str):
raise ValueError("mailconf requires a 'host' configuration")
use_tls = mailconf.get("tls")
@@ -186,7 +187,7 @@ def sendmail(mailconf, message):
port = mailconf.get("port", 25)
contextFactory = None
- if not isinstance(port, types.IntType):
+ if not isinstance(port, int):
raise ValueError("mailconf requires a proper 'port' configuration")
result = Deferred()
diff --git a/cyclone/options.py b/cyclone/options.py
index 9fe8319c2c..0ab4baf1ba 100644
--- a/cyclone/options.py
+++ b/cyclone/options.py
@@ -124,7 +124,7 @@ def parse_command_line(self, args=None):
if args is None:
args = sys.argv
remaining = []
- for i in xrange(1, len(args)):
+ for i in range(1, len(args)):
# All things after the last option are command line arguments
if not args[i].startswith("-"):
remaining = args[i:]
@@ -159,7 +159,8 @@ def parse_command_line(self, args=None):
def parse_config_file(self, path):
config = {}
- execfile(path, config, config)
+ with open(path, 'rb') as f:
+ exec(compile(f.read(), path, 'exec'), config, config)
for name in config:
if name in self:
self[name].set(config[name])
@@ -193,7 +194,7 @@ def print_help(self, file=sys.stdout):
class _Option(object):
- def __init__(self, name, default=None, type=basestring, help=None,
+ def __init__(self, name, default=None, type=str, help=None,
metavar=None, multiple=False, file_name=None,
group_name=None):
if default is None and multiple:
@@ -216,12 +217,12 @@ def parse(self, value):
datetime.datetime: self._parse_datetime,
datetime.timedelta: self._parse_timedelta,
bool: self._parse_bool,
- basestring: self._parse_string,
+ str: self._parse_string,
}.get(self.type, self.type)
if self.multiple:
self._value = []
for part in value.split(","):
- if self.type in (int, long):
+ if isnumeric(self):
# allow ranges of the form X:Y (inclusive at both ends)
lo, _, hi = part.partition(":")
lo = _parse(lo)
@@ -413,25 +414,25 @@ def __init__(self, color, *args, **kwargs):
fg_color = (curses.tigetstr("setaf") or
curses.tigetstr("setf") or "")
if (3, 0) < sys.version_info < (3, 2, 3):
- fg_color = unicode(fg_color, "ascii")
+ fg_color = str(fg_color, "ascii")
self._colors = {
- logging.DEBUG: unicode(curses.tparm(fg_color, 4), # Blue
+ logging.DEBUG: str(curses.tparm(fg_color, 4), # Blue
"ascii"),
- logging.INFO: unicode(curses.tparm(fg_color, 2), # Green
+ logging.INFO: str(curses.tparm(fg_color, 2), # Green
"ascii"),
- logging.WARNING: unicode(curses.tparm(fg_color, 3), # Yellow
+ logging.WARNING: str(curses.tparm(fg_color, 3), # Yellow
"ascii"),
- logging.ERROR: unicode(curses.tparm(fg_color, 1), # Red
+ logging.ERROR: str(curses.tparm(fg_color, 1), # Red
"ascii"),
}
- self._normal = unicode(curses.tigetstr("sgr0"), "ascii")
+ self._normal = str(curses.tigetstr("sgr0"), "ascii")
def format(self, record):
try:
record.message = record.getMessage()
except Exception as e:
record.message = "Bad message (%r): %r" % (e, record.__dict__)
- assert isinstance(record.message, basestring) # guaranteed by logging
+ assert isinstance(record.message, str) # guaranteed by logging
record.asctime = time.strftime(
"%y%m%d %H:%M:%S", self.converter(record.created))
prefix = '[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]' % \
diff --git a/cyclone/redis.py b/cyclone/redis.py
index c1d2e213e1..3e5fc1ac4b 100644
--- a/cyclone/redis.py
+++ b/cyclone/redis.py
@@ -83,7 +83,7 @@ def list_or_args(command, keys, args):
oldapi = bool(args)
try:
iter(keys)
- if isinstance(keys, (str, unicode)):
+ if isinstance(keys, (str,)):
keys = [keys]
if not oldapi:
return keys
@@ -287,7 +287,7 @@ def lineReceived(self, line):
if token == "$": # bulk data
try:
- self.bulk_length = long(data)
+ self.bulk_length = int(data)
except ValueError:
self.replyReceived(InvalidResponse("Cannot convert data "
"'%s' to integer" % data))
@@ -301,7 +301,7 @@ def lineReceived(self, line):
elif token == "*": # multi-bulk data
try:
- n = long(data)
+ n = int(data)
except (TypeError, ValueError):
self.multi_bulk = MultiBulkStorage()
self.replyReceived(InvalidResponse("Cannot convert "
@@ -457,7 +457,7 @@ def execute_command(self, *args, **kwargs):
for s in args:
if isinstance(s, str):
cmd = s
- elif isinstance(s, unicode):
+ elif isinstance(s, str):
if self.charset is None:
raise InvalidData("Encoding charset was not specified")
try:
@@ -745,7 +745,7 @@ def bitop(self, operation, destkey, *srckeys):
srclen = len(srckeys)
if srclen == 0:
return defer.fail(RedisError("no ``srckeys`` specified"))
- if isinstance(operation, (str, unicode)):
+ if isinstance(operation, str):
operation = operation.upper()
elif operation is operator.and_ or operation is operator.__and__:
operation = 'AND'
@@ -895,7 +895,7 @@ def blpop(self, keys, timeout=0):
"""
Blocking LPOP
"""
- if isinstance(keys, (str, unicode)):
+ if isinstance(keys, str):
keys = [keys]
else:
keys = list(keys)
@@ -907,7 +907,7 @@ def brpop(self, keys, timeout=0):
"""
Blocking RPOP
"""
- if isinstance(keys, (str, unicode)):
+ if isinstance(keys, str):
keys = [keys]
else:
keys = list(keys)
@@ -1235,7 +1235,7 @@ def _zaggregate(self, command, dstkey, keys, aggregate):
aggregate = 'SUM'
else:
err_flag = True
- if isinstance(aggregate, (str, unicode)):
+ if isinstance(aggregate, str):
aggregate_u = aggregate.upper()
if aggregate_u in ('MIN', 'MAX', 'SUM'):
aggregate = aggregate_u
@@ -1307,7 +1307,7 @@ def hdel(self, key, fields):
"""
Remove the specified field or fields from a hash
"""
- if isinstance(fields, (str, unicode)):
+ if isinstance(fields, str):
fields = [fields]
else:
fields = list(fields)
@@ -1382,7 +1382,7 @@ def watch(self, keys):
self.inMulti = False
self.unwatch_cc = self._clear_txstate
self.commit_cc = lambda: ()
- if isinstance(keys, (str, unicode)):
+ if isinstance(keys, str):
keys = [keys]
d = self.execute_command("WATCH", *keys).addCallback(self._tx_started)
return d
@@ -1643,7 +1643,7 @@ def dataReceived(self, data, unpause=False):
self._reader.feed(data)
res = self._reader.gets()
while res is not False:
- if isinstance(res, basestring):
+ if isinstance(res, str):
res = self.tryConvertData(res)
elif isinstance(res, list):
res = map(self.tryConvertData, res)
@@ -1698,22 +1698,22 @@ def replyReceived(self, reply):
self.replyQueue.put(reply)
def subscribe(self, channels):
- if isinstance(channels, (str, unicode)):
+ if isinstance(channels, str):
channels = [channels]
return self.execute_command("SUBSCRIBE", *channels)
def unsubscribe(self, channels):
- if isinstance(channels, (str, unicode)):
+ if isinstance(channels, str):
channels = [channels]
return self.execute_command("UNSUBSCRIBE", *channels)
def psubscribe(self, patterns):
- if isinstance(patterns, (str, unicode)):
+ if isinstance(patterns, str):
patterns = [patterns]
return self.execute_command("PSUBSCRIBE", *patterns)
def punsubscribe(self, patterns):
- if isinstance(patterns, (str, unicode)):
+ if isinstance(patterns, str):
patterns = [patterns]
return self.execute_command("PUNSUBSCRIBE", *patterns)
@@ -1855,7 +1855,7 @@ def __init__(self, nodes=[], replicas=160):
def add_node(self, node):
self.nodes.append(node)
- for x in xrange(self.replicas):
+ for x in range(self.replicas):
crckey = zlib.crc32("%s:%d" % (node._factory.uuid, x))
self.ring[crckey] = node
self.sorted_keys.append(crckey)
@@ -1864,7 +1864,7 @@ def add_node(self, node):
def remove_node(self, node):
self.nodes.remove(node)
- for x in xrange(self.replicas):
+ for x in range(self.replicas):
crckey = zlib.crc32("%s:%d" % (node, x))
self.ring.remove(crckey)
self.sorted_keys.remove(crckey)
@@ -1919,7 +1919,7 @@ def disconnect(self):
def _wrap(self, method, *args, **kwargs):
try:
key = args[0]
- assert isinstance(key, (str, unicode))
+ assert isinstance(key, str)
except:
raise ValueError(
"Method '%s' requires a key as the first argument" % method)
@@ -1980,9 +1980,6 @@ def __repr__(self):
class ShardedUnixConnectionHandler(ShardedConnectionHandler):
- def pipeline(self):
- pass
-
def __repr__(self):
nodes = []
for conn in self._ring.nodes:
@@ -2007,7 +2004,7 @@ def __init__(self, uuid, dbid, poolsize, isLazy=False,
raise ValueError("Redis poolsize must be an integer, not %s" %
repr(poolsize))
- if not isinstance(dbid, (int, types.NoneType)):
+ if not isinstance(dbid, (int, None)):
raise ValueError("Redis dbid must be an integer, not %s" %
repr(dbid))
@@ -2116,7 +2113,7 @@ def makeConnection(host, port, dbid, poolsize, reconnect, isLazy,
factory = RedisFactory(uuid, dbid, poolsize, isLazy, ConnectionHandler,
charset, password, replyTimeout, convertNumbers)
factory.continueTrying = reconnect
- for x in xrange(poolsize):
+ for x in range(poolsize):
reactor.connectTCP(host, port, factory, connectTimeout)
if isLazy:
@@ -2228,7 +2225,7 @@ def makeUnixConnection(path, dbid, poolsize, reconnect, isLazy,
factory = RedisFactory(path, dbid, poolsize, isLazy, UnixConnectionHandler,
charset, password, replyTimeout, convertNumbers)
factory.continueTrying = reconnect
- for x in xrange(poolsize):
+ for x in range(poolsize):
reactor.connectUNIX(path, factory, connectTimeout)
if isLazy:
diff --git a/cyclone/sse.py b/cyclone/sse.py
index 5f8b2e26b1..84fc3d0825 100644
--- a/cyclone/sse.py
+++ b/cyclone/sse.py
@@ -56,9 +56,9 @@ def sendEvent(self, message, event=None, eid=None, retry=None):
"""
if isinstance(message, dict):
message = escape.json_encode(message)
- if isinstance(message, unicode):
+ if isinstance(message, str):
message = message.encode("utf-8")
- assert isinstance(message, str)
+ assert isinstance(message, bytes)
if eid:
self.transport.write("id: %s\n" % eid)
diff --git a/cyclone/template.py b/cyclone/template.py
index 9de7d8a6b4..05d27a9ac2 100644
--- a/cyclone/template.py
+++ b/cyclone/template.py
@@ -196,22 +196,15 @@ def add(x, y):
import sys
import threading
import traceback
-
-if (sys.version_info >= (3, 0)):
- from io import StringIO
-else:
- from cStringIO import StringIO
+from io import StringIO
from cyclone import escape
from cyclone.util import ObjectDict
from cyclone.util import bytes_type
from cyclone.util import unicode_type
-from cyclone.util import list_type
-from cyclone.util import exec_in
from twisted.python.failure import Failure
from twisted.internet.defer import Deferred
-
_DEFAULT_AUTOESCAPE = "xhtml_escape"
_UNSET = object()
@@ -240,8 +233,7 @@ def __init__(self, template_string, name="", loader=None,
self.file = _File(self, _parse(reader, self))
self.code = self._generate_python(loader, compress_whitespace)
except ParseError as e:
- raise TemplateError("Error parsing template %s, line %d: %s" %
- (name, reader.line, str(e)))
+ raise TemplateError("Error parsing template %s, line %d: %s" %(name, reader.line, str(e)))
self.loader = loader
try:
@@ -253,7 +245,7 @@ def __init__(self, template_string, name="", loader=None,
"exec")
except Exception:
raise TemplateError("Error compiling template " + name + ":\n" +
- _format_code(self.code).rstrip())
+ _format_code(self.code).rstrip())
def generate(self, **kwargs):
"""Generate this template with the given arguments."""
@@ -274,7 +266,7 @@ def generate(self, **kwargs):
}
namespace.update(self.namespace)
namespace.update(kwargs)
- exec_in(self.compiled, namespace)
+ exec(self.compiled, namespace, namespace)
execute = namespace["_execute"]
# Clear the traceback module's cache of source data now that
# we've generated a new template (mainly for this module's
@@ -290,9 +282,8 @@ def generate(self, **kwargs):
if isinstance(rv, Failure):
rv.raiseException()
return rv
- except:
- raise TemplateError("Error executing template " + self.name +
- ":\n" + _format_code(traceback.format_exception(*sys.exc_info())))
+ except Exception:
+ raise TemplateError("Error executing template " + self.name + ":\n" + _format_code(traceback.format_exception(*sys.exc_info())))
def _generate_python(self, loader, compress_whitespace):
buffer = StringIO()
@@ -324,6 +315,7 @@ def _get_ancestors(self, loader):
ancestors.extend(template._get_ancestors(loader))
return ancestors
+
class BaseLoader(object):
"""Base class for template loaders."""
def __init__(self, autoescape=_DEFAULT_AUTOESCAPE, namespace=None):
@@ -430,6 +422,7 @@ def maybe_deferred(self, varName, writer):
with writer.indent():
writer.write_line("%s = yield %s" % (varName, varName), self.line)
+
class _File(_Node):
def __init__(self, template, body):
self.template = template
@@ -494,6 +487,7 @@ def find_named_blocks(self, loader, named_blocks):
named_blocks[self.name].append(self)
_Node.find_named_blocks(self, loader, named_blocks)
+
class _ExtendsBlock(_Node):
def __init__(self, name):
self.name = name
@@ -520,6 +514,7 @@ def generate(self, writer):
preParent = blocks[idx-1]
preParent.generate(writer, force_self=True)
+
class _IncludeBlock(_Node):
def __init__(self, name, reader, line):
self.name = name
@@ -554,8 +549,7 @@ def generate(self, writer):
writer.write_line("_append = _buffer.append", self.line)
self.body.generate(writer)
writer.write_line("return _utf8('').join(_buffer)", self.line)
- writer.write_line("_append(_utf8(%s(%s())))" % (
- self.method, method_name), self.line)
+ writer.write_line("_append(_utf8(%s(%s())))" % (self.method, method_name), self.line)
class _ControlBlock(_Node):
@@ -583,8 +577,8 @@ def __init__(self, statement, line):
def generate(self, writer):
# In case the previous block was empty
writer.write_line("pass", self.line)
- writer.write_line("%s:" % self.statement, self.line,
- writer.indent_size() - 1)
+ writer.write_line("%s:" % self.statement, self.line, writer.indent_size() - 1)
+
class _Statement(_Node):
def __init__(self, statement, line):
@@ -750,7 +744,7 @@ def __str__(self):
def _format_code(code):
- lines = code if isinstance(code, list_type) else code.splitlines()
+ lines = code if isinstance(code, list) else code.splitlines()
format = "%%%dd %%s\n" % len(repr(len(lines) + 1))
return "".join([format % (i + 1, line) for (i, line) in enumerate(lines)])
diff --git a/cyclone/testing/client.py b/cyclone/testing/client.py
index d02bfc8149..35718fce39 100644
--- a/cyclone/testing/client.py
+++ b/cyclone/testing/client.py
@@ -19,12 +19,7 @@
import urllib
from twisted.test import proto_helpers
from twisted.internet.defer import inlineCallbacks, returnValue
-
-try:
- from http.cookies import SimpleCookie
-except ImportError:
- # python 2 compatibility
- from Cookie import SimpleCookie
+from http.cookies import SimpleCookie
class DecodingSimpleCookie(SimpleCookie):
@@ -94,10 +89,10 @@ def head(self, uri, params=None, version="HTTP/1.0", headers=None,
def request(self, method, uri, *args, **kwargs):
params = kwargs.pop("params", {}) or {}
if method in ["GET", "HEAD", "OPTIONS"] and params:
- uri = uri + "?" + urllib.urlencode(params)
+ uri = uri + "?" + urllib.parse.urlencode(params)
elif method in ["POST", "PATCH", "PUT"]\
and params and not kwargs['body']:
- kwargs['body'] = urllib.urlencode(params)
+ kwargs['body'] = urllib.parse.urlencode(params)
connection = kwargs.pop('connection')
if not connection:
connection = HTTPConnection()
@@ -120,14 +115,14 @@ def request(self, method, uri, *args, **kwargs):
def setup_response():
headers = HTTPHeaders()
- for line in handler._generate_headers().split("\r\n"):
- if line.startswith("HTTP") or not line.strip():
+ for line in handler._generate_headers().split(b"\r\n"):
+ if line.startswith(b"HTTP") or not line.strip():
continue
headers.parse_line(line)
for cookie in headers.get_list("Set-Cookie"):
self.cookies.load(cookie)
response_body = connection.transport.io.getvalue()
- handler.content = response_body.split("\r\n\r\n", 1)[1]
+ handler.content = response_body.split(b"\r\n\r\n", 1)[1]
handler.headers = headers
if handler._finished:
diff --git a/cyclone/tests/test_app.py b/cyclone/tests/test_app.py
index 98fb238e2f..fa5c841623 100644
--- a/cyclone/tests/test_app.py
+++ b/cyclone/tests/test_app.py
@@ -18,4 +18,4 @@
class AppTests(unittest.TestCase):
def test_something(self):
- pass
+ pass
\ No newline at end of file
diff --git a/cyclone/tests/test_auth.py b/cyclone/tests/test_auth.py
index e8b33845cb..e0f98c16a2 100644
--- a/cyclone/tests/test_auth.py
+++ b/cyclone/tests/test_auth.py
@@ -15,19 +15,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+from urllib import parse as urllib_parse
import cyclone.web
-from cyclone.auth import FacebookGraphMixin
from twisted.trial import unittest
-
-try:
- # py3
- import urllib.parse as urllib_parse
- from unittest.mock import MagicMock, patch
-except ImportError:
- # py2
- from mock import MagicMock, patch
- import urllib as urllib_parse
+from twisted.internet import defer
+from cyclone.auth import FacebookGraphMixin
+from unittest.mock import patch, MagicMock
class TestHandler(cyclone.web.RequestHandler,
diff --git a/cyclone/tests/test_db.py b/cyclone/tests/test_db.py
index b4de516a9f..4cc0a94c59 100644
--- a/cyclone/tests/test_db.py
+++ b/cyclone/tests/test_db.py
@@ -13,21 +13,17 @@
# License for the specific language governing permissions and limitations
# under the License.
-from cyclone.sqlite import InlineSQLite
from twisted.trial import unittest
-
-try:
- from mock import Mock
-except ImportError:
- from unittest.mock import Mock
+from cyclone.sqlite import InlineSQLite
+from unittest.mock import Mock
class InlineSQLiteTest(unittest.TestCase):
def setUp(self):
self.isq = InlineSQLite()
self.isq.runOperation(
- "create table foobar_test (val1 string, val2 string)")
- self.isq.runOperation('insert into foobar_test values ("a", "b")')
+ "create table `nothing` (val1 string, val2 string)")
+ self.isq.runOperation('insert into `nothing` values ("a", "b")')
def test_init(self):
self.assertTrue(hasattr(self.isq, "autoCommit"))
@@ -41,16 +37,16 @@ def test_runQuery(self):
self.assertEqual(res, [1, 2, 3])
def test_runOperation(self):
- self.isq.runOperation('insert into foobar_test values ("c", "d")')
- res = self.isq.runQuery("select count(*) from foobar_test")
+ self.isq.runOperation('insert into `nothing` values ("c", "d")')
+ res = self.isq.runQuery("select count(*) from `nothing`")
self.assertEqual(res[0][0], 2)
def test_runOperationMany(self):
self.isq.runOperationMany(
- 'insert into foobar_test values (?, ?)',
+ 'insert into `nothing` values (?, ?)',
[["a", "b"], ["c", "d"]]
)
- res = self.isq.runQuery("select count(*) from foobar_test")
+ res = self.isq.runQuery("select count(*) from `nothing`")
self.assertEqual(res[0][0], 3)
def test_commit(self):
diff --git a/cyclone/tests/test_escape.py b/cyclone/tests/test_escape.py
index 2d05af09a6..1cf5f6e311 100644
--- a/cyclone/tests/test_escape.py
+++ b/cyclone/tests/test_escape.py
@@ -13,21 +13,24 @@
# License for the specific language governing permissions and limitations
# under the License.
-from cyclone import escape
from twisted.trial import unittest
+from unittest.mock import Mock
+
+from cyclone import escape
class TestEscape(unittest.TestCase):
+
def test_xhtml(self):
self.assertEqual(
- escape.xhtml_escape("abc42"),
- "abc42"
+ escape.xhtml_escape("abc42"),
+ "abc42"
)
self.assertEqual(
- escape.xhtml_escape("<>"),
- "<>"
+ escape.xhtml_escape("<>"),
+ "<>"
)
self.assertEqual(
- escape.xhtml_escape("\"'"),
- ""'"
- )
+ escape.xhtml_escape("\"'"),
+ ""'"
+ )
\ No newline at end of file
diff --git a/cyclone/tests/test_httpclient.py b/cyclone/tests/test_httpclient.py
index 13358c9bed..d582566d0c 100644
--- a/cyclone/tests/test_httpclient.py
+++ b/cyclone/tests/test_httpclient.py
@@ -13,20 +13,16 @@
# License for the specific language governing permissions and limitations
# under the License.
+from twisted.trial import unittest
+from cyclone.httpclient import StringProducer, Receiver, HTTPClient, fetch
import cyclone.httpclient
+from io import StringIO
+from twisted.internet.defer import inlineCallbacks, Deferred, succeed, fail
+from unittest.mock import Mock
+#from unittest import mock
import functools
-
-from cStringIO import StringIO
from cyclone import escape
-from cyclone.httpclient import StringProducer, Receiver, HTTPClient, fetch
from cyclone.web import HTTPError
-from twisted.internet.defer import inlineCallbacks, Deferred, succeed, fail
-from twisted.trial import unittest
-
-try:
- from mock import Mock
-except ImportError:
- from unittest.mock import Mock
class TestStringProducer(unittest.TestCase):
@@ -99,6 +95,7 @@ def test_fetch_basic(self):
client = HTTPClient("http://example.com")
client.agent = Mock()
_response = Mock()
+ _response.code = 200
_response.headers.getAllRawHeaders.return_value = {}
_response.deliverBody = lambda x: x.dataReceived("done") \
or x.connectionLost(None)
@@ -113,6 +110,7 @@ def test_fetch_head(self):
_response = Mock()
_response.headers.getAllRawHeaders.return_value = {}
_response.deliverBody = lambda x: x.connectionLost(None)
+ _response.code = 200
client.agent.request.return_value = succeed(_response)
response = yield client.fetch()
self.assertEqual(response.body, "")
diff --git a/cyclone/tests/test_httpserver.py b/cyclone/tests/test_httpserver.py
index 0eb135a8e3..3e614f4f4d 100644
--- a/cyclone/tests/test_httpserver.py
+++ b/cyclone/tests/test_httpserver.py
@@ -12,23 +12,16 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-
-from cyclone.httpserver import HTTPConnection, HTTPRequest
-from io import BytesIO
from twisted.internet import address
-from twisted.internet import interfaces
+from twisted.trial import unittest
+from unittest.mock import Mock
+from unittest import mock
+from cyclone.httpserver import HTTPConnection, HTTPRequest
from twisted.internet.defer import Deferred
from twisted.test.proto_helpers import StringTransport
-from twisted.trial import unittest
-
-try:
- # py3
- import http.cookies as Cookie
- from unittest.mock import Mock
-except ImportError:
- # py2
- import Cookie
- from mock import Mock
+from twisted.internet import interfaces
+from io import BytesIO
+from http import cookies as http_cookies
class HTTPConnectionTest(unittest.TestCase):
@@ -58,27 +51,27 @@ def test_notifyFinish(self):
def test_lineReceived(self):
self.con.connectionMade()
- line = "Header: something"
+ line = b"Header: something"
self.con.lineReceived(line)
self.assertTrue(line + self.con.delimiter in self.con._headersbuffer)
self.con._on_headers = Mock()
- self.con.lineReceived("")
- self.con._on_headers.assert_called_with('Header: something\r\n')
+ self.con.lineReceived(b"")
+ self.con._on_headers.assert_called_with(b'Header: something\r\n')
def test_rawDataReceived(self):
self.con.connectionMade()
self.con._contentbuffer = BytesIO()
self.con._on_request_body = Mock()
self.con.content_length = 5
- data = "some data"
+ data = b"some data"
self.con.rawDataReceived(data)
- self.con._on_request_body.assert_called_with("some ")
+ self.con._on_request_body.assert_called_with(b"some ")
def test_write(self):
self.con.transport = StringTransport()
self.con._request = Mock()
- self.con.write("data")
- self.assertEqual(self.con.transport.io.getvalue(), "data")
+ self.con.write(b"data")
+ self.assertEqual(self.con.transport.io.getvalue(), b"data")
def test_finish(self):
self.con._request = Mock()
@@ -146,79 +139,85 @@ def test_finish_request_http1_discon(self):
self.con.transport.loseConnection.assert_called_with()
def test_on_headers_simple(self):
- self.con._remote_ip = Mock()
- self.con.request_callback = Mock()
- self.con.__dict__['_remote_ip'] = "127.0.0.1"
- self.con.connectionMade()
- data = \
- "GET / HTTP/1.1\r\n"
- self.con._on_headers(data)
- self.assertEqual(self.con.request_callback.call_count, 1)
+ with mock.patch.object(HTTPConnection, '_remote_ip', return_value=None) as m_obj:
+ self.con = HTTPConnection()
+ self.con.factory = Mock()
+ self.con.request_callback = Mock()
+ self.con._remote_ip = "127.0.0.1"
+ self.con.connectionMade()
+ data = b"GET / HTTP/1.1\r\n"
+ self.con._on_headers(data)
+ self.assertEqual(self.con.request_callback.call_count, 1)
def test_on_headers_invalid(self):
- self.con._remote_ip = Mock()
- self.con.request_callback = Mock()
- self.con.transport = Mock()
- self.con.__dict__['_remote_ip'] = "127.0.0.1"
- self.con.connectionMade()
- data = \
- "GET /"
- self.con._on_headers(data)
- self.con.transport.loseConnection.assert_called_with()
+ with mock.patch.object(HTTPConnection, '_remote_ip', return_value=None) as m_obj:
+ self.con = HTTPConnection()
+ self.con.factory = Mock()
+ self.con.request_callback = Mock()
+ self.con.transport = Mock()
+ self.con._remote_ip = "127.0.0.1"
+ self.con.connectionMade()
+ data = b"GET /"
+ self.con._on_headers(data)
+ self.con.transport.loseConnection.assert_called_with()
def test_on_headers_invalid_version(self):
- self.con._remote_ip = Mock()
- self.con.request_callback = Mock()
- self.con.transport = Mock()
- self.con.__dict__['_remote_ip'] = "127.0.0.1"
- self.con.connectionMade()
- data = \
- "GET / HTTS/1.1"
- self.con._on_headers(data)
- self.con.transport.loseConnection.assert_called_with()
+ with mock.patch.object(HTTPConnection, '_remote_ip', return_value=None) as m_obj:
+ self.con = HTTPConnection()
+ self.con.factory = Mock()
+ self.con.request_callback = Mock()
+ self.con.transport = Mock()
+ self.con._remote_ip = "127.0.0.1"
+ self.con.connectionMade()
+ data = b"GET / HTTS/1.1"
+ self.con._on_headers(data)
+ self.con.transport.loseConnection.assert_called_with()
def test_on_headers_content_length(self):
- self.con._remote_ip = Mock()
- self.con.setRawMode = Mock()
- self.con.__dict__['_remote_ip'] = "127.0.0.1"
- self.con.connectionMade()
- data = \
- "GET / HTTP/1.1\r\n"\
- "Content-Length: 5\r\n"\
- "\r\n"
- self.con._on_headers(data)
- self.con.setRawMode.assert_called_with()
- self.assertEqual(self.con.content_length, 5)
+ with mock.patch.object(HTTPConnection, '_remote_ip', return_value=None) as m_obj:
+ self.con = HTTPConnection()
+ self.con.factory = Mock()
+ self.con.setRawMode = Mock()
+ self.con._remote_ip = "127.0.0.1"
+ self.con.connectionMade()
+ data = \
+ b"GET / HTTP/1.1\r\n" \
+ b"Content-Length: 5\r\n" \
+ b"\r\n"
+ self.con._on_headers(data)
+ self.con.setRawMode.assert_called_with()
+ self.assertEqual(self.con.content_length, 5)
def test_on_headers_continue(self):
- self.con._remote_ip = Mock()
- self.con.transport = StringTransport()
- self.con.setRawMode = Mock()
- self.con.__dict__['_remote_ip'] = "127.0.0.1"
- self.con.connectionMade()
- data = \
- "GET / HTTP/1.1\r\n"\
- "Content-Length: 5\r\n"\
- "Expect: 100-continue"\
- "\r\n"
- self.con._on_headers(data)
- self.assertEqual(
- self.con.transport.io.getvalue().strip(),
- "HTTP/1.1 100 (Continue)"
- )
+ with mock.patch.object(HTTPConnection, '_remote_ip', return_value=None) as m_obj:
+ self.con = HTTPConnection()
+ self.con.factory = Mock()
+ self.con.transport = StringTransport()
+ self.con.setRawMode = Mock()
+ self.con._remote_ip = "127.0.0.1"
+ self.con.connectionMade()
+ data = \
+ b"GET / HTTP/1.1\r\n"\
+ b"Content-Length: 5\r\n"\
+ b"Expect: 100-continue"\
+ b"\r\n"
+ self.con._on_headers(data)
+ self.assertEqual(self.con.transport.io.getvalue().strip(), b"HTTP/1.1 100 (Continue)")
def test_on_headers_big_body(self):
- self.con._remote_ip = Mock()
- self.con.transport = StringTransport()
- self.con.setRawMode = Mock()
- self.con.__dict__['_remote_ip'] = "127.0.0.1"
- self.con.connectionMade()
- data = \
- "GET / HTTP/1.1\r\n"\
- "Content-Length: 10000000\r\n"\
- "\r\n"
- self.con._on_headers(data)
- self.assertTrue(self.con._contentbuffer)
+ with mock.patch.object(HTTPConnection, '_remote_ip', return_value=None) as m_obj:
+ self.con = HTTPConnection()
+ self.con.factory = Mock()
+ self.con.transport = StringTransport()
+ self.con.setRawMode = Mock()
+ self.con._remote_ip = "127.0.0.1"
+ self.con.connectionMade()
+ data = \
+ b"GET / HTTP/1.1\r\n"\
+ b"Content-Length: 10000000\r\n"\
+ b"\r\n"
+ self.con._on_headers(data)
+ self.assertTrue(self.con._contentbuffer)
def test_on_request_body_get(self):
self.con.request_callback = Mock()
@@ -226,7 +225,7 @@ def test_on_request_body_get(self):
self.con._request.method = "GET"
self.con._request.headers = {
}
- data = ""
+ data = b""
self.con._on_request_body(data)
self.assertEqual(self.con.request_callback.call_count, 1)
@@ -252,14 +251,14 @@ def test_on_request_body_post_multipart_form_data(self):
"Content-Type": "multipart/form-data; boundary=AaB03x"
}
data = \
- "--AaB03x\r\n"\
- 'Content-Disposition: form-data; name="a"\r\n'\
- "\r\n"\
- "b\r\n"\
- "--AaB03x--\r\n"
+ b"--AaB03x\r\n"\
+ b'Content-Disposition: form-data; name="a"\r\n'\
+ b"\r\n"\
+ b"b\r\n"\
+ b"--AaB03x--\r\n"
self.con._on_request_body(data)
self.assertEqual(self.con.request_callback.call_count, 1)
- self.assertEqual(self.con._request.arguments, {"a": ["b"]})
+ self.assertEqual(self.con._request.arguments, {"a": [b"b"]})
def test_remote_ip(self):
self.con.transport = StringTransport()
@@ -356,13 +355,13 @@ def test_cookies_invalid(self):
def throw_exc(ignore):
raise Exception()
- old_cookie = Cookie.SimpleCookie
- Cookie.SimpleCookie = Mock()
- Cookie.SimpleCookie.return_value.load = throw_exc
+ old_cookie = http_cookies.SimpleCookie
+ http_cookies.SimpleCookie = Mock()
+ http_cookies.SimpleCookie.return_value.load = throw_exc
self.req.cookies
cookies = self.req.cookies
self.assertEqual(cookies, {})
- Cookie.SimpleCookie = old_cookie
+ http_cookies.SimpleCookie = old_cookie
def test_full_url(self):
expected = "http://127.0.0.1/something"
diff --git a/cyclone/tests/test_mail.py b/cyclone/tests/test_mail.py
index 78bb95432a..bf2f4ddd9c 100644
--- a/cyclone/tests/test_mail.py
+++ b/cyclone/tests/test_mail.py
@@ -12,17 +12,10 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-
-import types
-
+from twisted.trial import unittest
from cyclone.mail import ContextFactory, ClientContextFactory, Message
from cyclone.mail import sendmail
-from twisted.trial import unittest
-
-try:
- from mock import Mock, patch
-except ImportError:
- from unittest.mock import Mock, patch
+from unittest.mock import Mock, patch
class ContextFactoryTest(unittest.TestCase):
@@ -54,7 +47,7 @@ def test_init_single_addr(self):
"hi thar",
"This is a message."
)
- self.assertTrue(isinstance(message.to_addrs, types.ListType))
+ self.assertTrue(isinstance(message.to_addrs, list))
def test_attach(self):
open("foo.txt", "w").write("sometext")
diff --git a/cyclone/tests/test_requirements.txt b/cyclone/tests/test_requirements.txt
index 803b1afaf2..932a8957f7 100644
--- a/cyclone/tests/test_requirements.txt
+++ b/cyclone/tests/test_requirements.txt
@@ -1,3 +1 @@
mock
-twisted>=12.0
-pyopenssl
diff --git a/cyclone/tests/test_template.py b/cyclone/tests/test_template.py
index 1d219c9c8d..8811b8adaf 100644
--- a/cyclone/tests/test_template.py
+++ b/cyclone/tests/test_template.py
@@ -13,17 +13,20 @@
# License for the specific language governing permissions and limitations
# under the License.
-from cyclone import template
from twisted.internet import defer
-from twisted.internet import reactor
from twisted.trial import unittest
+from twisted.internet import reactor
+
+from unittest.mock import Mock
+
+from cyclone import template
class TestTemplates(unittest.TestCase):
def test_simple_var(self):
t = template.Template(r"My name is: {{ name }}")
- self.assertEqual(t.generate(name="Alice"), "My name is: Alice")
- self.assertEqual(t.generate(name="Bob"), "My name is: Bob")
+ self.assertEqual(t.generate(name="Alice"), b"My name is: Alice")
+ self.assertEqual(t.generate(name="Bob"), b"My name is: Bob")
def test_blocks(self):
loader = template.DictLoader({
@@ -60,7 +63,7 @@ def test_blocks(self):
def test_if(self):
t = template.Template(
- r"{% if a == 1 %}One{% elif a < 0 %}Negative{% elif isinstance(a, basestring) %}String{% else %}Unknown{% end %}")
+ r"{% if isinstance(a, str)%}String{% elif a < 0 %}Negative{% elif a==1 %}One{% else %}Unknown{% end %}")
self.assertEqual(t.generate(a=1), b"One")
self.assertEqual(t.generate(a=-1), b"Negative")
self.assertEqual(t.generate(a=-1.67), b"Negative")
@@ -71,48 +74,48 @@ def test_if(self):
def test_comment(self):
self.assertEqual(
- template.Template(r"{% comment blah! %}42").generate(),
- b"42"
+ template.Template(r"{% comment blah! %}42").generate(),
+ b"42"
)
def test_set(self):
self.assertEqual(
- template.Template(r"{% set x=42 %}{{ val + x }}").generate(val=-42),
- "0"
+ template.Template(r"{% set x=42 %}{{ val + x }}").generate(val=-42),
+ b"0"
)
self.assertEqual(
- template.Template(r"{% set x=val2 %}{{ val + x }}").generate(val=1, val2=10),
- "11"
+ template.Template(r"{% set x=val2 %}{{ val + x }}").generate(val=1, val2=10),
+ b"11"
)
def test_loops(self):
self.assertEqual(
- template.Template(r"{% for x in [1,2,3,4] %}{{ x }}:{% end %}").generate(),
- "1:2:3:4:"
+ template.Template(r"{% for x in [1,2,3,4] %}{{ x }}:{% end %}").generate(),
+ b"1:2:3:4:"
)
self.assertEqual(
- template.Template(r"{% set x=0 %}{% while x < 10 %}{{x}};{% set x += 2 %}{% end %}").generate(),
- "0;2;4;6;8;"
+ template.Template(r"{% set x=0 %}{% while x < 10 %}{{x}};{% set x += 2 %}{% end %}").generate(),
+ b"0;2;4;6;8;"
)
def test_autoescape(self):
t = template.Template(r"<{{x}}>")
self.assertEqual(
- t.generate(x="<"),
- "<<>"
+ t.generate(x="<"),
+ b"<<>"
)
self.assertEqual(
- t.generate(x=">"),
- "<>>"
+ t.generate(x=">"),
+ b"<>>"
)
t2 = template.Template(r"{% autoescape None %}<{{x}}>")
self.assertEqual(
- t2.generate(x="<"),
- "<<>"
+ t2.generate(x="<"),
+ b"<<>"
)
self.assertEqual(
- t2.generate(x=">"),
- "<>>"
+ t2.generate(x=">"),
+ b"<>>"
)
@defer.inlineCallbacks
@@ -128,18 +131,18 @@ def _mkDeferred(rv, delay=None):
# Test that template immidiatly resolves deferreds if possible
t = template.Template(r"-) {{x}} <-> {{y(63)}} :!")
self.assertEqual(
- t.generate(x=_mkDeferred(42), y=_mkDeferred),
- "-) 42 <-> 63 :!"
+ t.generate(x=_mkDeferred(42), y=_mkDeferred),
+ b"-) 42 <-> 63 :!"
)
# Test delayed execution
d = t.generate(
- x=_mkDeferred("hello", 0.1),
- y=lambda val: _mkDeferred(val - 60, 0.5)
+ x=_mkDeferred("hello", 0.1),
+ y=lambda val: _mkDeferred(val - 60, 0.5)
)
self.assertTrue(isinstance(d, defer.Deferred), d)
txt = yield d
self.assertEqual(
- txt,
- "-) hello <-> 3 :!"
+ txt,
+ b"-) hello <-> 3 :!"
)
diff --git a/cyclone/tests/test_testing.py b/cyclone/tests/test_testing.py
index d608c7ea20..e05aaf7dd6 100644
--- a/cyclone/tests/test_testing.py
+++ b/cyclone/tests/test_testing.py
@@ -13,11 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
+from twisted.trial import unittest
from cyclone.testing import CycloneTestCase, Client
from cyclone.web import Application, RequestHandler, asynchronous
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
-from twisted.trial import unittest
class TestHandler(RequestHandler):
@@ -87,43 +87,43 @@ def test_create_client(self):
@inlineCallbacks
def test_get_request(self):
response = yield self.client.get("/testing/")
- self.assertEqual(response.content, "Something")
+ self.assertEqual(response.content, b"Something")
self.assertTrue(len(response.headers) > 3)
@inlineCallbacks
def test_get_request_with_params(self):
response = yield self.client.get("/testing/", {"q": "query"})
- self.assertEqual(response.content, "Something")
+ self.assertEqual(response.content, b"Something")
self.assertTrue(len(response.headers) > 3)
@inlineCallbacks
def test_post_request(self):
response = yield self.client.post("/testing/")
- self.assertEqual(response.content, "Something posted")
+ self.assertEqual(response.content, b"Something posted")
self.assertTrue(len(response.headers) > 3)
@inlineCallbacks
def test_put_request(self):
response = yield self.client.put("/testing/")
- self.assertEqual(response.content, "Something put")
+ self.assertEqual(response.content, b"Something put")
self.assertTrue(len(response.headers) > 3)
@inlineCallbacks
def test_head_request(self):
response = yield self.client.head("/testing/")
- self.assertEqual(response.content, "")
+ self.assertEqual(response.content, b"")
self.assertTrue(len(response.headers) > 3)
@inlineCallbacks
def test_delete_request(self):
response = yield self.client.delete("/testing/")
- self.assertEqual(response.content, "")
+ self.assertEqual(response.content, b"")
self.assertTrue(len(response.headers) > 3)
@inlineCallbacks
def test_get_deferred_request(self):
response = yield self.client.get("/deferred_testing/")
- self.assertEqual(response.content, "Something...done!")
+ self.assertEqual(response.content, b"Something...done!")
self.assertTrue(len(response.headers) > 3)
@inlineCallbacks
@@ -131,8 +131,8 @@ def test_cookies(self):
response = yield self.client.get("/cookie_testing/")
self.assertEqual(
self.client.cookies.get_secure_cookie("test_cookie"),
- "test_value"
+ b"test_value"
)
response = yield self.client.post("/cookie_testing/")
- self.assertEqual(response.content, "test_value")
+ self.assertEqual(response.content, b"test_value")
diff --git a/cyclone/tests/test_utils.py b/cyclone/tests/test_utils.py
index 6b7af3f3fb..89520cce8d 100644
--- a/cyclone/tests/test_utils.py
+++ b/cyclone/tests/test_utils.py
@@ -13,20 +13,16 @@
# License for the specific language governing permissions and limitations
# under the License.
-import datetime
+from twisted.trial import unittest
+from cyclone.escape import xhtml_escape, xhtml_unescape
from cyclone.escape import json_encode, json_decode
-from cyclone.escape import recursive_unicode, linkify, _convert_entity
from cyclone.escape import squeeze, url_escape, url_unescape
from cyclone.escape import utf8, to_unicode, to_basestring
-from cyclone.escape import xhtml_escape, xhtml_unescape
+from cyclone.escape import recursive_unicode, linkify, _convert_entity
from cyclone.util import _emit, ObjectDict, import_object
-from twisted.trial import unittest
-
-try:
- from mock import Mock
-except ImportError:
- from unittest.mock import Mock
+from unittest.mock import Mock
+import datetime
class EscapeTest(unittest.TestCase):
@@ -57,7 +53,7 @@ def test_url_escape(self):
self.assertEqual(url_escape("a value"), "a+value")
def test_url_unescape(self):
- self.assertEqual(url_unescape("a+value", encoding=None), b"a value")
+ self.assertEqual(url_unescape("a+value", encoding=None), "a value")
self.assertEqual(url_unescape("a+value"), "a value")
def test_utf8(self):
diff --git a/cyclone/tests/test_web.py b/cyclone/tests/test_web.py
index bc5b0514d3..6f994079d6 100644
--- a/cyclone/tests/test_web.py
+++ b/cyclone/tests/test_web.py
@@ -13,26 +13,18 @@
# License for the specific language governing permissions and limitations
# under the License.
-import email.utils
-import time
-
-from cyclone.escape import unicode_type
-from cyclone.template import DictLoader
-from cyclone.web import Application, URLSpec, URLReverseError
+from twisted.trial import unittest
from cyclone.web import RequestHandler, HTTPError
+from cyclone.web import Application, URLSpec, URLReverseError
+from cyclone.escape import unicode_type
+from unittest.mock import Mock
from datetime import datetime
+from http import cookies as http_cookies
+import email.utils
+import calendar
+import time
from twisted.internet import defer, reactor
-from twisted.trial import unittest
-
-try:
- # py3
- import http.cookies as Cookie
- from unittest.mock import Mock
-except ImportError:
- # py2
- import Cookie
- from mock import Mock
-
+from cyclone.template import DictLoader
class RequestHandlerTest(unittest.TestCase):
def assertHasAttr(self, obj, attr_name):
@@ -73,7 +65,7 @@ def test_clear(self):
self.rh.clear()
self.assertEqual(
set(self.rh._headers.keys()),
- {"Server", "Content-Type", "Date", "Connection"},
+ set(["Server", "Content-Type", "Date", "Connection"])
)
self.assertEqual(self.rh._list_headers, [])
@@ -118,7 +110,7 @@ def test_convert_header_value(self):
def test_convert_unicode_header_value(self):
value = self.rh._convert_header_value(u"Value")
self.assertEqual(value, "Value")
- self.assertTrue(type(value) != unicode_type)
+ self.assertTrue(type(value) == unicode_type)
def test_convert_unicode_datetime_header_value(self):
now = datetime(2014, 4, 4)
@@ -187,7 +179,7 @@ def test_set_invalid_cookie(self):
ValueError, self.rh.set_cookie, "\x00bbb", "badcookie")
def test_set_cookie_already_exists(self):
- self.rh._new_cookie = Cookie.SimpleCookie()
+ self.rh._new_cookie = http_cookies.SimpleCookie()
self.rh._new_cookie["name"] = "value"
self.rh.set_cookie("name", "value")
@@ -251,7 +243,7 @@ def test_write_dict(self):
self.rh.write({"foo": "bar"})
self.assertEqual(
self.rh._write_buffer,
- ['{"foo": "bar"}']
+ [b'{"foo": "bar"}']
)
def test_create_template_loader(self):
@@ -374,24 +366,24 @@ def test_render_string(self):
_mkDeferred = self._mkDeferred
self.assertEqual(
self.handler.render_string("simple.html", msg="Hello World!"),
- "simple: Hello World!"
+ b"simple: Hello World!"
)
self.assertEqual(
self.handler.render_string(
"simple.html", msg=_mkDeferred("Hello Deferred!")),
- "simple: Hello Deferred!"
+ b"simple: Hello Deferred!"
)
d = self.handler.render_string(
"simple.html",
msg=_mkDeferred("Hello Deferred!", 0.1))
self.assertTrue(isinstance(d, defer.Deferred), d)
msg = yield d
- self.assertEqual(msg, "simple: Hello Deferred!")
+ self.assertEqual(msg, b"simple: Hello Deferred!")
def test_generate_headers(self):
headers = self.handler._generate_headers()
self.assertIn(
- "HTTP MOCK 200 OK",
+ b"HTTP MOCK 200 OK",
headers,
)
@@ -399,14 +391,14 @@ def test_generate_headers(self):
def test_simple_handler(self):
self.handler.get = lambda: self.handler.finish("HELLO WORLD")
page = yield self._execute_request(False)
- self.assertEqual(page, "HELLO WORLD")
+ self.assertEqual(page, b"HELLO WORLD")
@defer.inlineCallbacks
def test_deferred_handler(self):
self.handler.get = lambda: self._mkDeferred(
lambda: self.handler.finish("HELLO DEFERRED"), 0.01)
page = yield self._execute_request(False)
- self.assertEqual(page, "HELLO DEFERRED")
+ self.assertEqual(page, b"HELLO DEFERRED")
@defer.inlineCallbacks
def test_deferred_arg_in_render(self):
@@ -415,7 +407,7 @@ def test_deferred_arg_in_render(self):
"simple.html", msg=templateArg)
self.handler.get = handlerGetFn
page = yield self._execute_request(False)
- self.assertEqual(page, "simple: it works!")
+ self.assertEqual(page, b"simple: it works!")
def setUp(self):
self.app = app = Mock()
@@ -464,8 +456,7 @@ def _execute_request(self, outputHeaders):
handler._headers_written = True
handler._execute([])
yield self._onFinishD
-
- out = ""
+ out = b""
for (args, kwargs) in self.request.write.call_args_list:
self.assertFalse(kwargs)
self.assertEqual(len(args), 1)
diff --git a/cyclone/util.py b/cyclone/util.py
index 87ddfa9d49..f277e55b0a 100644
--- a/cyclone/util.py
+++ b/cyclone/util.py
@@ -15,8 +15,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import sys
-
from twisted.python import log
@@ -25,24 +23,16 @@ def _emit(self, eventDict):
if not text:
return
- # print "hello? '%s'" % repr(text)
timeStr = self.formatTime(eventDict['time'])
- # fmtDict = {'system': eventDict['system'],
- # 'text': text.replace("\n", "\n\t")}
- # msgStr = log._safeFormat("[%(system)s] %(text)s\n", fmtDict)
-
log.util.untilConcludes(self.write, "%s %s\n" % (timeStr,
- text.replace("\n", "\n\t")))
- log.util.untilConcludes(self.flush) # Hoorj!
+ text.replace("\n", "\n\t")))
+ log.util.untilConcludes(self.flush)
-
-# monkey patch, sorry
log.FileLogObserver.emit = _emit
class ObjectDict(dict):
"""Makes a dictionary behave like an object."""
-
def __getattr__(self, name):
try:
return self[name]
@@ -72,68 +62,10 @@ def import_object(name):
else:
raise ImportError("No method named %s" % parts[-1])
-# Fake unicode literal support: Python 3.2 doesn't have the u'' marker for
-# literal strings, and alternative solutions like "from __future__ import
-# unicode_literals" have other problems (see PEP 414). u() can be applied
-# to ascii strings that include \u escapes (but they must not contain
-# literal non-ascii characters).
-
-if not isinstance(b'', type('')):
- def u(s):
- return s
- unicode_type = str
- basestring_type = str
-else:
- def u(s):
- return s.decode('unicode_escape')
- # These names don't exist in py3, so use noqa comments to disable
- # warnings in flake8.
- unicode_type = unicode # noqa
- basestring_type = basestring # noqa
-
-bytes_type = str
-
-if sys.version_info < (3,):
- unicode_type = unicode
- unicode_char_type = unichr
- basestring_type = basestring
- import types
- list_type = types.ListType
- dict_type = types.DictType
- int_type = types.IntType
- tuple_type = types.TupleType
- string_type = types.StringType
- string_types = types.StringTypes
- exec("""
-def raise_exc_info(exc_info):
- raise exc_info[0], exc_info[1], exc_info[2]
-
-def exec_in(code, glob, loc=None):
- if isinstance(code, basestring):
- # exec(string) inherits the caller's future imports; compile
- # the string first to prevent that.
- code = compile(code, '', 'exec', dont_inherit=True)
- exec code in glob, loc
-""")
-else:
- unicode_type = str
- unicode_char_type = chr
- basestring_type = str
- list_type = list
- dict_type = dict
- int_type = int
- tuple_type = tuple
- string_type = str
- string_types = str
- exec("""
-def raise_exc_info(exc_info):
- raise exc_info[1].with_traceback(exc_info[2])
-def exec_in(code, glob, loc=None):
- if isinstance(code, str):
- code = compile(code, '', 'exec', dont_inherit=True)
- exec(code, glob, loc)
-""")
+bytes_type = bytes
+unicode_type = str
+basestring_type = str
def doctests(): # pragma: no cover
import doctest
diff --git a/cyclone/web.py b/cyclone/web.py
index 9dc26f573c..c2e7b9a17f 100644
--- a/cyclone/web.py
+++ b/cyclone/web.py
@@ -52,27 +52,9 @@ def get(self):
from __future__ import absolute_import, division, with_statement
-try:
- import Cookie # py2
-except ImportError:
- import http.cookies as Cookie # py3
-
-try:
- import urlparse # py2
-except ImportError:
- import urllib.parse as urlparse # py3
-
-try:
- from urllib import urlencode # py2
-except ImportError:
- from urllib.parse import urlencode # py3
-
-try:
- import httplib # py2
-except ImportError:
- import http.client as httplib # py3
-
-
+#import Cookie
+from http import cookies as http_cookies
+from http import client as http_client
import base64
import binascii
import calendar
@@ -93,6 +75,9 @@ def get(self):
import time
import traceback
import types
+import urllib
+#import urlparse
+from urllib import parse as urllib_parse
import uuid
import cyclone
@@ -101,10 +86,10 @@ def get(self):
from cyclone import locale
from cyclone import template
from cyclone.escape import utf8, _unicode
-from cyclone.util import ObjectDict, import_object, \
- bytes_type, unicode_type, \
- list_type, dict_type, int_type, tuple_type, string_type
-
+from cyclone.util import ObjectDict
+from cyclone.util import bytes_type
+from cyclone.util import import_object
+from cyclone.util import unicode_type
from io import BytesIO
from twisted.python import failure
from twisted.python import log
@@ -255,7 +240,7 @@ def clear(self):
self.set_header("Connection", "Keep-Alive")
self._write_buffer = []
self._status_code = 200
- self._reason = httplib.responses[200]
+ self._reason = http_client.responses[200]
def set_default_headers(self):
"""Override this to set HTTP headers at the beginning of the request.
@@ -280,7 +265,7 @@ def set_status(self, status_code, reason=None):
self._reason = escape.native_str(reason)
else:
try:
- self._reason = httplib.responses[status_code]
+ self._reason = http_client.responses[status_code]
except KeyError:
raise ValueError("unknown status code %d", status_code)
@@ -316,9 +301,9 @@ def clear_header(self, name):
def _convert_header_value(self, value):
if isinstance(value, bytes_type):
- pass
+ value = value.decode('utf-8')
elif isinstance(value, unicode_type):
- value = value.encode("utf-8")
+ pass
elif isinstance(value, numbers.Integral):
# return immediately since we know the converted value will be safe
return str(value)
@@ -413,7 +398,7 @@ def set_cookie(self, name, value, domain=None, expires=None, path="/",
# Don't let us accidentally inject bad stuff
raise ValueError("Invalid cookie %r: %r" % (name, value))
if not hasattr(self, "_new_cookie"):
- self._new_cookie = Cookie.SimpleCookie()
+ self._new_cookie = http_cookies.SimpleCookie()
if name in self._new_cookie:
del self._new_cookie[name]
self._new_cookie[name] = value
@@ -442,7 +427,7 @@ def clear_cookie(self, name, path="/", domain=None):
def clear_all_cookies(self):
"""Deletes all the cookies the user sent with this request."""
- for name in self.request.cookies.iterkeys():
+ for name in self.request.cookies.keys():
self.clear_cookie(name)
def set_secure_cookie(self, name, value, expires_days=30, **kwargs):
@@ -500,18 +485,17 @@ def redirect(self, url, permanent=False, status=None):
if status is None:
status = 301 if permanent else 302
else:
- assert isinstance(status, int_type) and 300 <= status <= 399
+ assert isinstance(status, int) and 300 <= status <= 399
self.set_status(status)
# Remove whitespace
- url = re.sub(r"[\x00-\x20]+", "", utf8(url))
+ url = re.sub(r"[\x00-\x20]+", "", url)
if not self.request.uri.startswith('/'):
request_uri = ''
if self.request.uri.startswith('//'):
request_uri = ''
else:
request_uri = self.request.uri
- self.set_header("Location", urlparse.urljoin(utf8(request_uri),
- url))
+ self.set_header("Location", urllib_parse.urljoin(request_uri, url))
self.finish()
def write(self, chunk):
@@ -530,12 +514,13 @@ def write(self, chunk):
http://haacked.com/archive/2008/11/20/\
anatomy-of-a-subtle-json-vulnerability.aspx
"""
+
if self._finished:
raise RuntimeError("Cannot write() after finish(). May be caused "
"by using async operations without the "
"@asynchronous decorator.")
- if isinstance(chunk, dict_type) or \
- (self.serialize_lists and isinstance(chunk, list_type)):
+ if isinstance(chunk, dict) or \
+ (self.serialize_lists and isinstance(chunk, list)):
chunk = escape.json_encode(chunk)
self.set_header("Content-Type", "application/json")
chunk = utf8(chunk)
@@ -698,7 +683,7 @@ def create_template_loader(self, template_path):
def flush(self, include_footers=False):
"""Flushes the current output buffer to the network."""
- chunk = "".join(self._write_buffer)
+ chunk = b"".join(self._write_buffer)
self._write_buffer = []
if not self._headers_written:
@@ -711,7 +696,7 @@ def flush(self, include_footers=False):
else:
for transform in self._transforms: # pragma: no cover
chunk = transform.transform_chunk(chunk, include_footers)
- headers = ""
+ headers = b""
# Ignore the chunk and only write the headers for HEAD requests
if self.request.method == "HEAD":
@@ -871,6 +856,7 @@ class BaseHandler(CustomErrorPageMixin, cyclone.web.RequestHandler):
kwargs['exception'] = exc_info[1]
try:
# Put the traceback into sys.exc_info()
+ #raise exc_info[0], exc_info[1], exc_info[2]
raise exc_info[0].with_traceback(exc_info[1], exc_info[2])
except Exception:
self.finish(self.get_error_html(status_code, **kwargs))
@@ -1135,7 +1121,7 @@ def _execute(self, transforms, *args, **kwargs):
def _deferred_handler(self, function, *args, **kwargs):
try:
result = function(*args, **kwargs)
- except:
+ except Exception as e:
return defer.fail(failure.Failure(
captureVars=defer.Deferred.debug))
else:
@@ -1159,8 +1145,7 @@ def _deferred_handler(self, function, *args, **kwargs):
def _execute_handler(self, r, args, kwargs):
if not self._finished:
args = [self.decode_argument(arg) for arg in args]
- kwargs = dict((k, self.decode_argument(v, name=k))
- for (k, v) in kwargs.iteritems())
+ kwargs = dict((k, self.decode_argument(v, name=k)) for (k, v) in kwargs.items())
function = getattr(self, self.request.method.lower(), self.default)
d = self._deferred_handler(function, *args, **kwargs)
d.addCallbacks(self._execute_success, self._execute_failure)
@@ -1178,12 +1163,11 @@ def _generate_headers(self):
lines = [utf8(self.request.version + " " +
str(self._status_code) +
" " + reason)]
- lines.extend([(utf8(n) + ": " + utf8(v)) for n, v in
- itertools.chain(self._headers.items(), self._list_headers)])
+ lines.extend([(utf8(n) + b": " + utf8(v)) for n, v in 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)))
- return "\r\n".join(lines) + "\r\n\r\n"
+ return b"\r\n".join(lines) + b"\r\n\r\n"
def _log(self):
"""Logs the current request.
@@ -1214,7 +1198,7 @@ def _handle_request_exception(self, e):
if e.log_message and self.settings.get("debug") is True:
log.msg(str(e))
- if e.status_code not in httplib.responses:
+ if e.status_code not in http_client.responses:
log.msg("Bad HTTP status code: " + repr(e.status_code))
e.status_code = 500
@@ -1435,12 +1419,12 @@ def add_handlers(self, host_pattern, host_handlers):
self.handlers.append((re.compile(host_pattern), handlers))
for spec in host_handlers:
- if isinstance(spec, tuple_type):
+ if isinstance(spec, tuple):
assert len(spec) in (2, 3)
pattern = spec[0]
handler = spec[1]
- if isinstance(handler, string_type):
+ if isinstance(handler, str):
# import the Module and instantiate the class
# Must be a fully qualified name (module.ClassName)
try:
@@ -1484,7 +1468,7 @@ def _load_ui_methods(self, methods):
if isinstance(methods, types.ModuleType):
self._load_ui_methods(dict((n, getattr(methods, n))
for n in dir(methods)))
- elif isinstance(methods, list_type):
+ elif isinstance(methods, list):
for m in methods:
self._load_ui_methods(m)
else:
@@ -1497,11 +1481,11 @@ def _load_ui_modules(self, modules):
if isinstance(modules, types.ModuleType):
self._load_ui_modules(dict((n, getattr(modules, n))
for n in dir(modules)))
- elif isinstance(modules, list_type):
+ elif isinstance(modules, list):
for m in modules:
self._load_ui_modules(m)
else:
- assert isinstance(modules, dict_type)
+ assert isinstance(modules, dict)
for name, cls in modules.items():
try:
if issubclass(cls, UIModule):
@@ -1530,7 +1514,7 @@ def __call__(self, request):
def unquote(s):
if s is None:
return s
- return escape.url_unescape(s, encoding=None)
+ return escape.url_unescape(s)
# Pass matched groups to the handler. Since
# match.groups() includes both named and
# unnamed groups,we want to use either groups
@@ -1615,8 +1599,7 @@ def __str__(self):
if self.log_message:
return self.log_message % self.args
else:
- return self.reason or \
- httplib.responses.get(self.status_code, "Unknown")
+ return self.reason or http_client.responses.get(self.status_code, "Unknown")
class HTTPAuthenticationRequired(HTTPError):
@@ -1952,13 +1935,13 @@ def wrapper(self, *args, **kwargs):
if self.request.method in ("GET", "HEAD"):
url = self.get_login_url()
if "?" not in url:
- if urlparse.urlsplit(url).scheme:
+ if urllib_parse.urlsplit(url).scheme:
# if login url is absolute, make next absolute too
next_url = self.request.full_url()
else:
next_url = self.request.uri
url = "%s?%s" % (url,
- urlencode(dict(next=next_url)))
+ urllib_parse.urlencode(dict(next=next_url)))
return self.redirect(url)
raise HTTPError(403)
return method(self, *args, **kwargs)
@@ -2065,7 +2048,7 @@ def embedded_javascript(self):
def javascript_files(self):
result = []
for f in self._get_resources("javascript_files"):
- if isinstance(f, (unicode, bytes_type)):
+ if isinstance(f, (str, bytes_type)):
result.append(f)
else:
result.extend(f)
@@ -2077,7 +2060,7 @@ def embedded_css(self):
def css_files(self):
result = []
for f in self._get_resources("css_files"):
- if isinstance(f, (unicode, bytes_type)):
+ if isinstance(f, (str, bytes_type)):
result.append(f)
else:
result.extend(f)
@@ -2089,9 +2072,11 @@ def html_head(self):
def html_body(self):
return "".join(self._get_resources("html_body"))
+
class URLReverseError(Exception):
"""Error generating reversed URL."""
+
class URLSpec(object):
"""Specifies mappings between URLs and handlers."""
def __init__(self, pattern, handler_class, kwargs=None, name=None):
@@ -2142,7 +2127,7 @@ def _find_groups(self):
if self.regex.groups != pattern.count('('):
# The pattern is too complicated for our simplistic matching,
# so we can't support reversing it.
- return None, None
+ return (None, None)
pieces = []
for fragment in pattern.split('('):
@@ -2153,7 +2138,7 @@ def _find_groups(self):
else:
pieces.append(fragment)
- return ''.join(pieces), self.regex.groups
+ return (''.join(pieces), self.regex.groups)
def reverse(self, *args, **kwargs):
if not self._path:
@@ -2178,7 +2163,7 @@ def reverse(self, *args, **kwargs):
if kwargs:
items = list(kwargs.items())
items.sort(key=lambda el: el[0])
- rv += "?" + urlencode(items)
+ rv += "?" + urllib_parse.urlencode(items)
return rv
url = URLSpec
@@ -2188,7 +2173,7 @@ def _time_independent_equals(a, b):
if len(a) != len(b):
return False
result = 0
- if isinstance(a[0], int_type): # python3 byte strings
+ if isinstance(a[0], int):
for x, y in zip(a, b):
result |= x ^ y
else: # python2
@@ -2198,8 +2183,8 @@ def _time_independent_equals(a, b):
def create_signed_value(secret, name, value):
- timestamp = utf8(str(int(time.time())))
- value = base64.b64encode(utf8(value))
+ timestamp = str(int(time.time()))
+ value = base64.b64encode(bytes(value,'utf8')).decode('utf8')
signature = _create_signature(secret, name, value, timestamp)
value = "|".join([value, timestamp, signature])
return value
@@ -2208,7 +2193,7 @@ def create_signed_value(secret, name, value):
def decode_signed_value(secret, name, value, max_age_days=31):
if not value:
return None
- parts = utf8(value).split("|")
+ parts = value.split("|")
if len(parts) != 3:
return None
signature = _create_signature(secret, name, parts[0], parts[1])
@@ -2240,4 +2225,4 @@ def _create_signature(secret, *parts):
hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
for part in parts:
hash.update(utf8(part))
- return utf8(hash.hexdigest())
+ return hash.hexdigest()
diff --git a/demos/bottle/bottledemo.py b/demos/bottle/bottledemo.py
index d65af14272..53ea628b60 100755
--- a/demos/bottle/bottledemo.py
+++ b/demos/bottle/bottledemo.py
@@ -63,7 +63,7 @@ def auth_login(cli):
try:
redis_pwd = yield cli.redisdb.get("cyclone:%s" % usr)
- except Exception as e:
+ except Exception, e:
log.msg("Redis failed to get('cyclone:%s'): %s" % (usr, str(e)))
raise cyclone.web.HTTPError(503) # Service Unavailable
@@ -112,7 +112,7 @@ def xmlrpc_echo(self, text):
from twisted.python.logfile import DailyLogFile
logFile = DailyLogFile.fromFullPath("server.log")
print("Logging to daily log file: server.log")
-except Exception as e:
+except Exception, e:
import sys
logFile = sys.stdout
diff --git a/demos/email/emaildemo.py b/demos/email/emaildemo.py
index fa75e4e6db..b490e293fe 100755
--- a/demos/email/emaildemo.py
+++ b/demos/email/emaildemo.py
@@ -81,7 +81,7 @@ def post(self):
response = yield cyclone.mail.sendmail(
self.settings.email_settings, msg)
self.render("response.html", title="Success", response=response)
- except Exception as e:
+ except Exception, e:
self.render("response.html", title="Failure", response=str(e))
diff --git a/demos/httpauth/httpauthdemo_mongo.py b/demos/httpauth/httpauthdemo_mongo.py
index ccd0cc601e..2dd487068f 100755
--- a/demos/httpauth/httpauthdemo_mongo.py
+++ b/demos/httpauth/httpauthdemo_mongo.py
@@ -62,7 +62,7 @@ def wrapper(self, *args, **kwargs):
response = yield self.mongodb.cyclonedb.users.find_one(
{"usr": usr, "pwd": pwd}, fields=["usr"])
mongo_usr = response.get("usr")
- except Exception as e:
+ except Exception, e:
log.msg("MongoDB failed to find(): %s" % str(e))
raise cyclone.web.HTTPError(503) # Service Unavailable
@@ -97,7 +97,7 @@ def post(self):
ObjId = yield self.mongodb.cyclonedb.users.update(
{"usr": usr}, {"usr": usr, "pwd": pwd},
upsert=True, safe=True)
- except Exception as e:
+ except Exception, e:
log.msg("MongoDB failed to upsert(): %s" % str(e))
raise cyclone.web.HTTPError(503) # Service Unavailable
diff --git a/demos/redis/redisdemo.py b/demos/redis/redisdemo.py
index 5214bc8cd0..f9c9bf81a8 100755
--- a/demos/redis/redisdemo.py
+++ b/demos/redis/redisdemo.py
@@ -118,7 +118,7 @@ class TextHandler(cyclone.web.RequestHandler, RedisMixin):
def get(self, key):
try:
value = yield self.dbconn.get(key)
- except Exception as e:
+ except Exception, e:
log.err("Redis failed to get('%s'): %s" % (key, str(e)))
raise cyclone.web.HTTPError(503)
@@ -130,7 +130,7 @@ def post(self, key):
value = self.get_argument("value")
try:
yield self.dbconn.set(key, value)
- except Exception as e:
+ except Exception, e:
log.err("Redis failed to set('%s', '%s'): %s" %
(key, value, str(e)))
raise cyclone.web.HTTPError(503)
@@ -142,7 +142,7 @@ def post(self, key):
def delete(self, key):
try:
n = yield self.dbconn.delete(key)
- except Exception as e:
+ except Exception, e:
log.err("Redis failed to del('%s'): %s" % (key, str(e)))
raise cyclone.web.HTTPError(503)
@@ -157,7 +157,7 @@ class QueueHandler(cyclone.web.RequestHandler, RedisMixin):
def get(self, channels):
try:
channels = channels.split(",")
- except Exception as e:
+ except Exception, e:
log.err("Could not split channel names: %s" % str(e))
raise cyclone.web.HTTPError(400, str(e))
@@ -176,7 +176,7 @@ def post(self, channel):
try:
n = yield self.dbconn.publish(channel, message.encode("utf-8"))
- except Exception as e:
+ except Exception, e:
log.msg("Redis failed to publish('%s', '%s'): %s" %
(channel, repr(message), str(e)))
raise cyclone.web.HTTPError(503)
diff --git a/demos/upload/uploaddemo.py b/demos/upload/uploaddemo.py
index 45322a924a..f0f10b1521 100755
--- a/demos/upload/uploaddemo.py
+++ b/demos/upload/uploaddemo.py
@@ -46,7 +46,7 @@ def __init__(self):
if not os.path.exists(settings["repository_path"]):
try:
os.mkdir(settings["repository_path"])
- except Exception as e:
+ except Exception, e:
print("mkdir failed: %s" % str(e))
sys.exit(1)
@@ -80,7 +80,7 @@ def post(self):
fp = open(os.path.abspath(fn), "w")
fp.write(body)
fp.close()
- except Exception as e:
+ except Exception, e:
log.msg("Could not write file: %s" % str(e))
raise cyclone.web.HTTPError(500)
diff --git a/demos/websocket/chat/chatdemo.py b/demos/websocket/chat/chatdemo.py
index c8e9d2667a..90dd069017 100755
--- a/demos/websocket/chat/chatdemo.py
+++ b/demos/websocket/chat/chatdemo.py
@@ -90,7 +90,7 @@ def send_updates(cls, chat):
for waiter in cls.waiters:
try:
waiter.sendMessage(chat)
- except Exception as e:
+ except Exception, e:
log.err("Error sending message. %s" % str(e))
def messageReceived(self, message):
diff --git a/setup.py b/setup.py
index fcc21e74f0..7aaf53359d 100644
--- a/setup.py
+++ b/setup.py
@@ -21,57 +21,25 @@
import setuptools
from distutils import log
-requires = ["twisted"]
-
-def version_cmp(version1, version2):
- """
- Return True if version1 is less greater than version2
- """
- def normalize(v):
- return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
- return normalize(version1) < normalize(version2)
-
-# Avoid installation problems on old RedHat distributions (ex. CentOS 5)
-# http://stackoverflow.com/questions/7340784/easy-install-pyopenssl-error
-py_version = platform.python_version()
-
-if (version_cmp(str(py_version), str('2.6'))):
- distname, version, _id = platform.dist()
-else:
- distname, version, _id = platform.linux_distribution()
-
-is_redhat = distname in ["CentOS", "redhat"]
-if is_redhat and version and StrictVersion(version) < StrictVersion('6.0'):
- requires.append("pyopenssl==0.12")
-else:
- requires.append("pyopenssl")
-
-extra = dict(extras_require={'ssl': requires})
-
-try:
- from setuptools.command import egg_info
- egg_info.write_toplevel_names
-except (ImportError, AttributeError):
- pass
-else:
- """
- 'twisted' should not occur in the top_level.txt file as this
- triggers a bug in pip that removes all of twisted when a package
- with a twisted plugin is removed.
- """
- def _top_level_package(name):
- return name.split('.', 1)[0]
-
- def _hacked_write_toplevel_names(cmd, basename, filename):
- pkgs = dict.fromkeys(
- [_top_level_package(k)
- for k in cmd.distribution.iter_distribution_names()
- if _top_level_package(k) != "twisted"
- ]
- )
- cmd.write_file("top-level names", filename, '\n'.join(pkgs) + '\n')
-
- egg_info.write_toplevel_names = _hacked_write_toplevel_names
+CLASSIFIERS = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Operating System :: POSIX',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3 :: Only',
+ 'Topic :: Internet',
+ 'Topic :: Utilities',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Topic :: Internet :: WWW/HTTP :: WSGI',
+ 'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
+ 'Topic :: Internet :: WWW/HTTP :: Dynamic Content']
setuptools.setup(
@@ -90,12 +58,6 @@ def _hacked_write_toplevel_names(cmd, basename, filename):
"appskel_foreman.zip",
"appskel_signup.zip"]},
scripts=["scripts/cyclone"],
- **extra
-)
-
-try:
- from twisted.plugin import IPlugin, getPlugins
- list(getPlugins(IPlugin))
-except Exception as e:
- log.warn("*** Failed to update Twisted plugin cache. ***")
- log.warn(str(e))
+ install_requires=["twisted==19.2.1","pyOpenSSL==19.0.0"],
+ classifiers=CLASSIFIERS,
+)
\ No newline at end of file
diff --git a/website/sphinx/deferreds.rst b/website/sphinx/deferreds.rst
index a5f4492c68..e5fc75963b 100644
--- a/website/sphinx/deferreds.rst
+++ b/website/sphinx/deferreds.rst
@@ -215,7 +215,7 @@ This is how you handle it::
def get(self):
try:
response = yield fetch("http://freegeoip.net/xml/")
- except Exception as e:
+ except Exception, e:
raise web.HTTPError(503, str(e)) # Service Unavailable
...
diff --git a/website/sphinx/redis.rst b/website/sphinx/redis.rst
index f31c7c2c2c..3d0cee642b 100644
--- a/website/sphinx/redis.rst
+++ b/website/sphinx/redis.rst
@@ -241,7 +241,7 @@ Example:
def get(self, key):
try:
value = yield self.redis_conn.get(key)
- except Exception as e:
+ except Exception, e:
log.msg("Redis failed to get('%s'): %s" % (key, str(e)))
raise cyclone.web.HTTPError(503)
@@ -253,7 +253,7 @@ Example:
value = self.get_argument("value")
try:
yield self.redis_conn.set(key, value)
- except Exception as e:
+ except Exception, e:
log.msg("Redis failed to set('%s', '%s'): %s" % (key, value, str(e)))
raise cyclone.web.HTTPError(503)
@@ -264,7 +264,7 @@ Example:
def delete(self, key):
try:
n = yield self.redis_conn.delete(key)
- except Exception as e:
+ except Exception, e:
log.msg("Redis failed to del('%s'): %s" % (key, str(e)))
raise cyclone.web.HTTPError(503)
@@ -438,7 +438,7 @@ Example:
try:
v = yield rc.get("foo")
print "foo=", v
- except Exception as e:
+ except Exception, e:
print "can't get foo:", e
# Commit, and get all responses from transaction.