From 660d72d1f2b1fa3656b826564d827b7bf5537daf Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 24 Mar 2016 22:25:53 +0000 Subject: [PATCH] Trival changes to support Python 3. --- doc/demo.txt | 2 +- quixote/__init__.py | 2 +- quixote/demo/altdemo.py | 11 +++++---- quixote/demo/integers.ptl | 4 ++-- quixote/demo/mini_demo.py | 4 ++-- quixote/demo/root.ptl | 4 ++-- quixote/directory.py | 10 ++++---- quixote/form/compatibility.py | 2 +- quixote/form/form.py | 20 +++++++--------- quixote/form/widget.py | 27 +++++++++++---------- quixote/form1/form.py | 28 +++++++++++----------- quixote/form1/widget.py | 21 ++++++++--------- quixote/html/__init__.py | 2 +- quixote/html/_py_htmltext.py | 22 ++++++++--------- quixote/html/test/utest_html.py | 12 ++++------ quixote/http_request.py | 17 +++++++------- quixote/http_response.py | 35 ++++++++++++++-------------- quixote/ptl/__init__.py | 4 ++-- quixote/ptl/ihooks_local.py | 4 ++-- quixote/ptl/ptlrun.py | 7 +++--- quixote/ptl/qx_distutils.py | 10 ++++---- quixote/publish.py | 4 ++-- quixote/publish1.py | 10 ++++---- quixote/sendmail.py | 11 ++++----- quixote/server/_fcgi.py | 19 +++++++-------- quixote/server/cgi_server.py | 4 ++-- quixote/server/fastcgi_server.py | 4 ++-- quixote/server/mod_python_handler.py | 2 +- quixote/server/scgi_server.py | 4 ++-- quixote/server/simple_server.py | 2 +- quixote/server/twisted_server.py | 2 +- quixote/session.py | 17 +++++--------- quixote/test/ua_test.py | 4 ++-- quixote/util.py | 9 ++++--- tests/qx_testlib.py | 4 ++-- tests/qx_testserver.py | 2 +- 36 files changed, 162 insertions(+), 184 deletions(-) diff --git a/doc/demo.txt b/doc/demo.txt index 621c30d..fcd15bf 100644 --- a/doc/demo.txt +++ b/doc/demo.txt @@ -12,7 +12,7 @@ different server scripts may be found in the scripts themselves and in web-server.txt. To start, though, the easiest way to view the demos is as follows: in a terminal window, run:: - python -m quixote.server.simple_server + python3 -m quixote.server.simple_server and in a browser, open http://localhost:8080. If you wish to run the demo on a remote computer, you will need to ask the server to listen diff --git a/quixote/__init__.py b/quixote/__init__.py index fbf598e..2ba8cfd 100644 --- a/quixote/__init__.py +++ b/quixote/__init__.py @@ -3,7 +3,7 @@ A small and flexible Python web application framework. """ -__version__ = '2.9' +__version__ = '3.0' # These are frequently needed by Quixote applications. from quixote.publish import \ diff --git a/quixote/demo/altdemo.py b/quixote/demo/altdemo.py index 7a9edb5..c0332c0 100644 --- a/quixote/demo/altdemo.py +++ b/quixote/demo/altdemo.py @@ -1,3 +1,5 @@ ++#!/usr/bin/env python3 ++ """An alternative Quixote demo. This version is contained in a single module and does not use PTL. The easiest way to run this demo is to use the simple HTTP server included with Quixote. For example: @@ -54,9 +56,8 @@ class RootDirectory(Directory): content += htmltext( '

Hello, %s.

') % get_user() content += htmltext('

%s

' % href('logout', 'logout')) - sessions = get_session_manager().items() + sessions = sorted(get_session_manager().items()) if sessions: - sessions.sort() content += htmltext('' '' '' @@ -177,17 +178,17 @@ try: session_class=PersistentSession, session_mapping=sessions) def forget_changes(self, session): - print 'abort changes', get_session() + print('abort changes', get_session()) connection.abort() def commit_changes(self, session): - print 'commit changes', get_session() + print('commit changes', get_session()) connection.commit() def create_durus_publisher(): global connection filename = os.path.join(tempfile.gettempdir(), 'quixote-demo.durus') - print 'Opening %r as a Durus database.' % filename + print('Opening %r as a Durus database.' % filename) connection = Connection(FileStorage(filename)) root = connection.get_root() session_manager = root.get('session_manager', None) diff --git a/quixote/demo/integers.ptl b/quixote/demo/integers.ptl index b94f11f..31a6d59 100644 --- a/quixote/demo/integers.ptl +++ b/quixote/demo/integers.ptl @@ -4,7 +4,7 @@ from quixote.directory import Directory, export from quixote.errors import TraversalError def fact(n): - f = 1L + f = 1 while n > 1: f *= n n -= 1 @@ -15,7 +15,7 @@ class IntegerUI(Directory): def __init__(self, component): try: self.n = int(component) - except ValueError, exc: + except ValueError as exc: raise TraversalError(str(exc)) @export diff --git a/quixote/demo/mini_demo.py b/quixote/demo/mini_demo.py index 5980089..1d0413d 100755 --- a/quixote/demo/mini_demo.py +++ b/quixote/demo/mini_demo.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ A minimal Quixote demo. If you have the 'quixote' package in your Python path, you can run it like this: @@ -35,5 +35,5 @@ def create_publisher(): if __name__ == '__main__': from quixote.server.simple_server import run - print 'creating demo listening on http://localhost:8080/' + print('creating demo listening on http://localhost:8080/') run(create_publisher, host='localhost', port=8080) diff --git a/quixote/demo/root.ptl b/quixote/demo/root.ptl index 582b58d..4073899 100644 --- a/quixote/demo/root.ptl +++ b/quixote/demo/root.ptl @@ -13,7 +13,7 @@ class RootDirectory(Directory): @export(name='') def index [html] (self): - print "debug message from the index page" + print("debug message from the index page") """ @@ -65,7 +65,7 @@ class RootDirectory(Directory): @export def error(self): - raise ValueError, "this is a Python exception" + raise ValueError("this is a Python exception") @export def publish_error(self): diff --git a/quixote/directory.py b/quixote/directory.py index c0f59b1..cc3f0ea 100644 --- a/quixote/directory.py +++ b/quixote/directory.py @@ -28,13 +28,11 @@ class DirectoryClass(type): return cls -class Directory(object): +class Directory(object, metaclass=DirectoryClass): """ Instance attributes: none """ - __metaclass__ = DirectoryClass - # A list containing strings or 2-tuples of strings that map external # names to internal names. Note that the empty string will be # implicitly mapped to '_q_index'. @@ -99,7 +97,7 @@ class Directory(object): if "" in self._q_exports and not quixote.get_request().form: # Fix missing trailing slash. path = quixote.get_path() - print "Adding slash to: %r " % path + print("Adding slash to: %r " % path) return quixote.redirect(path + "/", permanent=True) else: raise TraversalError(private_msg=('directory %r is not ' @@ -143,7 +141,7 @@ def export(func=None, name=None): """ def do_export(func): if name is None: - func._q_name = func.func_name + func._q_name = func.__name__ else: func._q_name = name return func @@ -160,7 +158,7 @@ def subdir(func=None, name=None): """ def do_export(func): if name is None: - func._q_name = func.func_name + func._q_name = func.__name__ else: func._q_name = name return property(func) diff --git a/quixote/form/compatibility.py b/quixote/form/compatibility.py index 41d8f01..7247827 100644 --- a/quixote/form/compatibility.py +++ b/quixote/form/compatibility.py @@ -69,7 +69,7 @@ class Form(_Form): return values def action(self, submit, values): - raise NotImplementedError, "sub-classes must implement 'action()'" + raise NotImplementedError("sub-classes must implement 'action()'") def handle(self): """handle() -> string diff --git a/quixote/form/form.py b/quixote/form/form.py index 5c30724..88e5faf 100644 --- a/quixote/form/form.py +++ b/quixote/form/form.py @@ -88,7 +88,7 @@ class Form(object): if enctype is not None and enctype not in ( "application/x-www-form-urlencoded", "multipart/form-data"): - raise ValueError, ("Form enctype must be " + raise ValueError("Form enctype must be " "'application/x-www-form-urlencoded' or " "'multipart/form-data', not %r" % enctype) self.enctype = enctype @@ -117,14 +117,11 @@ class Form(object): try: return self._names[name].parse() except KeyError: - raise KeyError, 'no widget named %r' % name - - def has_key(self, name): - """Return true if the widget named 'name' is in the form.""" - return name in self._names + raise KeyError('no widget named %r' % name) def __contains__(self, name): - return self.has_key(name) + """Return true if the widget named 'name' is in the form.""" + return name in self._names def get(self, name, default=None): """(name:string, default=None) -> any @@ -156,7 +153,7 @@ class Form(object): does not include sub-widgets (e.g. widgets that are part of CompositeWidgets) """ - return self._names.values() + return list(self._names.values()) # -- Form processing and error checking ---------------------------- @@ -223,14 +220,14 @@ class Form(object): """ widget = self._names.get(name) if not widget: - raise KeyError, "unknown name %r" % name + raise KeyError("unknown name %r" % name) widget.set_error(error) # -- Form population methods --------------------------------------- def add(self, widget_class, name, *args, **kwargs): if name in self._names: - raise ValueError, "form already has '%s' widget" % name + raise ValueError("form already has '%s' widget" % name) # add 'id' attribute if not already present if 'id' not in kwargs: kwargs['id'] = name @@ -348,8 +345,7 @@ class Form(object): sorted by code_id. """ form_code = [] - code_ids = javascript_code.keys() - code_ids.sort() + code_ids = sorted(javascript_code.keys()) for code_id in code_ids: code = javascript_code[code_id] if code: diff --git a/quixote/form/widget.py b/quixote/form/widget.py index 82f9e53..857905e 100644 --- a/quixote/form/widget.py +++ b/quixote/form/widget.py @@ -133,7 +133,7 @@ class Widget(object): if submitted: try: self._parse(request) - except WidgetValueError, exc: + except WidgetValueError as exc: self.set_error(stringify(exc)) if (self.required and self.value is None and not self.has_error()): @@ -143,7 +143,7 @@ class Widget(object): def _parse(self, request): # subclasses may override but this is not part of the public API value = request.form.get(self.name) - if isinstance(value, basestring) and value.strip(): + if isinstance(value, str) and value.strip(): self.value = value else: self.value = None @@ -311,7 +311,7 @@ class SelectWidget(Widget): if not options: # The HTML and XHTML specifications require select elements to # contain at least one option. - raise ValueError, "a non-empty list of 'options' is required" + raise ValueError("a non-empty list of 'options' is required") else: self.set_options(options, sort) self.verify_selection = verify_selection @@ -350,10 +350,10 @@ class SelectWidget(Widget): return keys # can't use OIDs, try using descriptions used_keys = {} - keys = map(stringify, descriptions) + keys = list(map(stringify, descriptions)) for key in keys: if key in used_keys: - raise ValueError, "duplicated descriptions (provide keys)" + raise ValueError("duplicated descriptions (provide keys)") used_keys[key] = 1 return keys @@ -391,14 +391,14 @@ class SelectWidget(Widget): descriptions.append(description) keys.append(stringify(key)) else: - raise ValueError, 'invalid options %r' % options + raise ValueError('invalid options %r' % options) else: values = descriptions = options if not keys: keys = self._generate_keys(values, descriptions) - options = zip(values, descriptions, keys) + options = list(zip(values, descriptions, keys)) if sort: def make_sort_key(option): @@ -407,8 +407,7 @@ class SelectWidget(Widget): return ('', option) else: return (stringify(description).lower(), option) - doptions = map(make_sort_key, options) - doptions.sort() + doptions = sorted(map(make_sort_key, options)) options = [item[1] for item in doptions] self.options = options @@ -439,7 +438,7 @@ class SelectWidget(Widget): self.set_options(allowed_values, sort) else: assert len(descriptions) == len(allowed_values) - self.set_options(zip(allowed_values, descriptions), sort) + self.set_options(list(zip(allowed_values, descriptions)), sort) def is_selected(self, value): return value == self.value @@ -613,7 +612,7 @@ class HiddenWidget(Widget): def set_error(self, error): if error is not None: - raise TypeError, 'error not allowed on hidden widgets' + raise TypeError('error not allowed on hidden widgets') def render_content(self): if self.value is None: @@ -769,7 +768,7 @@ class CompositeWidget(Widget): def add(self, widget_class, name, *args, **kwargs): if name in self._names: - raise ValueError, 'the name %r is already used' % name + raise ValueError('the name %r is already used' % name) if self.attrs.get('disabled') and 'disabled' not in kwargs: kwargs['disabled'] = True widget = widget_class(subname(self.name, name), *args, **kwargs) @@ -805,7 +804,7 @@ class WidgetList(CompositeWidget): assert type(element_kwargs) is dict, ( "value '%s' element_kwargs not a dict: " "got %r" % (name, element_kwargs)) - assert isinstance(add_element_label, (basestring, htmltext)), ( + assert isinstance(add_element_label, (str, htmltext)), ( "value '%s'add_element_label not a string: " "got %r" % (name, add_element_label)) @@ -899,7 +898,7 @@ class WidgetDict(CompositeWidget): assert type(element_value_kwargs) is dict, ( "value '%s' element_value_kwargs not a dict: " "got %r" % (name, element_value_kwargs)) - assert isinstance(add_element_label, (basestring, htmltext)), ( + assert isinstance(add_element_label, (str, htmltext)), ( 'value %r element_name not a string: ' 'got %r' % (name, add_element_label)) diff --git a/quixote/form1/form.py b/quixote/form1/form.py index 1adc978..afc436a 100644 --- a/quixote/form1/form.py +++ b/quixote/form1/form.py @@ -89,7 +89,7 @@ class Form: if enctype is not None and enctype not in ( "application/x-www-form-urlencoded", "multipart/form-data"): - raise ValueError, ("Form enctype must be " + raise ValueError("Form enctype must be " "'application/x-www-form-urlencoded' or " "'multipart/form-data', not %r" % enctype) self.enctype = enctype @@ -231,7 +231,7 @@ class Form: return r def _render_required_notice(self, request): - if filter(None, self.required.values()): + if any(self.required.values()): r = htmltext('') @@ -256,8 +256,7 @@ class Form: javascript_code = request.response.javascript_code if javascript_code: form_code = [] - code_ids = javascript_code.keys() - code_ids.sort() + code_ids = sorted(javascript_code.keys()) for code_id in code_ids: code = javascript_code[code_id] if code: @@ -289,7 +288,7 @@ class Form: for widget in self.widget_order: try: val = widget.parse(request) - except FormValueError, exc: + except FormValueError as exc: self.error[widget.name] = exc.msg else: values[widget.name] = val @@ -306,7 +305,7 @@ class Form: checking cannot be done here -- it must done in the 'process()' method. """ - raise NotImplementedError, "sub-classes must implement 'action()'" + raise NotImplementedError("sub-classes must implement 'action()'") def handle(self, request): """handle(request : HTTPRequest) -> string @@ -396,7 +395,7 @@ class Form: """ try: return self.widgets[name].parse(request) - except FormValueError, exc: + except FormValueError as exc: self.error[name] = str(exc) return None @@ -440,14 +439,13 @@ class Form: mod(value) elif mode == "direct": if not hasattr(target, key): - raise AttributeError, \ - ("target object %s doesn't have attribute %s" % - (`target`, key)) + raise AttributeError("target object %s doesn't have attribute %s" % + (repr(target), key)) setattr(target, key, value) elif mode == "dict": target[key] = value else: - raise ValueError, "unknown update mode %s" % `mode` + raise ValueError("unknown update mode %s" % repr(mode)) def clear_widget(self, widget_name): self.widgets[widget_name].clear() @@ -479,9 +477,9 @@ class Form: Returns the new Widget. """ if name in self.widgets: - raise ValueError, "form already has '%s' variable" % name + raise ValueError("form already has '%s' variable" % name) klass = get_widget_class(widget_type) - new_widget = apply(klass, (name, value), args) + new_widget = klass(*(name, value), **args) self.widgets[name] = new_widget self.widget_order.append(new_widget) @@ -493,7 +491,7 @@ class Form: def add_submit_button(self, name, value): global _widget_class if name in self.widgets: - raise ValueError, "form already has '%s' variable" % name + raise ValueError("form already has '%s' variable" % name) new_widget = _widget_class['submit_button'](name, value) self.widgets[name] = new_widget @@ -501,7 +499,7 @@ class Form: def add_cancel_button(self, caption, url): if not isinstance(url, (StringType, htmltext)): - raise TypeError, "url must be a string (got %r)" % url + raise TypeError("url must be a string (got %r)" % url) self.add_submit_button("cancel", caption) self.cancel_url = url diff --git a/quixote/form1/widget.py b/quixote/form1/widget.py index 2f181f8..7589f78 100644 --- a/quixote/form1/widget.py +++ b/quixote/form1/widget.py @@ -99,7 +99,7 @@ class Widget: from quixote.form.form import get_widget_class klass = get_widget_class(widget_type) name = self.get_subwidget_name(widget_name) - return apply(klass, (name, value), args) + return klass(*(name, value), **args) # class Widget @@ -325,10 +325,10 @@ class SelectWidget (Widget): return keys # can't use OIDs, try using descriptions used_keys = {} - keys = map(str, descriptions) + keys = list(map(str, descriptions)) for key in keys: if key in used_keys: - raise ValueError, "duplicated descriptions (provide keys)" + raise ValueError("duplicated descriptions (provide keys)") used_keys[key] = 1 return keys @@ -367,14 +367,14 @@ class SelectWidget (Widget): descriptions.append(description) keys.append(str(key)) else: - raise ValueError, 'invalid options %r' % options + raise ValueError('invalid options %r' % options) else: values = descriptions = options if not keys: keys = self._generate_keys(values, descriptions) - options = zip(values, descriptions, keys) + options = list(zip(values, descriptions, keys)) if sort: def make_sort_key(option): @@ -383,8 +383,7 @@ class SelectWidget (Widget): return ('', option) else: return (str(description).lower(), option) - doptions = map(make_sort_key, options) - doptions.sort() + doptions = sorted(map(make_sort_key, options)) options = [item[1] for item in doptions] self.options = options @@ -395,7 +394,7 @@ class SelectWidget (Widget): return value else: if self.verify_selection: - raise FormValueError, "invalid value selected" + raise FormValueError("invalid value selected") else: return self.options[0][0] @@ -413,7 +412,7 @@ class SelectWidget (Widget): self.set_options(allowed_values, sort) else: assert len(descriptions) == len(allowed_values) - self.set_options(zip(allowed_values, descriptions), sort) + self.set_options(list(zip(allowed_values, descriptions)), sort) def is_selected(self, value): @@ -456,7 +455,7 @@ class SingleSelectWidget (SelectWidget): self.value = None if parsed_key: if type(parsed_key) is ListType: - raise FormValueError, "cannot select multiple values" + raise FormValueError("cannot select multiple values") self.value = self.parse_single_selection(parsed_key) return self.value @@ -637,7 +636,7 @@ class NumberWidget (StringWidget): try: self.value = self.type_converter(value) except ValueError: - raise FormValueError, self.type_error + raise FormValueError(self.type_error) return self.value diff --git a/quixote/html/__init__.py b/quixote/html/__init__.py index fa83011..395b1a6 100644 --- a/quixote/html/__init__.py +++ b/quixote/html/__init__.py @@ -100,7 +100,7 @@ def url_quote(value, fallback=None): """ if value is None: if fallback is None: - raise ValueError, "value is None and no fallback supplied" + raise ValueError("value is None and no fallback supplied") else: return fallback return urllib.quote(stringify(value)) diff --git a/quixote/html/_py_htmltext.py b/quixote/html/_py_htmltext.py index ff267a6..69a870b 100644 --- a/quixote/html/_py_htmltext.py +++ b/quixote/html/_py_htmltext.py @@ -47,7 +47,7 @@ class htmltext(object): # raise AttributeError, 'immutable object' def __getstate__(self): - raise ValueError, 'htmltext objects should not be pickled' + raise ValueError('htmltext objects should not be pickled') def __repr__(self): return '' % self.s @@ -71,14 +71,14 @@ class htmltext(object): return htmltext(self.s % _wraparg(args)) def format(self, *args, **kwargs): - args = map(_wraparg, args) + args = list(map(_wraparg, args)) newkw = {} - for k, v in kwargs.iteritems(): + for k, v in kwargs.items(): newkw[k] = _wraparg(v) return htmltext(self.s.format(*args, **newkw)) def __add__(self, other): - if isinstance(other, basestring): + if isinstance(other, str): return htmltext(self.s + _escape_string(other)) elif isinstance(other, htmltext): return htmltext(self.s + other.s) @@ -86,7 +86,7 @@ class htmltext(object): return NotImplemented def __radd__(self, other): - if isinstance(other, basestring): + if isinstance(other, str): return htmltext(_escape_string(other) + self.s) else: return NotImplemented @@ -99,11 +99,10 @@ class htmltext(object): for item in items: if isinstance(item, htmltext): quoted_items.append(stringify(item)) - elif isinstance(item, basestring): + elif isinstance(item, str): quoted_items.append(_escape_string(item)) else: - raise TypeError( - 'join() requires string arguments (got %r)' % item) + raise TypeError('join() requires string arguments (got %r)' % item) return htmltext(self.s.join(quoted_items)) def startswith(self, s): @@ -152,7 +151,7 @@ class _QuoteWrapper(object): return _escape_string(stringify(self.value)) def __repr__(self): - return _escape_string(`self.value`) + return _escape_string(repr(self.value)) def __getitem__(self, key): return _wraparg(self.value[key]) @@ -175,13 +174,12 @@ def _wraparg(arg): # necessary to work around a PyString_Format bug in Python. Should # be fixed in Python 2.5 return stringify(arg) - elif isinstance(arg, unicode): + elif isinstance(arg, str): # again, work around PyString_Format bug return _UnicodeWrapper(arg) elif (isinstance(arg, int) or - isinstance(arg, long) or isinstance(arg, float)): - # ints, longs, floats are okay + # ints, floats are okay return arg else: # everything is gets wrapped diff --git a/quixote/html/test/utest_html.py b/quixote/html/test/utest_html.py index 0e9d91f..ef7516e 100755 --- a/quixote/html/test/utest_html.py +++ b/quixote/html/test/utest_html.py @@ -25,10 +25,10 @@ class BrokenError(Exception): class Broken: def __str__(self): - raise BrokenError, 'eieee' + raise BrokenError('eieee') def __repr__(self): - raise BrokenError, 'eieee' + raise BrokenError('eieee') htmltext = escape = htmlescape = TemplateIO = stringify = None @@ -104,7 +104,7 @@ class HTMLTextTest (UTest): assert s != 'bar' assert s == htmltext('foo') assert s != htmltext('bar') - assert htmltext(u'\u1234') == u'\u1234' + assert htmltext('\\u1234') == '\\u1234' assert htmltext('1') != 1 assert 1 != s @@ -127,7 +127,7 @@ class HTMLTextTest (UTest): assert isinstance(s + markupchars, htmltext) assert markupchars + s == quotedchars + "foo" assert isinstance(markupchars + s, htmltext) - assert markupchars + htmltext(u'') == quotedchars + assert markupchars + htmltext('') == quotedchars try: s + 1 assert 0 @@ -136,10 +136,8 @@ class HTMLTextTest (UTest): 1 + s assert 0 except TypeError: pass - # mixing unicode and str + assert repr(htmltext('a') + htmltext('b')) == "" - assert repr(htmltext(u'a') + htmltext('b')) == "" - assert repr(htmltext('a') + htmltext(u'b')) == "" def check_repeat(self): s = htmltext('a') diff --git a/quixote/http_request.py b/quixote/http_request.py index 78daae8..0708ae9 100644 --- a/quixote/http_request.py +++ b/quixote/http_request.py @@ -92,7 +92,9 @@ def parse_query(qs, charset): Parse a query given as a string argument and return a dictionary. """ fields = {} - for chunk in filter(None, qs.split('&')): + for chunk in qs.split('&'): + if not chunk: + continue if '=' not in chunk: name = chunk value = '' @@ -309,7 +311,7 @@ class HTTPRequest: return self.environ.get('REQUEST_METHOD', 'GET') def formiter(self): - return self.form.iteritems() + return self.form.items() def get_scheme(self): return self.scheme @@ -370,7 +372,7 @@ class HTTPRequest: else: path_comps = path.split('/') if abs(n) > len(path_comps)-1: - raise ValueError, "n=%d too big for path '%s'" % (n, path) + raise ValueError("n=%d too big for path '%s'" % (n, path)) if n > 0: return '/'.join(path_comps[:-n]) elif n < 0: @@ -471,21 +473,18 @@ class HTTPRequest: row='%-15s %s' result.append("Form:") - L = self.form.items() ; L.sort() - for k,v in L: + for k, v in sorted(self.form.items()): result.append(row % (k,v)) result.append("") result.append("Cookies:") - L = self.cookies.items() ; L.sort() - for k,v in L: + for k, v in sorted(self.cookies.items()): result.append(row % (k,v)) result.append("") result.append("Environment:") - L = self.environ.items() ; L.sort() - for k,v in L: + for k, v in sorted(self.environ.items()): result.append(row % (k,v)) return "\n".join(result) diff --git a/quixote/http_response.py b/quixote/http_response.py index 0a792da..430e63f 100644 --- a/quixote/http_response.py +++ b/quixote/http_response.py @@ -9,7 +9,7 @@ try: except ImportError: pass import struct -from rfc822 import formatdate +from email.utils import formatdate import quixote from quixote.html import stringify @@ -82,7 +82,7 @@ _GZIP_EXCLUDE = set(["application/pdf", ]) def _LOWU32(i): - return i & 0xFFFFFFFFL + return i & 0xFFFFFFFF class HTTPResponse: """ @@ -180,7 +180,7 @@ class HTTPResponse: if not charset: self.charset = None else: - self.charset = str(charset).lower() + self.charset = charset.lower() def set_status(self, status, reason=None): """set_status(status : int, reason : string = None) @@ -193,9 +193,9 @@ class HTTPResponse: if status == 493, the reason for status 400 will be used. """ if not isinstance(status, int): - raise TypeError, "status must be an integer" + raise TypeError("status must be an integer") if not (100 <= status <= 599): - raise ValueError, "status must be between 100 and 599" + raise ValueError("status must be between 100 and 599") self.status_code = status if reason is None: @@ -233,9 +233,9 @@ class HTTPResponse: self.cache = seconds + 60*(minutes + 60*(hours + 24*days)) def _encode_chunk(self, chunk): - """(chunk : str | unicode) -> str + """(chunk : str) -> bytes """ - if isinstance(chunk, unicode): + if isinstance(chunk, str): if self.charset is None: # iso-8859-1 is the default for the HTTP protocol if charset # parameter of content-type header is not provided @@ -265,13 +265,13 @@ class HTTPResponse: def _generate_compressed(self, body): co = zlib.compressobj(6, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0) - crc = zlib.crc32('') & 0xffffffffL + crc = zlib.crc32('') & 0xffffffff n = 0 yield _GZIP_HEADER for chunk in body: - if not isinstance(chunk, str): + if not isinstance(chunk, bytes): chunk = self._encode_chunk(stringify(chunk)) - crc = zlib.crc32(chunk, crc) & 0xffffffffL + crc = zlib.crc32(chunk, crc) & 0xffffffff n += len(chunk) yield co.compress(chunk) crc = struct.pack(" 0: - expire_date = formatdate(now + self.cache) + expire_date = formatdate(now + self.cache, usegmt=True) cache_control = "max-age=%d" % self.cache else: # This is the default case and makes sense for a @@ -491,7 +492,7 @@ class HTTPResponse: pass elif isinstance(self.body, Stream): for chunk in self.body: - if not isinstance(chunk, str): + if not isinstance(chunk, bytes): chunk = self._encode_chunk(chunk) yield chunk else: diff --git a/quixote/ptl/__init__.py b/quixote/ptl/__init__.py index 052107c..2cef76a 100644 --- a/quixote/ptl/__init__.py +++ b/quixote/ptl/__init__.py @@ -26,7 +26,7 @@ Here's a sample plain text template:: def foo [plain] (x, y = 5): "This is a chunk of static text." greeting = "hello world" # statement, no PTL output - print 'Input values:', x, y + print('Input values:', x, y) z = x + y """You can plug in variables like x (%s) in a variety of ways.""" % x @@ -53,7 +53,7 @@ value of that template. Look at the first part of the example again:: def foo [plain] (x, y = 5): "This is a chunk of static text." greeting = "hello world" # statement, no PTL output - print 'Input values:', x, y + print('Input values:', x, y) z = x + y """You can plug in variables like x (%s) in a variety of ways.""" % x diff --git a/quixote/ptl/ihooks_local.py b/quixote/ptl/ihooks_local.py index 7eebd4c..c101bf4 100644 --- a/quixote/ptl/ihooks_local.py +++ b/quixote/ptl/ihooks_local.py @@ -92,9 +92,9 @@ class _Verbose: def message(self, format, *args): if args: - print format%args + print(format%args) else: - print format + print(format) class BasicModuleLoader(_Verbose): diff --git a/quixote/ptl/ptlrun.py b/quixote/ptl/ptlrun.py index e207124..ecc5611 100755 --- a/quixote/ptl/ptlrun.py +++ b/quixote/ptl/ptlrun.py @@ -1,6 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys from quixote.ptl.ptl_compile import compile_template -if __name__ == '__main__': - exec compile_template(open(sys.argv[1]), sys.argv[1]) + +if __name__ == '__main__': + exec(compile_template(open(sys.argv[1]), sys.argv[1])) diff --git a/quixote/ptl/qx_distutils.py b/quixote/ptl/qx_distutils.py index c39b76b..098a490 100644 --- a/quixote/ptl/qx_distutils.py +++ b/quixote/ptl/qx_distutils.py @@ -4,7 +4,6 @@ PTL files. import os, string from glob import glob -from types import StringType, ListType, TupleType from distutils.command.build_py import build_py class qx_build_py(build_py): @@ -26,11 +25,10 @@ class qx_build_py(build_py): return modules def build_module(self, module, module_file, package): - if type(package) is StringType: - package = string.split(package, '.') - elif type(package) not in (ListType, TupleType): - raise TypeError, \ - "'package' must be a string (dot-separated), list, or tuple" + if type(package) is str: + package = package.split('.') + elif type(package) not in (list, tuple): + raise TypeError("'package' must be a string (dot-separated), list, or tuple") # Now put the module source file into the "build" area -- this is # easy, we just copy it somewhere under self.build_lib (the build diff --git a/quixote/publish.py b/quixote/publish.py index 118a8be..cb7de16 100644 --- a/quixote/publish.py +++ b/quixote/publish.py @@ -68,7 +68,7 @@ class Publisher: self.session_manager = NullSessionManager() if _publisher is not None: - raise RuntimeError, "only one instance of Publisher allowed" + raise RuntimeError("only one instance of Publisher allowed") _publisher = self if not hasattr(getattr(root_directory, '_q_traverse'), '__call__'): @@ -252,7 +252,7 @@ class Publisher: try: self.parse_request(request) output = self.try_publish(request) - except PublishError, exc: + except PublishError as exc: # Exit the publishing loop and return a result right away. output = self.finish_interrupted_request(exc) except: diff --git a/quixote/publish1.py b/quixote/publish1.py index 88a5140..896afc9 100644 --- a/quixote/publish1.py +++ b/quixote/publish1.py @@ -23,7 +23,7 @@ class Publisher(_Publisher): def __init__(self, root_namespace, config=None): from quixote.config import Config - if type(root_namespace) is types.StringType: + if isinstance(root_namespace, str): root_namespace = _get_module(root_namespace) self.namespace_stack = [root_namespace] if config is None: @@ -67,7 +67,7 @@ class RootDirectory(Directory): elif hasattr(object, '__call__'): output = object(request) if output is None: - raise RuntimeError, 'callable %s returned None' % repr(object) + raise RuntimeError('callable %r returned None' % object) # Uh-oh: 'object' is neither a string nor a callable. else: @@ -199,7 +199,7 @@ def _get_component(container, component, request, namespace_stack): else: # check for an explicit external to internal mapping for value in container._q_exports: - if type(value) is types.TupleType: + if type(value) is tuple: if value[0] == component: internal_name = value[1] break @@ -236,7 +236,7 @@ def _get_component(container, component, request, namespace_stack): elif hasattr(container, "_q_resolve"): object = container._q_resolve(internal_name) if object is None: - raise RuntimeError, ("component listed in _q_exports, " + raise RuntimeError("component listed in _q_exports, " "but not returned by _q_resolve(%r)" % internal_name) else: @@ -264,4 +264,4 @@ def _get_component(container, component, request, namespace_stack): def isstring(x): - return isinstance(x, (str, unicode, htmltext)) + return isinstance(x, (str, htmltext)) diff --git a/quixote/sendmail.py b/quixote/sendmail.py index 047851e..50a6bf4 100644 --- a/quixote/sendmail.py +++ b/quixote/sendmail.py @@ -3,7 +3,6 @@ Tools for sending mail from Quixote applications. """ import re -from types import ListType, TupleType from smtplib import SMTP import quixote @@ -36,7 +35,7 @@ class RFC822Mailbox: Create a new RFC822Mailbox instance. The variety of call signatures is purely for your convenience. """ - if (len(args) == 1 and type(args[0]) is TupleType): + if (len(args) == 1 and type(args[0]) is tuple): args = args[0] if len(args) == 1: @@ -193,9 +192,9 @@ def sendmail(subject, msg_body, to_addrs, if config is not None: mail_debug_addr = mail_debug_addr or config.mail_debug_addr - if not isinstance(to_addrs, ListType): + if not isinstance(to_addrs, list): raise TypeError("'to_addrs' must be a list") - if not (cc_addrs is None or isinstance(cc_addrs, ListType)): + if not (cc_addrs is None or isinstance(cc_addrs, list)): raise TypeError("'cc_addrs' must be a list or None") # Make sure we have a "From" address @@ -205,9 +204,9 @@ def sendmail(subject, msg_body, to_addrs, # Ensure all of our addresses are really RFC822Mailbox objects. from_addr = _ensure_mailbox(from_addr) - to_addrs = map(_ensure_mailbox, to_addrs) + to_addrs = list(map(_ensure_mailbox, to_addrs)) if cc_addrs: - cc_addrs = map(_ensure_mailbox, cc_addrs) + cc_addrs = list(map(_ensure_mailbox, cc_addrs)) # Start building the message headers. headers = ["From: %s" % from_addr.format(), diff --git a/quixote/server/_fcgi.py b/quixote/server/_fcgi.py index e92ce4d..4de6858 100644 --- a/quixote/server/_fcgi.py +++ b/quixote/server/_fcgi.py @@ -33,7 +33,7 @@ #------------------------------------------------------------------------ import os, sys, string, socket, errno, struct -from StringIO import StringIO +from io import StringIO import cgi #--------------------------------------------------------------------------- @@ -158,7 +158,7 @@ class record: #--------------------------------------------------------------------------- -_lowbits = ~(1L << 31) # everything but the 31st bit +_lowbits = ~(1 << 31) # everything but the 31st bit def readPair(s, pos): nameLen = ord(s[pos]) ; pos = pos+1 @@ -174,7 +174,7 @@ def readPair(s, pos): #--------------------------------------------------------------------------- -_highbit = (1L << 31) +_highbit = (1 << 31) def writePair(name, value): l = len(name) @@ -236,7 +236,7 @@ class FCGI: if 'FCGI_WEB_SERVER_ADDRS' in os.environ: good_addrs = string.split(os.environ['FCGI_WEB_SERVER_ADDRS'], ',') - good_addrs = map(string.strip, good_addrs) # Remove whitespace + good_addrs = list(map(string.strip, good_addrs)) # Remove whitespace else: good_addrs = None @@ -248,7 +248,7 @@ class FCGI: # Check if the connection is from a legal address if good_addrs != None and addr not in good_addrs: - raise error, 'Connection from invalid server!' + raise error('Connection from invalid server!') while remaining: r = record() @@ -388,7 +388,8 @@ def _startup(): s = socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM) s.getpeername() - except socket.error, (err, errmsg): + except socket.error as xxx_todo_changeme: + (err, errmsg) = xxx_todo_changeme.args if err != errno.ENOTCONN: # must be a non-fastCGI environment _isFCGI = 0 return @@ -419,8 +420,7 @@ def _test(): if 'CONTENT_LENGTH' in req.env: cl = string.atoi(req.env['CONTENT_LENGTH']) doc.append('
POST data (%s):
' % cl)
-                    keys = fs.keys()
-                    keys.sort()
+                    keys = sorted(fs.keys())
                     for k in keys:
                         val = fs[k]
                         if type(val) == type([]):
@@ -433,8 +433,7 @@ def _test():
 
 
                 doc.append('


')
-                keys = req.env.keys()
-                keys.sort()
+                keys = sorted(req.env.keys())
                 for k in keys:
                     doc.append('%-20s :  %s\n' % (k, req.env[k]))
                 doc.append('\n


\n') diff --git a/quixote/server/cgi_server.py b/quixote/server/cgi_server.py index 499ed9e..7c05653 100755 --- a/quixote/server/cgi_server.py +++ b/quixote/server/cgi_server.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import os @@ -13,7 +13,7 @@ def run(create_publisher): response = publisher.process(sys.__stdin__, os.environ) try: response.write(sys.__stdout__) - except IOError, err: + except IOError as err: publisher.log("IOError while sending response ignored: %s" % err) diff --git a/quixote/server/fastcgi_server.py b/quixote/server/fastcgi_server.py index 3e1b297..77ad094 100755 --- a/quixote/server/fastcgi_server.py +++ b/quixote/server/fastcgi_server.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Server for Quixote applications that use FastCGI. It should work for CGI too but the cgi_server module is preferred as it is more portable. @@ -13,7 +13,7 @@ def run(create_publisher): response = publisher.process(f.inp, f.env) try: response.write(f.out) - except IOError, err: + except IOError as err: publisher.log("IOError while sending response ignored: %s" % err) f.Finish() diff --git a/quixote/server/mod_python_handler.py b/quixote/server/mod_python_handler.py index 997d334..eaed114 100644 --- a/quixote/server/mod_python_handler.py +++ b/quixote/server/mod_python_handler.py @@ -80,7 +80,7 @@ def run(publisher, req): apache.build_cgi_env(req)) try: response.write(apache.CGIStdout(req)) - except IOError, err: + except IOError as err: publisher.log("IOError while sending response ignored: %s" % err) return apache.OK diff --git a/quixote/server/scgi_server.py b/quixote/server/scgi_server.py index 9a5fee4..9880448 100755 --- a/quixote/server/scgi_server.py +++ b/quixote/server/scgi_server.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """A SCGI server that uses Quixote to publish dynamic content. """ @@ -30,7 +30,7 @@ class QuixoteHandler(scgi_server.SCGIHandler): input.close() output.close() conn.close() - except IOError, err: + except IOError as err: self.publisher.log("IOError while sending response " "ignored: %s" % err) diff --git a/quixote/server/simple_server.py b/quixote/server/simple_server.py index f2f6d9f..1727ffa 100755 --- a/quixote/server/simple_server.py +++ b/quixote/server/simple_server.py @@ -44,7 +44,7 @@ class HTTPRequestHandler(BaseHTTPRequestHandler): else: accept = accept + line[7:].split(',') env['HTTP_ACCEPT'] = ','.join(accept) - co = filter(None, self.headers.getheaders('cookie')) + co = [c for c in self.headers.get_all('cookie') or [] if c] if co: env['HTTP_COOKIE'] = ', '.join(co) env.update(self.required_cgi_environment) diff --git a/quixote/server/twisted_server.py b/quixote/server/twisted_server.py index 658f488..1198f06 100755 --- a/quixote/server/twisted_server.py +++ b/quixote/server/twisted_server.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """An HTTP server for Twisted that publishes a Quixote application. """ diff --git a/quixote/session.py b/quixote/session.py index 0ef63fb..7dbb3fc 100644 --- a/quixote/session.py +++ b/quixote/session.py @@ -104,23 +104,21 @@ class SessionManager: Return the list of session IDs of sessions in this session manager. """ - return self.sessions.keys() + return list(self.sessions.keys()) def sorted_keys(self): """() -> [string] Return the same list as keys(), but sorted. """ - keys = self.keys() - keys.sort() - return keys + return sorted(self.keys()) def values(self): """() -> [Session] Return the list of sessions in this session manager. """ - return self.sessions.values() + return list(self.sessions.values()) def items(self): """() -> [(string, Session)] @@ -128,7 +126,7 @@ class SessionManager: Return the list of (session_id, session) pairs in this session manager. """ - return self.sessions.items() + return list(self.sessions.items()) def get(self, session_id, default=None): """(session_id : string, default : any = None) -> Session @@ -139,7 +137,7 @@ class SessionManager: return self.sessions.get(session_id, default) def __iter__(self): - return self.sessions.itervalues() + return iter(self.sessions.values()) def __getitem__(self, session_id): """(session_id : string) -> Session @@ -149,7 +147,7 @@ class SessionManager: """ return self.sessions[session_id] - def has_key(self, session_id): + def __contains__(self, session_id): """(session_id : string) -> boolean Return true if a session identified by 'session_id' exists in @@ -157,9 +155,6 @@ class SessionManager: """ return session_id in self.sessions - def __contains__(self, session_id): - return self.has_key(session_id) - def has_session(self, session_id): return self.has_key(session_id) diff --git a/quixote/test/ua_test.py b/quixote/test/ua_test.py index 57c4e84..6f95991 100644 --- a/quixote/test/ua_test.py +++ b/quixote/test/ua_test.py @@ -24,6 +24,6 @@ if __name__ == '__main__': req = HTTPRequest(None, env) (name, version) = req.guess_browser_version() if name is None: - print "%s -> ???" % line + print("%s -> ???" % line) else: - print "%s -> (%s, %s)" % (line, name, version) + print("%s -> (%s, %s)" % (line, name, version)) diff --git a/quixote/util.py b/quixote/util.py index fdfa28b..7006fbd 100644 --- a/quixote/util.py +++ b/quixote/util.py @@ -163,7 +163,7 @@ class StaticFile: # be followed self.path = path if not os.path.isabs(path): - raise ValueError, "Path %r is not absolute" % path + raise ValueError("Path %r is not absolute" % path) # Decide the Content-Type of the file guess_mime, guess_enc = mimetypes.guess_type(os.path.basename(path), strict=False) @@ -174,8 +174,7 @@ class StaticFile: def __call__(self): if not self.follow_symlinks and os.path.islink(self.path): - raise errors.TraversalError(private_msg="Path %r is a symlink" - % self.path) + raise errors.TraversalError(private_msg="Path %r is a symlink" % self.path) request = quixote.get_request() response = quixote.get_response() @@ -192,7 +191,7 @@ class StaticFile: stat = os.stat(self.path) except OSError: raise errors.TraversalError - last_modified = formatdate(stat.st_mtime) + last_modified = formatdate(stat.st_mtime, usegmt=True) if last_modified == request.get_header('If-Modified-Since'): # handle exact match of If-Modified-Since header response.set_status(304) @@ -241,7 +240,7 @@ class StaticDirectory(Directory): # Check that the supplied path is absolute self.path = path if not os.path.isabs(path): - raise ValueError, "Path %r is not absolute" % path + raise ValueError("Path %r is not absolute" % path) self.use_cache = use_cache self.cache = {} diff --git a/tests/qx_testlib.py b/tests/qx_testlib.py index 92599b4..0745fed 100644 --- a/tests/qx_testlib.py +++ b/tests/qx_testlib.py @@ -10,7 +10,7 @@ import urllib _server_url = None testdir = os.path.dirname(__file__) -print 'testdir is:', testdir +print('testdir is:', testdir) sys.path.insert(0, os.path.abspath(os.path.join(testdir, '..'))) import twill @@ -53,7 +53,7 @@ def run_server(create_fn, PORT=None): outfd = tempfile.mkstemp('quixote_tst')[0] - print 'STARTING:', sys.executable, 'tests/qx_testserver.py', os.getcwd() + print('STARTING:', sys.executable, 'tests/qx_testserver.py', os.getcwd()) process = subprocess.Popen([sys.executable, '-u', 'qx_testserver.py'], stderr=subprocess.STDOUT, stdout=outfd) diff --git a/tests/qx_testserver.py b/tests/qx_testserver.py index 1a4f36f..dda87bd 100644 --- a/tests/qx_testserver.py +++ b/tests/qx_testserver.py @@ -25,7 +25,7 @@ class TestServer(Directory): if __name__ == '__main__': from quixote.server.simple_server import run port = int(os.environ.get('QX_TEST_PORT', '8080')) - print 'starting qx_testserver on port %d.' % (port,) + print('starting qx_testserver on port %d.' % (port,)) try: run(create_publisher, port=port) except KeyboardInterrupt:
Session
' '* = required field' '