Trival changes to support Python 3.

This commit is contained in:
Neil Schemenauer 2016-03-24 22:25:53 +00:00
parent efe8e3d235
commit 660d72d1f2
36 changed files with 162 additions and 184 deletions

View File

@ -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

View File

@ -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 \

View File

@ -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(
'<p>Hello, %s.</p>') % get_user()
content += htmltext('<p>%s</p>' % href('logout', 'logout'))
sessions = get_session_manager().items()
sessions = sorted(get_session_manager().items())
if sessions:
sessions.sort()
content += htmltext('<table><tr>'
'<th></th>'
'<th>Session</th>'
@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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")
"""
<html>
<head>
@ -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):

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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))

View File

@ -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('<tr><td colspan="3">'
'<b>*</b> = <em>required field</em>'
'</td></tr>')
@ -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

View File

@ -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

View File

@ -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))

View File

@ -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 '<htmltext %r>' % 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

View File

@ -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')) == "<htmltext 'ab'>"
assert repr(htmltext(u'a') + htmltext('b')) == "<htmltext u'ab'>"
assert repr(htmltext('a') + htmltext(u'b')) == "<htmltext u'ab'>"
def check_repeat(self):
s = htmltext('a')

View File

@ -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)

View File

@ -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("<LL", _LOWU32(crc), _LOWU32(n))
@ -284,7 +284,8 @@ class HTTPResponse:
is true then the body may be compressed.
"""
if not isinstance(body, Stream):
body = self._encode_chunk(stringify(body))
if not isinstance(body, bytes):
body = self._encode_chunk(stringify(body))
if compress and self.content_type not in _GZIP_EXCLUDE:
body = self._compress_body(body)
else:
@ -350,10 +351,10 @@ class HTTPResponse:
def redirect(self, location, permanent=False):
"""Cause a redirection without raising an error"""
if not isinstance(location, str):
raise TypeError, "location must be a string (got %s)" % `location`
raise TypeError("location must be a string (got %r)" % location)
# Ensure that location is a full URL
if location.find('://') == -1:
raise ValueError, "URL must include the server name"
raise ValueError("URL must include the server name")
if permanent:
status = 301
else:
@ -419,10 +420,10 @@ class HTTPResponse:
# Date header
now = time.time()
if "date" not in self.headers:
self.headers['date'] = formatdate(now)
self.headers['date'] = formatdate(now, usegmt=True)
# Cache directives
if self.cache is None or self.headers.has_key("expires"):
if self.cache is None or "expires" in self.headers:
pass # don't mess with the expires or cache control header
else:
# We add both an Expires header and a Cache-Control header
@ -430,7 +431,7 @@ class HTTPResponse:
# priority when both Expires and max-age are present (even
# if Expires is more restrictive, RFC 2616 section 14.9.3).
if self.cache > 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:

View File

@ -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

View File

@ -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):

View File

@ -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]))

View File

@ -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

View File

@ -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:

View File

@ -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))

View File

@ -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(),

View File

@ -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('<br><b>POST data (%s):</b><br><pre>' % 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('<P><HR><P><pre>')
keys = req.env.keys()
keys.sort()
keys = sorted(req.env.keys())
for k in keys:
doc.append('<b>%-20s :</b> %s\n' % (k, req.env[k]))
doc.append('\n</pre><P><HR>\n')

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""An HTTP server for Twisted that publishes a Quixote application.
"""

View File

@ -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)

View File

@ -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))

View File

@ -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 = {}

View File

@ -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)

View File

@ -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: