make bottle.py work without running 2to3 on python3

this is a copy of bottle.py from
https://github.com/schmir/bottle/commits/single-source
This commit is contained in:
Ralf Schmitt 2011-11-24 01:53:03 +01:00
parent 3e253b3bbd
commit 2d3f7bfb7a
1 changed files with 81 additions and 46 deletions

View File

@ -23,7 +23,6 @@ import cgi
import email.utils
import functools
import hmac
import httplib
import imp
import itertools
import mimetypes
@ -32,29 +31,41 @@ import re
import subprocess
import sys
import tempfile
import thread
if sys.version_info >= (3,0):
import _thread as thread
else:
import thread
import threading
import time
import warnings
from Cookie import SimpleCookie
if sys.version_info >= (3,0):
from http.cookies import SimpleCookie
else:
from Cookie import SimpleCookie
from tempfile import TemporaryFile
from traceback import format_exc
from urlparse import urlunsplit, urljoin, SplitResult as UrlSplitResult
# Workaround for a bug in some versions of lib2to3 (fixed on CPython 2.7 and 3.2)
import urllib
urlencode = urllib.urlencode
urlquote = urllib.quote
urlunquote = urllib.unquote
if sys.version_info >= (3,0):
from urllib.parse import urlunsplit, urljoin, SplitResult as UrlSplitResult
else:
from urlparse import urlunsplit, urljoin, SplitResult as UrlSplitResult
if sys.version_info >= (3,0):
from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote
else:
from urllib import urlencode, quote as urlquote, unquote as urlunquote
try: from collections import MutableMapping as DictMixin
except ImportError: # pragma: no cover
from UserDict import DictMixin
try: from urlparse import parse_qs
except ImportError: # pragma: no cover
from cgi import parse_qs
if sys.version_info >= (3,0):
from urllib.parse import parse_qs
else:
try: from urlparse import parse_qs
except ImportError: # pragma: no cover
from cgi import parse_qs
try: import cPickle as pickle
except ImportError: # pragma: no cover
@ -68,8 +79,18 @@ except ImportError: # pragma: no cover
except ImportError: # pragma: no cover
json_dumps = None
try:
next
except NameError:
def next(it):
return it.next()
NCTextIOWrapper = None
if sys.version_info >= (3,0,0): # pragma: no cover
imap = map
basestring = str
unicode = str
# See Request.POST
from io import BytesIO
def touni(x, enc='utf8', err='strict'):
@ -82,6 +103,7 @@ if sys.version_info >= (3,0,0): # pragma: no cover
the wrapped buffer. This subclass keeps it open. '''
def close(self): pass
else:
imap = itertools.imap
from StringIO import StringIO as BytesIO
bytes = str
def touni(x, enc='utf8', err='strict'):
@ -287,7 +309,7 @@ class Router(object):
parts = [p.replace('\\:',':') for p in token[::3]]
names = token[1::3]
if len(parts) > len(names): names.append(None)
pairs = zip(parts, names)
pairs = list(zip(parts, names))
self.named[_name] = (rule, pairs)
try:
anon = list(anon)
@ -297,7 +319,8 @@ class Router(object):
except IndexError:
msg = "Not enough arguments to fill out anonymous wildcards."
raise RouteBuildError(msg)
except KeyError, e:
except KeyError:
e = sys.exc_info()[1]
raise RouteBuildError(*e.args)
if args: url += ['?', urlencode(args)]
@ -367,10 +390,12 @@ class Router(object):
combined = '%s|(%s)' % (self.dynamic[-1][0].pattern, fpat)
self.dynamic[-1] = (re.compile(combined), self.dynamic[-1][1])
self.dynamic[-1][1].append((gpat, target))
except (AssertionError, IndexError), e: # AssertionError: Too many groups
except (AssertionError, IndexError): # AssertionError: Too many groups
e = sys.exc_info()[1]
self.dynamic.append((re.compile('(^%s$)'%fpat),
[(gpat, target)]))
except re.error, e:
except re.error:
e = sys.exc_info()[1]
raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, e))
def _compile_pattern(self, rule):
@ -433,7 +458,7 @@ class Bottle(object):
'''
if not isinstance(app, Bottle):
raise TypeError('Only Bottle instances are supported for now.')
prefix = '/'.join(filter(None, prefix.split('/')))
prefix = '/'.join([x for x in prefix.split('/') if x])
if not prefix:
raise TypeError('Empty prefix. Perhaps you want a merge()?')
for other in self.mounts:
@ -654,14 +679,16 @@ class Bottle(object):
try:
callback, args = self._match(environ)
return callback(**args)
except HTTPResponse, r:
except HTTPResponse:
r = sys.exc_info()[1]
return r
except RouteReset: # Route reset requested by the callback or a plugin.
del self.ccache[handle]
return self._handle(environ) # Try again.
except (KeyboardInterrupt, SystemExit, MemoryError):
raise
except Exception, e:
except Exception:
e = sys.exc_info()[1]
if not self.catchall: raise
return HTTPError(500, "Internal Server Error", e, format_exc(10))
@ -708,14 +735,16 @@ class Bottle(object):
# Handle Iterables. We peek into them to detect their inner type.
try:
out = iter(out)
first = out.next()
first = next(out)
while not first:
first = out.next()
first = next(out)
except StopIteration:
return self._cast('', request, response)
except HTTPResponse, e:
except HTTPResponse:
e = sys.exc_info()[1]
first = e
except Exception, e:
except Exception:
e = sys.exc_info()[1]
first = HTTPError(500, 'Unhandled exception', e, format_exc(10))
if isinstance(e, (KeyboardInterrupt, SystemExit, MemoryError))\
or not self.catchall:
@ -726,8 +755,8 @@ class Bottle(object):
if isinstance(first, bytes):
return itertools.chain([first], out)
if isinstance(first, unicode):
return itertools.imap(lambda x: x.encode(response.charset),
itertools.chain([first], out))
return imap(lambda x: x.encode(response.charset),
itertools.chain([first], out))
return self._cast(HTTPError(500, 'Unsupported response type: %s'\
% type(first)), request, response)
@ -748,7 +777,8 @@ class Bottle(object):
return out
except (KeyboardInterrupt, SystemExit, MemoryError):
raise
except Exception, e:
except Exception:
e = sys.exc_info()[1]
if not self.catchall: raise
err = '<h1>Critical error while processing request: %s</h1>' \
% environ.get('PATH_INFO', '/')
@ -818,7 +848,7 @@ class Request(threading.local, DictMixin):
def __delitem__(self, key): self[key] = ""; del(self.environ[key])
def __iter__(self): return iter(self.environ)
def __len__(self): return len(self.environ)
def keys(self): return self.environ.keys()
def keys(self): return list(self.environ.keys())
def __setitem__(self, key, value):
""" Shortcut for Request.environ.__setitem__ """
self.environ[key] = value
@ -888,7 +918,7 @@ class Request(threading.local, DictMixin):
""" The QUERY_STRING parsed into an instance of :class:`MultiDict`. """
data = parse_qs(self.query_string, keep_blank_values=True)
get = self.environ['bottle.get'] = MultiDict()
for key, values in data.iteritems():
for key, values in data.items():
for value in values:
get[key] = value
return get
@ -989,7 +1019,7 @@ class Request(threading.local, DictMixin):
"""
raw_dict = SimpleCookie(self.headers.get('Cookie',''))
cookies = {}
for cookie in raw_dict.itervalues():
for cookie in raw_dict.values():
cookies[cookie.key] = cookie.value
return cookies
@ -1104,7 +1134,7 @@ class Response(threading.local):
raise TypeError('Secret missing for non-string Cookie.')
self.COOKIES[key] = value
for k, v in kargs.iteritems():
for k, v in kargs.items():
self.COOKIES[key][k.replace('_', '-')] = v
def delete_cookie(self, key, **kwargs):
@ -1285,14 +1315,14 @@ class MultiDict(DictMixin):
# collections.MutableMapping would be better for Python >= 2.6
def __init__(self, *a, **k):
self.dict = dict()
for k, v in dict(*a, **k).iteritems():
for k, v in dict(*a, **k).items():
self[k] = v
def __len__(self): return len(self.dict)
def __iter__(self): return iter(self.dict)
def __contains__(self, key): return key in self.dict
def __delitem__(self, key): del self.dict[key]
def keys(self): return self.dict.keys()
def keys(self): return list(self.dict.keys())
def __getitem__(self, key): return self.get(key, KeyError, -1)
def __setitem__(self, key, value): self.append(key, value)
@ -1306,7 +1336,7 @@ class MultiDict(DictMixin):
return self.dict[key][index]
def iterallitems(self):
for key, values in self.dict.iteritems():
for key, values in self.dict.items():
for value in values:
yield key, value
@ -1615,7 +1645,7 @@ def validate(**vkargs):
"""
def decorator(func):
def wrapper(**kargs):
for key, value in vkargs.iteritems():
for key, value in vkargs.items():
if key not in kargs:
abort(403, 'Missing parameter: %s' % key)
try:
@ -1751,8 +1781,8 @@ class FapwsServer(ServerAdapter):
evwsgi.start(self.host, port)
# fapws3 never releases the GIL. Complain upstream. I tried. No luck.
if 'BOTTLE_CHILD' in os.environ and not self.quiet:
print "WARNING: Auto-reloading does not work with Fapws3."
print " (Fapws3 breaks python thread support)"
sys.stdout.write("WARNING: Auto-reloading does not work with Fapws3.\n")
sys.stdout.write(" (Fapws3 breaks python thread support)\n")
evwsgi.set_base_module(base)
def app(environ, start_response):
environ['wsgi.multiprocess'] = False
@ -1976,10 +2006,9 @@ def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
raise RuntimeError("Server must be a subclass of ServerAdapter")
server.quiet = server.quiet or quiet
if not server.quiet and not os.environ.get('BOTTLE_CHILD'):
print "Bottle server starting up (using %s)..." % repr(server)
print "Listening on http://%s:%d/" % (server.host, server.port)
print "Use Ctrl-C to quit."
print
sys.stdout.write("Bottle server starting up (using %s)...\n" % repr(server))
sys.stdout.write("Listening on http://%s:%d/\n" % (server.host, server.port))
sys.stdout.write("Use Ctrl-C to quit.\n\n")
try:
if reloader:
interval = min(interval, 1)
@ -1992,7 +2021,7 @@ def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
except KeyboardInterrupt:
pass
if not server.quiet and not os.environ.get('BOTTLE_CHILD'):
print "Shutting down..."
sys.stdout.write("Shutting down...\n")
class FileCheckerThread(threading.Thread):
@ -2014,7 +2043,7 @@ class FileCheckerThread(threading.Thread):
if path[-4:] in ('.pyo', '.pyc'): path = path[:-1]
if path and exists(path): files[path] = mtime(path)
while not self.status:
for path, lmtime in files.iteritems():
for path, lmtime in files.items():
if not exists(path) or mtime(path) > lmtime:
self.status = 3
if not exists(self.lockfile):
@ -2068,7 +2097,7 @@ def _reloader_observer(server, app, interval):
if os.path.exists(lockfile): os.unlink(lockfile)
sys.exit(p.poll())
elif not server.quiet:
print "Reloading server..."
sys.stdout.write("Reloading server...\n")
except KeyboardInterrupt:
pass
if os.path.exists(lockfile): os.unlink(lockfile)
@ -2108,7 +2137,7 @@ class BaseTemplate(object):
self.name = name
self.source = source.read() if hasattr(source, 'read') else source
self.filename = source.filename if hasattr(source, 'filename') else None
self.lookup = map(os.path.abspath, lookup)
self.lookup = [os.path.abspath(x) for x in lookup]
self.encoding = encoding
self.settings = self.settings.copy() # Copy from class variable
self.settings.update(settings) # Apply
@ -2237,7 +2266,10 @@ class SimpleTALTemplate(BaseTemplate):
def render(self, *args, **kwargs):
from simpletal import simpleTALES
from StringIO import StringIO
if sys.version_info >= (3, 0):
from io import StringIO
else:
from StringIO import StringIO
for dictarg in args: kwargs.update(dictarg)
# TODO: maybe reuse a context instead of always creating one
context = simpleTALES.Context()
@ -2475,7 +2507,10 @@ DEBUG = False
MEMFILE_MAX = 1024*100
#: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found')
HTTP_CODES = httplib.responses
if sys.version_info >= (3,0):
from http.client import responses as HTTP_CODES
else:
from httplib import responses as HTTP_CODES
HTTP_CODES[418] = "I'm a teapot" # RFC 2324
#: The default template used for error pages. Override with @error()