Allow to override welcome-msg(/) from a separate html file.

- Read welcome-msg in UTF-8.
- Add cmd-line option for `welcome-file`.
- Add TCs for welcome-file option and `/` http-req.
- Update docs for new option.
- Failback to in-code welcome-msg if unreadable (ie standalone mode, bad file).
This commit is contained in:
ankostis@kilo 2014-11-14 00:36:32 +01:00 committed by Kostis Anagnostopoulos @ STUW025
parent d0946ebbcf
commit c64b8c32d2
10 changed files with 110 additions and 24 deletions

@ -9,6 +9,7 @@ include pypi-server-in.py
include pypiserver/__init__.py include pypiserver/__init__.py
include pypiserver/__main__.py include pypiserver/__main__.py
include pypiserver/_app.py include pypiserver/_app.py
include pypiserver/welcome.html
include pypiserver/bottle.py include pypiserver/bottle.py
include pypiserver/core.py include pypiserver/core.py
include pypiserver/manage.py include pypiserver/manage.py

@ -112,6 +112,9 @@ pypi-server -h will print a detailed usage message::
-o, --overwrite -o, --overwrite
allow overwriting existing package files allow overwriting existing package files
--welcome HTML_FILE
uses the ASCII contents of HTML_FILE as welcome message response.
-v -v
enable INFO logging; repeate for more verbosity. enable INFO logging; repeate for more verbosity.

@ -9,7 +9,8 @@ def app(root=None,
overwrite=False, overwrite=False,
log_req_frmt="%(bottle.request)s", log_req_frmt="%(bottle.request)s",
log_res_frmt="%(status)s", log_res_frmt="%(status)s",
log_err_frmt="%(body)s: %(exception)s \n%(traceback)s"): log_err_frmt="%(body)s: %(exception)s \n%(traceback)s",
welcome_file=None):
import sys, os import sys, os
from pypiserver import core from pypiserver import core
sys.modules.pop("pypiserver._app", None) sys.modules.pop("pypiserver._app", None)
@ -26,7 +27,8 @@ def app(root=None,
_app.configure(root=root, redirect_to_fallback=redirect_to_fallback, fallback_url=fallback_url, _app.configure(root=root, redirect_to_fallback=redirect_to_fallback, fallback_url=fallback_url,
password_file=password_file, overwrite=overwrite, password_file=password_file, overwrite=overwrite,
log_req_frmt=log_req_frmt, log_res_frmt=log_res_frmt, log_err_frmt=log_err_frmt) log_req_frmt=log_req_frmt, log_res_frmt=log_res_frmt, log_err_frmt=log_err_frmt,
welcome_file=welcome_file)
_app.app.module = _app _app.app.module = _app
bottle.debug(True) bottle.debug(True)

@ -1,4 +1,4 @@
import sys, os, itertools, zipfile, mimetypes, logging import sys, os, io, itertools, zipfile, mimetypes, logging, pkg_resources
try: try:
from io import BytesIO from io import BytesIO
@ -23,6 +23,8 @@ class configuration(object):
self.fallback_url = "http://pypi.python.org/simple" self.fallback_url = "http://pypi.python.org/simple"
self.redirect_to_fallback = True self.redirect_to_fallback = True
self.htpasswdfile = None self.htpasswdfile = None
self.welcome_file = None
self.welcome_msg = None
config = configuration() config = configuration()
@ -40,7 +42,8 @@ def configure(root=None,
overwrite=False, overwrite=False,
log_req_frmt=None, log_req_frmt=None,
log_res_frmt=None, log_res_frmt=None,
log_err_frmt=None): log_err_frmt=None,
welcome_file=None):
global packages global packages
log.info("Starting(%s)", dict(root=root, log.info("Starting(%s)", dict(root=root,
@ -48,6 +51,7 @@ def configure(root=None,
fallback_url=fallback_url, fallback_url=fallback_url,
password_file=password_file, password_file=password_file,
overwrite=overwrite, overwrite=overwrite,
welcome_file=welcome_file,
log_req_frmt=log_req_frmt, log_req_frmt=log_req_frmt,
log_res_frmt=log_res_frmt, log_res_frmt=log_res_frmt,
log_err_frmt=log_err_frmt)) log_err_frmt=log_err_frmt))
@ -81,6 +85,40 @@ def configure(root=None,
config.htpasswdfile = HtpasswdFile(password_file) config.htpasswdfile = HtpasswdFile(password_file)
config.overwrite = overwrite config.overwrite = overwrite
## Read welcome-msg from external file,
# or failback to the embedded-msg (ie. in standalone mode).
#
try:
if not welcome_file:
welcome_file = pkg_resources.resource_filename(__name__, "welcome.html") # @UndefinedVariable
config.welcome_file = welcome_file
with io.open(config.welcome_file, 'r', encoding='utf-8') as fd:
config.welcome_msg = fd.read()
except Exception:
log.warning("Could not load welcome-file(%s)!", welcome_file, exc_info=1)
if not config.welcome_msg:
from textwrap import dedent
config.welcome_msg = dedent("""\
<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)s">here</a> or via the <a href="%(SIMPLE)s">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>\
""")
config.log_req_frmt = log_req_frmt config.log_req_frmt = log_req_frmt
config.log_res_frmt = log_res_frmt config.log_res_frmt = log_res_frmt
config.log_err_frmt = log_err_frmt config.log_err_frmt = log_err_frmt
@ -122,27 +160,13 @@ def root():
except: except:
numpkgs = 0 numpkgs = 0
return """<html><head><title>Welcome to pypiserver!</title></head><body> return config.welcome_msg % dict(
<h1>Welcome to pypiserver!</h1> URL=request.url,
<p>This is a PyPI compatible package index serving %(NUMPKGS)s packages.</p> VERSION=__version__,
NUMPKGS=numpkgs,
<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)s">here</a> or via the <a href="%(SIMPLE)s">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,
PACKAGES=urljoin(fp, "packages/"), PACKAGES=urljoin(fp, "packages/"),
SIMPLE=urljoin(fp, "simple/")) SIMPLE=urljoin(fp, "simple/")
)
@app.post('/') @app.post('/')

@ -213,6 +213,9 @@ pypi-server understands the following options:
-o, --overwrite -o, --overwrite
allow overwriting existing package files allow overwriting existing package files
--welcome HTML_FILE
uses the ASCII contents of HTML_FILE as welcome message response.
-v -v
enable verbose logging; repeate for more verbosity. enable verbose logging; repeate for more verbosity.
@ -283,6 +286,7 @@ def main(argv=None):
log_req_frmt = None log_req_frmt = None
log_res_frmt = None log_res_frmt = None
log_err_frmt = None log_err_frmt = None
welcome_file = None
update_dry_run = True update_dry_run = True
update_directory = None update_directory = None
@ -303,6 +307,7 @@ def main(argv=None):
"log-req-frmt=", "log-req-frmt=",
"log-res-frmt=", "log-res-frmt=",
"log-err-frmt=", "log-err-frmt=",
"welcome=",
"version", "version",
"help" "help"
]) ])
@ -326,6 +331,8 @@ def main(argv=None):
sys.exit("unknown server %r. choose one of %s" % ( sys.exit("unknown server %r. choose one of %s" % (
v, ", ".join(server_names.keys()))) v, ", ".join(server_names.keys())))
server = v server = v
elif k == "--welcome":
welcome_file = v
elif k == "--version": elif k == "--version":
sys.stdout.write("pypiserver %s\n" % __version__) sys.stdout.write("pypiserver %s\n" % __version__)
sys.exit(0) sys.exit(0)
@ -379,6 +386,7 @@ def main(argv=None):
fallback_url=fallback_url, fallback_url=fallback_url,
overwrite=overwrite, overwrite=overwrite,
log_req_frmt=log_req_frmt, log_res_frmt=log_res_frmt, log_err_frmt=log_err_frmt, log_req_frmt=log_req_frmt, log_res_frmt=log_res_frmt, log_err_frmt=log_err_frmt,
welcome_file=welcome_file,
) )
server = server or "auto" server = server or "auto"
sys.stdout.write("This is pypiserver %s serving %r on http://%s:%s\n\n" % (__version__, ", ".join(roots), host, port)) sys.stdout.write("This is pypiserver %s serving %r on http://%s:%s\n\n" % (__version__, ", ".join(roots), host, port))

18
pypiserver/welcome.html Normal file

@ -0,0 +1,18 @@
<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)s">here</a> or via the <a href="%(SIMPLE)s">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>

@ -32,6 +32,7 @@ setup(name="pypiserver",
long_description=open("README.rst").read(), long_description=open("README.rst").read(),
version=get_version(), version=get_version(),
packages=["pypiserver"], packages=["pypiserver"],
package_data={'pypiserver': ['welcome.html']},
url="https://github.com/schmir/pypiserver", url="https://github.com/schmir/pypiserver",
maintainer="Ralf Schmitt", maintainer="Ralf Schmitt",
maintainer_email="ralf@systemexit.de", maintainer_email="ralf@systemexit.de",
@ -53,4 +54,5 @@ setup(name="pypiserver",
"Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.3",
"Topic :: Software Development :: Build Tools", "Topic :: Software Development :: Build Tools",
"Topic :: System :: Software Distribution"], "Topic :: System :: Software Distribution"],
zip_safe=False,
**extra) **extra)

7
tests/sample_msg.html Normal file

@ -0,0 +1,7 @@
Hello pypiserver tester!
%(URL)s
%(VERSION)s
%(NUMPKGS)s
%(PACKAGES)s
%(SIMPLE)s

@ -55,6 +55,18 @@ def test_root_hostname(testapp):
# go("http://systemexit.de/") # go("http://systemexit.de/")
def test_root_welcome_msg(root):
wmsg = "Hey there!"
wfile = root.join("testwelcome.html")
wfile.write(wmsg)
from pypiserver import app
app = app(root=root.strpath, welcome_file=wfile.strpath)
testapp = webtest.TestApp(app)
resp = testapp.get("/")
resp.mustcontain(wmsg)
def test_packages_empty(testapp): def test_packages_empty(testapp):
resp = testapp.get("/packages") resp = testapp.get("/packages")
assert len(resp.html("a")) == 0 assert len(resp.html("a")) == 0

@ -102,3 +102,12 @@ def test_logging_verbosity(main):
assert logging.getLogger().level == logging.DEBUG assert logging.getLogger().level == logging.DEBUG
main(["-v", "-v", "-v"]) main(["-v", "-v", "-v"])
assert logging.getLogger().level == logging.NOTSET assert logging.getLogger().level == logging.NOTSET
def test_welcome_file(main):
sample_msg_file = os.path.join(os.path.dirname(__file__), "sample_msg.html")
main(["--welcome", sample_msg_file])
assert "Hello pypiserver tester!" in main.app.module.config.welcome_msg
def test_welcome_file_default(main):
main([])
assert "Welcome to pypiserver!" in main.app.module.config.welcome_msg