hashes, #53: Add `--hash-algo` option with TCs.

This commit is contained in:
ankostis on tokoti 2015-12-21 00:50:56 +02:00 committed by Kostis Anagnostopoulos
parent 24eddb292a
commit 912d405a83
6 changed files with 85 additions and 70 deletions

View File

@ -270,6 +270,10 @@ Running ``pypi-server -h`` will print a detailed usage message::
-o, --overwrite
allow overwriting existing package files
--hash-algo ALGO
any `hashlib` available algo used as fragments on package links.
Set one of (0, no, off, false) to disabled it. (default: md5)
--welcome HTML_FILE
uses the ASCII contents of HTML_FILE as welcome message response.

View File

@ -43,6 +43,7 @@ def default_config():
authenticated = ['update'],
password_file = None,
overwrite = False,
hash_algo = 'md5',
verbosity = 1,
log_file = None,
log_frmt = "%(asctime)s|%(levelname)s|%(thread)d|%(message)s",
@ -70,15 +71,15 @@ def app(**kwds):
return _app.app
def str2bool(s, default):
if s is not None and s != '':
return s.lower() not in ("no", "off", "0", "false")
return default
def paste_app_factory(global_config, **local_conf):
import os
def upd_bool_attr_from_dict_str_item(conf, attr, sdict):
def str2bool(s, default):
if s is not None and s != '':
return s.lower() not in ("no", "off", "0", "false")
return default
setattr(conf, attr, str2bool(sdict.pop(attr, None), getattr(conf, attr)))
def _make_root(root):

View File

@ -83,6 +83,10 @@ def usage():
-o, --overwrite
allow overwriting existing package files
--hash-algo ALGO
any `hashlib` available algo used as fragments on package links.
Set one of (0, no, off, false) to disabled it. (default: md5)
--welcome HTML_FILE
uses the ASCII contents of HTML_FILE as welcome message response.
@ -167,6 +171,7 @@ def main(argv=None):
"fallback-url=",
"disable-fallback",
"overwrite",
"hash-algo=",
"log-file=",
"log-frmt=",
"log-req-frmt=",
@ -226,6 +231,8 @@ def main(argv=None):
c.password_file = v
elif k in ("-o", "--overwrite"):
c.overwrite = True
elif k in ("--hash-algo"):
c.hash_algo = None if not pypiserver.str2bool(v, c.hash_algo) else v
elif k == "--log-file":
c.log_file = v
elif k == "--log-frmt":

View File

@ -192,7 +192,9 @@ def simple(prefix=""):
return HTTPError(404)
links = [(os.path.basename(f.relfn),
urljoin(fp, "../../packages/%s#%s" % (f.relfn_unix(), f.hash())))
urljoin(fp, "../../packages/%s#%s" % (f.relfn_unix(),
f.hash(config.hash_algo))))
for f in files]
tmpl = """\
<html>
@ -222,7 +224,8 @@ def list_packages():
key=lambda x: (os.path.dirname(x.relfn),
x.pkgname,
x.parsed_version))
links = [(f.relfn_unix(), '%s#%s' % (urljoin(fp, f.relfn), f.hash()))
links = [(f.relfn_unix(), '%s#%s' % (urljoin(fp, f.relfn),
f.hash(config.hash_algo)))
for f in files]
tmpl = """\
<html>

View File

@ -22,6 +22,7 @@ def configure(root=None,
authenticated=None,
password_file=None,
overwrite=False,
hash_algo='md5',
log_file=None,
log_frmt=None,
log_req_frmt=None,
@ -73,29 +74,15 @@ def configure(root=None,
:return: a 2-tuple (Configure, package-list)
"""
log.info("+++Pypiserver invoked with: %s", Configuration(
root=root,
redirect_to_fallback=redirect_to_fallback,
fallback_url=fallback_url,
authenticated=authenticated,
password_file=password_file,
overwrite=overwrite,
welcome_file=welcome_file,
log_file=log_file,
log_frmt=log_frmt,
log_req_frmt=log_req_frmt,
log_res_frmt=log_res_frmt,
log_err_frmt=log_err_frmt,
cache_control=cache_control,
auther=auther,
host=host, port=port, server=server,
verbosity=verbosity, VERSION=VERSION
))
return _configure(**locals())
def _configure(**kwds):
c = Configuration(**kwds)
log.info("+++Pypiserver invoked with: %s", c)
if root is None:
root = os.path.expanduser("~/packages")
roots = root if isinstance(root, (list, tuple)) else [root]
if c.root is None:
c. root = os.path.expanduser("~/packages")
roots = c.root if isinstance(c.root, (list, tuple)) else [c.root]
roots = [os.path.abspath(r) for r in roots]
for r in roots:
try:
@ -107,56 +94,46 @@ def configure(root=None,
packages = lambda: itertools.chain(*[listdir(r) for r in roots])
packages.root = roots[0]
authenticated = authenticated or []
if not callable(auther):
if password_file and password_file != '.':
if not c.authenticated:
c.authenticated = []
if not callable(c.auther):
if c.password_file and c.password_file != '.':
from passlib.apache import HtpasswdFile
htPsswdFile = HtpasswdFile(password_file)
htPsswdFile = HtpasswdFile(c.password_file)
else:
password_file = htPsswdFile = None
auther = functools.partial(auth_by_htpasswd_file, htPsswdFile)
c.password_file = htPsswdFile = None
c.auther = functools.partial(auth_by_htpasswd_file, htPsswdFile)
# Read welcome-msg from external file,
# or failback to the embedded-msg (ie. in standalone mode).
#
try:
if not welcome_file:
welcome_file = "welcome.html"
welcome_msg = pkg_resources.resource_string( # @UndefinedVariable
if not c.welcome_file:
c.welcome_file = "welcome.html"
c.welcome_msg = pkg_resources.resource_string( # @UndefinedVariable
__name__, "welcome.html").decode("utf-8") # @UndefinedVariable
else:
welcome_file = welcome_file
with io.open(welcome_file, 'r', encoding='utf-8') as fd:
welcome_msg = fd.read()
with io.open(c.welcome_file, 'r', encoding='utf-8') as fd:
c.welcome_msg = fd.read()
except Exception:
log.warning(
"Could not load welcome-file(%s)!", welcome_file, exc_info=1)
"Could not load welcome-file(%s)!", c.welcome_file, exc_info=1)
if fallback_url is None:
fallback_url = "http://pypi.python.org/simple"
if c.fallback_url is None:
c.fallback_url = "http://pypi.python.org/simple"
log_req_frmt = log_req_frmt
log_res_frmt = log_res_frmt
log_err_frmt = log_err_frmt
if c.hash_algo:
try:
halgos = hashlib.algorithms_available
except AttributeError:
halgos = ['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']
config = Configuration(
root=root,
redirect_to_fallback=redirect_to_fallback,
fallback_url=fallback_url,
authenticated=authenticated,
password_file=password_file,
overwrite=overwrite,
welcome_file=welcome_file,
welcome_msg=welcome_msg,
log_req_frmt=log_req_frmt,
log_res_frmt=log_res_frmt,
log_err_frmt=log_err_frmt,
cache_control=cache_control,
auther=auther
)
log.info("+++Pypiserver started with: %s", config)
if c.hash_algo not in halgos:
sys.exit('Hash-algorithm %s not one of: %s' % (c.hash_algo, halgos))
return config, packages
log.info("+++Pypiserver started with: %s", c)
return c, packages
def auth_by_htpasswd_file(htPsswdFile, username, password):
@ -268,7 +245,7 @@ class PkgFile(object):
def relfn_unix(self):
return self.relfn.replace("\\", "/")
def hash(self, hash_algo='md5'):
def hash(self, hash_algo):
return '%s=%.32s' % (hash_algo, digest_file(self.fn, hash_algo))

View File

@ -89,11 +89,34 @@ def test_fallback_url_default(main):
assert main.app.module.config.fallback_url == \
"http://pypi.python.org/simple"
@pytest.fixture
def logfile(tmpdir):
return tmpdir.mkdir("logs").join('test.log')
def test_logging(main, logfile):
def test_hash_algo_default(main):
main([])
assert main.app.module.config.hash_algo == 'md5'
def test_hash_algo(main):
main(['--hash-algo=sha256'])
assert main.app.module.config.hash_algo == 'sha256'
def test_hash_algo_off(main):
main(['--hash-algo=off'])
assert main.app.module.config.hash_algo is None
main(['--hash-algo=0'])
assert main.app.module.config.hash_algo is None
main(['--hash-algo=no'])
assert main.app.module.config.hash_algo is None
main(['--hash-algo=false'])
assert main.app.module.config.hash_algo is None
def test_hash_algo_BAD(main):
with pytest.raises(SystemExit) as excinfo:
main(['--hash-algo BAD'])
#assert excinfo.value.message == 'some info' main(['--hash-algo BAD'])
print(excinfo)
def test_logging(main, tmpdir):
logfile = tmpdir.mkdir("logs").join('test.log')
main(["-v", "--log-file", logfile.strpath])
assert logfile.check(), logfile