diff --git a/setup.py b/setup.py index 42982e9..eaa0b10 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ keywords='wsgi', author='Ben Bangert, Ian Bicking, Mark Ramm', author_email='invalid@invalid.com', - url='https://bitbucket.org/bbangert/weberror', + url='https://github.com/Pylons/weberror', license='MIT', packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), include_package_data=True, diff --git a/weberror/collector.py b/weberror/collector.py index ead263d..3c65c2c 100644 --- a/weberror/collector.py +++ b/weberror/collector.py @@ -23,10 +23,13 @@ import sys import traceback import time -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO +if sys.version_info[:2] < (3, 0): + try: + from cStringIO import StringIO + except ImportError: + from StringIO import StringIO +else: + from io import StringIO import linecache from weberror.util import source_encoding, serial_number_generator @@ -380,7 +383,10 @@ def safeStr(self, obj): return str(obj) except UnicodeEncodeError: try: - return unicode(obj).encode(FALLBACK_ENCODING, 'replace') + if sys.version_info[:2] < (3, 0): + return unicode(obj).encode(FALLBACK_ENCODING, 'replace') + else: + return str(obj).encode(FALLBACK_ENCODING, 'replace') except UnicodeEncodeError: # This is when something is really messed up, but this can # happen when the __str__ of an object has to handle unicode diff --git a/weberror/errormiddleware.py b/weberror/errormiddleware.py index bbf33bb..c6f4512 100644 --- a/weberror/errormiddleware.py +++ b/weberror/errormiddleware.py @@ -7,16 +7,20 @@ import sys import traceback import cgi -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO from weberror import formatter, collector, reporter from paste import wsgilib from paste import request from paste.util import import_string import types +if sys.version_info[:2] < (3, 0): + try: + from cStringIO import StringIO + except ImportError: + from StringIO import StringIO +else: + from io import StringIO + __all__ = ['ErrorMiddleware', 'handle_exception'] class _NoDefault(object): diff --git a/weberror/evalcontext.py b/weberror/evalcontext.py index 5ce311d..5ca55c4 100644 --- a/weberror/evalcontext.py +++ b/weberror/evalcontext.py @@ -1,9 +1,13 @@ -from cStringIO import StringIO import traceback import threading import pdb import sys +if sys.version_info[:2] < (3, 0): + from cStringIO import StringIO +else: + from io import StringIO + exec_lock = threading.Lock() class EvalContext(object): @@ -32,7 +36,7 @@ def exec_expr(self, s): sys.stdout = out try: code = compile(s, '', "single", 0, 1) - exec code in self.namespace, self.globs + exec(code in self.namespace, self.globs) debugger.set_continue() except KeyboardInterrupt: raise diff --git a/weberror/evalexception.py b/weberror/evalexception.py index 7ce0b6a..1993b18 100644 --- a/weberror/evalexception.py +++ b/weberror/evalexception.py @@ -22,12 +22,10 @@ ``wsgi.errors``, so you can open it up in another browser window. """ -import httplib import sys import os import cgi import traceback -from cStringIO import StringIO import pprint import itertools import time @@ -43,13 +41,21 @@ from paste import urlparser from paste.util import import_string -import evalcontext from weberror import errormiddleware, formatter, collector from weberror.util import security from tempita import HTMLTemplate from webob import Request, Response from webob import exc +if sys.version_info[:2] < (3, 0): + import httplib as httplib + from cStringIO import StringIO + import evalcontext +else: + import http.client as httplib + from io import StringIO + from . import evalcontext + limit = 200 def html_quote(v): @@ -124,7 +130,7 @@ def application(environ, start_response): form['environ'] = environ try: res = func(*args, **form.mixed()) - except ValueError, ve: + except ValueError as ve: status = '500 Server Error' res = 'There was an error: %s' % \ html_quote(ve) @@ -150,7 +156,7 @@ def debug_info_replacement(self, req): debugcount = req.params['debugcount'] try: debugcount = int(debugcount) - except ValueError, e: + except ValueError as e: return exc.HTTPBadRequest( "Invalid value for debugcount (%r): %s" % (debugcount, e)) @@ -525,7 +531,7 @@ def frame(self, tbid): if id(frame) == tbid: return frame else: - raise ValueError, ( + raise ValueError( "No frame by id %s found from %r" % (tbid, self.frames)) def wsgi_application(self, environ, start_response): @@ -641,7 +647,7 @@ def pprint_format(value, safe=False): out = StringIO() try: pprint.pprint(value, out) - except Exception, e: + except Exception as e: if safe: out.write('Error: %s' % e) else: diff --git a/weberror/formatter.py b/weberror/formatter.py index b2e95ab..ea0fa15 100644 --- a/weberror/formatter.py +++ b/weberror/formatter.py @@ -274,7 +274,7 @@ def format_extra_data(self, importance, title, value): for n, v in items: try: v = repr(v) - except Exception, e: + except Exception as e: v = 'Cannot display: %s' % e v = truncate(v) lines.append(' %s: %s' % (n, v)) @@ -379,7 +379,7 @@ def zebra_table(self, title, rows, table_class="variables"): for name, value in rows: try: value = repr(value) - except Exception, e: + except Exception as e: value = 'Cannot print: %s' % e odd = not odd table.append( @@ -449,7 +449,7 @@ def format_collected_data(self, exc_data): libs = get_libraries(self.extra_kwargs.get('libraries')) if libs: libraries = newdoc.createElement('libraries') - for k, v in libs.iteritems(): + for k, v in libs.items(): lib = newdoc.createElement('library') lib.attributes['version'] = v lib.attributes['name'] = k @@ -484,7 +484,7 @@ def format_collected_data(self, exc_data): # @@@ TODO: Put in a way to optionally toggle including variables # variables = newdoc.createElement('variables') # xml_frame.appendChild(variables) - # for name, value in frame.locals.iteritems(): + # for name, value in frame.locals.items(): # if isinstance(value, unicode): # value = value.encode('ascii', 'xmlcharrefreplace') # variable = newdoc.createElement('variable') @@ -493,8 +493,12 @@ def format_collected_data(self, exc_data): # variables.appendChild(variable) etype = exc_data.exception_type - if not isinstance(etype, basestring): - etype = etype.__name__ + if sys.version_info[:2] < (3, 0): + if not isinstance(etype, basestring): + etype = etype.__name__ + else: + if not isinstance(etype, str): + etype = etype.__name__ top_element.appendChild(self.format_exception_info( etype, exc_data.exception_value, newdoc, frame)) diff --git a/weberror/pdbcapture.py b/weberror/pdbcapture.py index 7f94e5f..d76fc30 100644 --- a/weberror/pdbcapture.py +++ b/weberror/pdbcapture.py @@ -66,7 +66,7 @@ def __call__(self, environ, start_response): resp = state['response'] return resp(environ, start_response) if 'exc_info' in state: - raise state['exc_info'][0], state['exc_info'][1], state['exc_info'][2] + raise Exception(state['exc_info'][0], state['exc_info'][1], state['exc_info'][2]) self.states[id] = state tmpl = self.get_template('pdbcapture_response.html') body = tmpl.substitute(req=req, state=state, id=id) diff --git a/weberror/reporter.py b/weberror/reporter.py index 5b5e0c0..4c87b30 100644 --- a/weberror/reporter.py +++ b/weberror/reporter.py @@ -1,13 +1,20 @@ # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php -from email.MIMEText import MIMEText -from email.MIMEMultipart import MIMEMultipart import smtplib import ssl import time from weberror import formatter from email.utils import formatdate +import sys + +if sys.version_info[:2] < (3, 0): + from email.MIMEText import MIMEText + from email.MIMEMultipart import MIMEMultipart +else: + from email.mime.text import MIMEText + from email.mime.multipart import MIMEMultipart + class Reporter(object): @@ -142,8 +149,9 @@ def __call__(self, environ, start_response): def as_str(v): if isinstance(v, str): return v - if not isinstance(v, unicode): - v = unicode(v) - if isinstance(v, unicode): - v = v.encode('utf8') + if sys.version_info[:2] < (3, 0): + if not isinstance(v, unicode): + v = unicode(v) + if isinstance(v, unicode): + v = v.encode('utf8') return v diff --git a/weberror/util/escaping.py b/weberror/util/escaping.py index c8dfeab..77de794 100644 --- a/weberror/util/escaping.py +++ b/weberror/util/escaping.py @@ -6,8 +6,13 @@ # the MIT License: http://www.opensource.org/licenses/mit-license.php -import re, cgi, urllib, htmlentitydefs, codecs -from StringIO import StringIO +import sys, re, cgi, urllib, codecs +if sys.version_info[:2] < (3, 0): + from cStringIO import StringIO + import htmlentitydefs +else: + from io import StringIO + import html.entities as htmlentitydefs xml_escapes = { '&' : '&', @@ -43,12 +48,15 @@ def trim(string): class Decode(object): def __getattr__(self, key): def decode(x): - if isinstance(x, unicode): - return x - elif not isinstance(x, str): - return unicode(str(x), encoding=key) + if sys.version_info[:2] < (3, 0): + if isinstance(x, unicode): + return x + elif not isinstance(x, str): + return unicode(str(x), encoding=key) + else: + return unicode(x, encoding=key) else: - return unicode(x, encoding=key) + return str(x).encode(key) return decode decode = Decode() @@ -63,7 +71,7 @@ def is_ascii_str(text): class XMLEntityEscaper(object): def __init__(self, codepoint2name, name2codepoint): self.codepoint2entity = dict([(c, u'&%s;' % n) - for c,n in codepoint2name.iteritems()]) + for c,n in codepoint2name.items()]) self.name2codepoint = name2codepoint def escape_entities(self, text): @@ -71,7 +79,10 @@ def escape_entities(self, text): Only characters corresponding to a named entity are replaced. """ - return unicode(text).translate(self.codepoint2entity) + if sys.version_info[:2] < (3, 0): + return unicode(text).translate(self.codepoint2entity) + else: + return str(text) def __escape(self, m): codepoint = ord(m.group()) @@ -92,7 +103,11 @@ def escape(self, text): The return value is guaranteed to be ASCII. """ - return self.__escapable.sub(self.__escape, unicode(text) + if sys.version_info[:2] < (3, 0): + return self.__escapable.sub(self.__escape, unicode(text) + ).encode('ascii') + else: + return self.__escapable.sub(self.__escape, str(text) ).encode('ascii') # XXX: This regexp will not match all valid XML entity names__. @@ -117,7 +132,10 @@ def __unescape(self, m): # U+FFFD = "REPLACEMENT CHARACTER" if codepoint < 128: return chr(codepoint) - return unichr(codepoint) + if sys.version_info[:2] < (3, 0): + return unichr(codepoint) + else: + return chr(codepoint) def unescape(self, text): """Unescape character references. @@ -149,7 +167,9 @@ def htmlentityreplace_errors(ex): # Handle encoding errors bad_text = ex.object[ex.start:ex.end] text = _html_entities_escaper.escape(bad_text) - return (unicode(text), ex.end) + if sys.version_info[:2] < (3, 0): + return (unicode(text), ex.end) + return (str(text), ex.end) raise ex codecs.register_error('htmlentityreplace', htmlentityreplace_errors) diff --git a/weberror/util/security.py b/weberror/util/security.py index dd26be8..514f512 100644 --- a/weberror/util/security.py +++ b/weberror/util/security.py @@ -1,6 +1,6 @@ import calendar from datetime import datetime, timedelta -import os +import os, sys import hashlib import hmac @@ -21,8 +21,12 @@ def constant_time_compare(actual, expected): expected_len = len(expected) result = actual_len ^ expected_len if expected_len > 0: - for i in xrange(actual_len): - result |= ord(actual[i]) ^ ord(expected[i % expected_len]) + if sys.version_info[:2] < (3, 0): + for i in xrange(actual_len): + result |= ord(actual[i]) ^ ord(expected[i % expected_len]) + else: + for i in range(actual_len): + result |= ord(actual[i]) ^ ord(expected[i % expected_len]) return result == 0 @@ -43,7 +47,7 @@ def valid_csrf_token(secret, token): try: expiry_ts, hashed = token.split(',') expiry_dt = datetime.utcfromtimestamp(int(expiry_ts)) - except ValueError, e: + except ValueError as e: return False if expiry_dt < datetime.utcnow(): diff --git a/weberror/util/serial_number_generator.py b/weberror/util/serial_number_generator.py index c4b9f7e..d2dd741 100644 --- a/weberror/util/serial_number_generator.py +++ b/weberror/util/serial_number_generator.py @@ -7,6 +7,7 @@ to create compact representations that are unique for a certain string (or concatenation of strings) """ +import sys try: from hashlib import md5 @@ -19,8 +20,12 @@ def lazy_result(return_type, dummy_initial=None): """Decorator to allow for on-demand evaluation (limited scope of use!)""" - if not issubclass(return_type, basestring): - raise NotImplementedError + if sys.version_info[:2] < (3, 0): + if not issubclass(return_type, basestring): + raise NotImplementedError + else: + if not issubclass(return_type, str): + raise NotImplementedError class _lazy_class(return_type): """lazified access to value of %s type""" % return_type.__name__ @@ -41,7 +46,8 @@ def __call__(self, **kwargs): # not everything implemented (and will never be 100%) _lazy_class.__str__ = lambda self: str(self()) _lazy_class.__repr__ = lambda self: repr(self()) - _lazy_class.__cmp__ = lambda self, other: cmp(self(), other) + if sys.version_info[:2] < (3, 0): + _lazy_class.__cmp__ = lambda self, other: cmp(self(), other) _lazy_class.__getslice__ = lambda self, **kws: slice(self(), **kws) def _generating_lazy_class(generator): @@ -54,10 +60,12 @@ def make_identifier(number): """ Encodes a number as an identifier. """ - if not isinstance(number, (int, long)): - raise ValueError( - "You can only make identifiers out of integers (not %r)" - % number) + if sys.version_info[:2] < (3, 0): + if not isinstance(number, (int, long)): + raise ValueError("You can only make identifiers out of integers (not %r)" % number) + else: + if not isinstance(number, int): + raise ValueError("You can only make identifiers out of integers (not %r)" % number) if number < 0: raise ValueError( "You cannot make identifiers out of negative numbers: %r" @@ -90,11 +98,15 @@ def hash_identifier(s, length, pad=True, hasher=md5, prefix='', # Accept sha/md5 modules as well as callables hasher = hasher.new if length > 26 and hasher is md5: - raise ValueError, ( + raise ValueError( "md5 cannot create hashes longer than 26 characters in " "length (you gave %s)" % length) - if isinstance(s, unicode): - s = s.encode('utf-8') + if sys.version_info[:2] < (3, 0): + if isinstance(s, unicode): + s = s.encode('utf-8') + else: + if isinstance(s, str): + s = s.encode('utf-8') h = hasher(str(s)) bin_hash = h.digest() modulo = base ** length