move bottle app function to _app module

I'd like to run multiple pypiserver wsgi apps inside one process and
plan to do so by reloading pypiserver._app.

this is the first step. we move all of the @route'd functions to _app.
This commit is contained in:
Ralf Schmitt 2012-04-07 22:30:54 +02:00
parent ace7c63fa5
commit c0f82470c6
5 changed files with 164 additions and 139 deletions

@ -6,7 +6,7 @@ def app(root=None,
redirect_to_fallback=True,
fallback_url=None):
import os
from pypiserver import core
from pypiserver import core, _app
import bottle
if root is None:
@ -16,9 +16,7 @@ def app(root=None,
fallback_url="http://pypi.python.org/simple"
os.listdir(root)
core.packages = core.pkgset(root)
core.config.redirect_to_fallback = redirect_to_fallback
core.config.fallback_url = fallback_url
_app.configure(root=root, redirect_to_fallback=redirect_to_fallback, fallback_url=fallback_url)
bottle.debug(True)
return bottle.default_app()

143
pypiserver/_app.py Normal file

@ -0,0 +1,143 @@
import sys, os
if sys.version_info >= (3, 0):
from urllib.parse import urljoin
else:
from urlparse import urljoin
from bottle import route, static_file, redirect, request, HTTPError
from pypiserver import __version__
from pypiserver.core import is_allowed_path
packages = None
class configuration(object):
def __init__(self):
self.fallback_url = "http://pypi.python.org/simple"
self.redirect_to_fallback = True
config = configuration()
def configure(root=None,
redirect_to_fallback=True,
fallback_url=None):
from pypiserver.core import pkgset
global packages
if root is None:
root = os.path.expanduser("~/packages")
if fallback_url is None:
fallback_url="http://pypi.python.org/simple"
packages = pkgset(root)
config.redirect_to_fallback = redirect_to_fallback
config.fallback_url = fallback_url
@route("/favicon.ico")
def favicon():
return HTTPError(404)
@route('/')
def root():
try:
numpkgs = len(packages.find_packages())
except:
numpkgs = 0
return """<html><head><title>Welcome to pypiserver!</title></head><body>
<h1>Welcome to pypiserver!</h1>
<p>This is a PyPI compatible package index serving %(NUMPKGS)s packages.</p>
<p> To use this server with pip, run the the following command:
<blockquote><pre>
pip install -i %(URL)ssimple/ PACKAGE [PACKAGE2...]
</pre></blockquote></p>
<p> To use this server with easy_install, run the the following command:
<blockquote><pre>
easy_install -i %(URL)ssimple/ PACKAGE
</pre></blockquote></p>
<p>The complete list of all packages can be found <a href="packages/">here</a> or via the <a href="simple/">simple</a> index.</p>
<p>This instance is running version %(VERSION)s of the <a href="http://pypi.python.org/pypi/pypiserver">pypiserver</a> software.</p>
</body></html>
""" % dict(URL=request.url, VERSION=__version__, NUMPKGS=numpkgs)
@route("/simple")
def simpleindex_redirect():
return redirect(request.fullpath + "/")
@route("/simple/")
def simpleindex():
prefixes = list(packages.find_prefixes())
prefixes.sort()
res = ["<html><head><title>Simple Index</title></head><body>\n"]
for x in prefixes:
res.append('<a href="%s/">%s</a><br>\n' % (x, x))
res.append("</body></html>")
return "".join(res)
@route("/simple/:prefix")
@route("/simple/:prefix/")
def simple(prefix=""):
fp = request.fullpath
if not fp.endswith("/"):
fp += "/"
files = packages.find_packages(prefix)
if not files:
if config.redirect_to_fallback:
return redirect("%s/%s/" % (config.fallback_url.rstrip("/"), prefix))
return HTTPError(404)
files.sort()
res = ["<html><head><title>Links for %s</title></head><body>\n" % prefix]
res.append("<h1>Links for %s</h1>\n" % prefix)
for x in files:
abspath = urljoin(fp, "../../packages/%s" % x)
res.append('<a href="%s">%s</a><br>\n' % (abspath, os.path.basename(x)))
res.append("</body></html>\n")
return "".join(res)
@route('/packages')
@route('/packages/')
def list_packages():
fp = request.fullpath
if not fp.endswith("/"):
fp += "/"
files = packages.find_packages()
files.sort()
res = ["<html><head><title>Index of packages</title></head><body>\n"]
for x in files:
res.append('<a href="%s">%s</a><br>\n' % (urljoin(fp, x), x))
res.append("</body></html>\n")
return "".join(res)
@route('/packages/:filename#.*#')
def server_static(filename):
if not is_allowed_path(filename):
return HTTPError(404)
return static_file(filename, root=packages.root)
@route('/:prefix')
@route('/:prefix/')
def bad_url(prefix):
p = request.fullpath
if not p.endswith("/"):
p += "/"
p += "../simple/%s/" % prefix
return redirect(p)
# return redirect("../simple/%s/" % prefix)

@ -2,27 +2,13 @@
"""minimal PyPI like server for use with pip/easy_install"""
import os, sys, getopt, re, mimetypes
if sys.version_info >= (3, 0):
from urllib.parse import urljoin
else:
from urlparse import urljoin
from pypiserver import bottle, __version__
sys.modules["bottle"] = bottle
from bottle import run, debug, server_names
from bottle import route, run, static_file, redirect, request, debug, server_names, HTTPError
mimetypes.add_type("application/octet-stream", ".egg")
packages = None
class configuration(object):
def __init__(self):
self.fallback_url = "http://pypi.python.org/simple"
self.redirect_to_fallback = True
config = configuration()
def guess_pkgname(path):
pkgname = re.split(r"-\d+", os.path.basename(path))[0]
@ -80,113 +66,6 @@ class pkgset(object):
return prefixes
@route("/favicon.ico")
def favicon():
return HTTPError(404)
@route('/')
def root():
try:
numpkgs = len(packages.find_packages())
except:
numpkgs = 0
return """<html><head><title>Welcome to pypiserver!</title></head><body>
<h1>Welcome to pypiserver!</h1>
<p>This is a PyPI compatible package index serving %(NUMPKGS)s packages.</p>
<p> To use this server with pip, run the the following command:
<blockquote><pre>
pip install -i %(URL)ssimple/ PACKAGE [PACKAGE2...]
</pre></blockquote></p>
<p> To use this server with easy_install, run the the following command:
<blockquote><pre>
easy_install -i %(URL)ssimple/ PACKAGE
</pre></blockquote></p>
<p>The complete list of all packages can be found <a href="packages/">here</a> or via the <a href="simple/">simple</a> index.</p>
<p>This instance is running version %(VERSION)s of the <a href="http://pypi.python.org/pypi/pypiserver">pypiserver</a> software.</p>
</body></html>
""" % dict(URL=request.url, VERSION=__version__, NUMPKGS=numpkgs)
@route("/simple")
def simpleindex_redirect():
return redirect(request.fullpath + "/")
@route("/simple/")
def simpleindex():
prefixes = list(packages.find_prefixes())
prefixes.sort()
res = ["<html><head><title>Simple Index</title></head><body>\n"]
for x in prefixes:
res.append('<a href="%s/">%s</a><br>\n' % (x, x))
res.append("</body></html>")
return "".join(res)
@route("/simple/:prefix")
@route("/simple/:prefix/")
def simple(prefix=""):
fp = request.fullpath
if not fp.endswith("/"):
fp += "/"
files = packages.find_packages(prefix)
if not files:
if config.redirect_to_fallback:
return redirect("%s/%s/" % (config.fallback_url.rstrip("/"), prefix))
return HTTPError(404)
files.sort()
res = ["<html><head><title>Links for %s</title></head><body>\n" % prefix]
res.append("<h1>Links for %s</h1>\n" % prefix)
for x in files:
abspath = urljoin(fp, "../../packages/%s" % x)
res.append('<a href="%s">%s</a><br>\n' % (abspath, os.path.basename(x)))
res.append("</body></html>\n")
return "".join(res)
@route('/packages')
@route('/packages/')
def list_packages():
fp = request.fullpath
if not fp.endswith("/"):
fp += "/"
files = packages.find_packages()
files.sort()
res = ["<html><head><title>Index of packages</title></head><body>\n"]
for x in files:
res.append('<a href="%s">%s</a><br>\n' % (urljoin(fp, x), x))
res.append("</body></html>\n")
return "".join(res)
@route('/packages/:filename#.*#')
def server_static(filename):
if not is_allowed_path(filename):
return HTTPError(404)
return static_file(filename, root=packages.root)
@route('/:prefix')
@route('/:prefix/')
def bad_url(prefix):
p = request.fullpath
if not p.endswith("/"):
p += "/"
p += "../simple/%s/" % prefix
return redirect(p)
# return redirect("../simple/%s/" % prefix)
def usage():
@ -257,6 +136,8 @@ def main(argv=None):
host = "0.0.0.0"
port = 8080
server = None
redirect_to_fallback = True
update_dry_run = True
update_directory = None
update_stable_only = True
@ -275,7 +156,7 @@ def main(argv=None):
elif k in ("-r", "--root"):
roots.append(v)
elif k == "--disable-fallback":
config.redirect_to_fallback = False
redirect_to_fallback = False
elif k == "--server":
if v not in server_names:
sys.exit("unknown server %r. choose one of %s" % (v, ", ".join(server_names.keys())))
@ -308,13 +189,15 @@ def main(argv=None):
err = sys.exc_info()[1]
sys.exit("Error: while trying to list %r: %s" % (root, err))
packages = pkgset(root)
if command == "update":
packages = pkgset(root)
from pypiserver import manage
manage.update(packages, update_directory, update_dry_run, stable_only=update_stable_only)
return
from pypiserver import _app
_app.configure(root=root, redirect_to_fallback=redirect_to_fallback)
server = server or "auto"
debug(True)
sys.stdout.write("This is pypiserver %s serving %r on %s:%s\n\n" % (__version__, root, host, port))

@ -9,7 +9,7 @@ try:
except ImportError:
loadapp = None
from pypiserver import core
from pypiserver import core, _app
import bottle
bottle.debug(True)
@ -25,9 +25,9 @@ def pytest_funcarg__root(request):
tmpdir = request.getfuncargvalue("tmpdir")
monkeypatch = request.getfuncargvalue("monkeypatch")
monkeypatch.setattr(core, "packages", core.pkgset(tmpdir.strpath))
monkeypatch.setattr(core, "config", core.configuration())
from pypiserver import _app
monkeypatch.setattr(_app, "packages", core.pkgset(tmpdir.strpath))
monkeypatch.setattr(_app, "config", _app.configuration())
if loadapp:
pini = tmpdir.join(".paste.ini")
@ -101,13 +101,13 @@ def test_favicon(root):
def test_fallback(root):
assert core.config.redirect_to_fallback
assert _app.config.redirect_to_fallback
final_url = go("/simple/pypiserver/")
assert final_url == "http://pypi.python.org/simple/pypiserver/"
def test_no_fallback(root):
core.config.redirect_to_fallback = False
_app.config.redirect_to_fallback = False
final_url = go("/simple/pypiserver/")
assert final_url == "http://localhost:8080/simple/pypiserver/"
code(404)
@ -116,6 +116,7 @@ def test_no_fallback(root):
def test_serve_no_dotfiles(root):
root.join(".foo-1.0.zip").write("secret")
go("/packages/.foo-1.0.zip")
show()
code(404)

@ -2,7 +2,7 @@
import os
import pytest
from pypiserver import core
from pypiserver import core, _app
class main_wrapper(object):
@ -32,8 +32,8 @@ def pytest_funcarg__main(request):
monkeypatch = request.getfuncargvalue("monkeypatch")
monkeypatch.setattr(core, "run", run)
monkeypatch.setattr(os, "listdir", listdir)
monkeypatch.setattr(core, "packages", None)
monkeypatch.setattr(core, "config", core.configuration())
monkeypatch.setattr(_app, "packages", None)
monkeypatch.setattr(_app, "config", _app.configuration())
return main
@ -61,13 +61,13 @@ def test_server(main):
def test_root(main):
main(["--root", "."])
assert core.packages.root == os.path.abspath(".")
assert _app.packages.root == os.path.abspath(".")
assert main.pkgdir == os.path.abspath(".")
def test_root_r(main):
main(["-r", "."])
assert core.packages.root == os.path.abspath(".")
assert _app.packages.root == os.path.abspath(".")
assert main.pkgdir == os.path.abspath(".")