merge with the single-source branch

This commit is contained in:
Ralf Schmitt 2011-12-05 22:14:23 +01:00
commit 9b14fbfb08
6 changed files with 109 additions and 63 deletions

20
pypi-server-in.py Normal file → Executable file
View File

@ -4,13 +4,11 @@
sources = """
@SOURCES@"""
import sys, base64, zlib, cPickle
sources = cPickle.loads(zlib.decompress(base64.decodestring(sources)))
import sys, base64, zlib
class DictImporter(object):
sources = sources
sources = None
def find_module(self, fullname, path=None):
if fullname in self.sources:
@ -34,7 +32,7 @@ class DictImporter(object):
if is_pkg:
module.__path__ = [fullname]
exec co in module.__dict__
do_exec(co, module.__dict__)
return sys.modules[fullname]
def get_source(self, name):
@ -44,7 +42,19 @@ class DictImporter(object):
return res
if sys.version_info >= (3, 0):
import pickle
exec("def do_exec(co, loc): exec(co, loc)\n")
sources = sources.encode("ascii") # ensure bytes
d = zlib.decompress(base64.decodebytes(sources))
sources = pickle.loads(zlib.decompress(base64.decodebytes(sources)), encoding="utf-8")
else:
import cPickle as pickle
exec("def do_exec(co, loc): exec co in loc\n")
sources = pickle.loads(zlib.decompress(base64.decodestring(sources)))
importer = DictImporter()
importer.sources = sources
sys.meta_path.append(importer)
if __name__ == "__main__":

View File

@ -1,2 +1,2 @@
__version_info__ = (0, 4, 1)
version = __version__ = "0.4.1"
__version_info__ = (0, 5, 0)
version = __version__ = "0.5.0"

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

View File

@ -240,7 +240,8 @@ def main(argv=None):
try:
opts, roots = getopt.getopt(argv[1:], "i:p:r:d:Uuxh", ["interface=", "port=", "root=", "server=", "disable-fallback", "version", "help"])
except getopt.GetoptError, err:
except getopt.GetoptError:
err = sys.exc_info()[1]
sys.exit("usage error: %s" % (err,))
for k, v in opts:
@ -280,7 +281,8 @@ def main(argv=None):
try:
os.listdir(root)
except Exception, err:
except Exception:
err = sys.exc_info()[1]
sys.exit("Error: while trying to list %r: %s" % (root, err))
packages = pkgset(root)

View File

@ -1,7 +1,11 @@
import sys, os, re, xmlrpclib
import sys, os, re
from pypiserver import core
if sys.version_info >= (3,0):
from xmlrpc.client import Server
else:
from xmlrpclib import Server
# --- the following two functions were copied from distribute's pkg_resources module
component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
@ -65,7 +69,7 @@ def find_updates(pkgset, stable_only=True):
sys.stdout.write(s)
sys.stdout.flush()
pypi = xmlrpclib.Server("http://pypi.python.org/pypi/")
pypi = Server("http://pypi.python.org/pypi/")
pkgname2latest = {}
pkgfiles = [pkgfile(x) for x in pkgset.find_packages()]

View File

@ -1,14 +1,10 @@
#! /usr/bin/env python
import sys, os
extrakw = {}
try:
from setuptools import setup
extrakw["use_2to3"] = True
except ImportError:
if sys.version_info >= (3, 0):
raise
from distutils.core import setup
@ -57,5 +53,4 @@ setup(name="pypiserver",
"Programming Language :: Python :: 3.1",
"Programming Language :: Python :: 3.2",
"Topic :: Software Development :: Build Tools",
"Topic :: System :: Software Distribution"],
**extrakw)
"Topic :: System :: Software Distribution"])