1
0
mirror of https://github.com/pypiserver/pypiserver synced 2024-11-09 16:45:51 +01:00

init: Continue de-duplication of config-options.

+ Move default-opts and class into `pypiserver` pkg, to be used by
python-clients.
+ main() reuses default-opts class. 
+ Also apply default options to paste-factory (UNTESTED).
+ Setup loggers with `__file__`.
+ Document and relate main-options & config-params.
This commit is contained in:
ankostis on tokoti 2015-12-21 00:18:27 +02:00
parent 2fe615f22e
commit 24eddb292a
3 changed files with 145 additions and 98 deletions

@ -1,4 +1,5 @@
import re as _re
version = __version__ = "1.1.9-dev.3"
__version_info__ = tuple(_re.split('[.-]', __version__))
__updated__ = "2015-12-20"
@ -8,7 +9,57 @@ __summary__ = "A minimal PyPI server for use with pip/easy_install."
__uri__ = "https://github.com/pypiserver/pypiserver"
class Configuration(object):
"""
.. see:: config-options: :func:`pypiserver.configure()`
"""
def __init__(self, **kwds):
vars(self).update(kwds)
def __repr__(self, *args, **kwargs):
return 'Configuration(**%s)' % vars(self)
def __str__(self, *args, **kwargs):
return 'Configuration:\n%s' % '\n'.join('%20s = %s' % (k, v)
for k, v in sorted(vars(self).items()))
def update(self, props):
d = props if isinstance(props, dict) else vars(props)
vars(self).update(props)
DEFAULT_SERVER = "auto"
def default_config():
c = Configuration(
VERSION=version,
root=None,
host = "0.0.0.0",
port = 8080,
server = DEFAULT_SERVER,
redirect_to_fallback = True,
fallback_url = None,
authenticated = ['update'],
password_file = None,
overwrite = False,
verbosity = 1,
log_file = None,
log_frmt = "%(asctime)s|%(levelname)s|%(thread)d|%(message)s",
log_req_frmt = "%(bottle.request)s",
log_res_frmt = "%(status)s",
log_err_frmt = "%(body)s: %(exception)s \n%(traceback)s",
welcome_file = None,
cache_control = None,
)
return c
def app(**kwds):
"""
:param dict kwds:
May use ``**vars(default_config())`.
"""
from . import core, _app, bottle
bottle.debug(True)
@ -23,26 +74,26 @@ def app(**kwds):
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):
root = root.strip()
if root.startswith("~"):
return os.path.expanduser(root)
return root
c = default_config()
root = local_conf.get("root")
if root:
roots = [_make_root(x) for x in root.split("\n") if x.strip()]
else:
roots = None
c.root = [_make_root(x) for x in root.split("\n") if x.strip()]
def str2bool(s, default):
if s is not None and s != '':
return s.lower() not in ("no", "off", "0", "false")
return default
upd_bool_attr_from_dict_str_item(c, 'redirect_to_fallback', local_conf)
upd_bool_attr_from_dict_str_item(c, 'overwrite', local_conf)
redirect_to_fallback = str2bool(
local_conf.pop('redirect_to_fallback', None), True)
overwrite = str2bool(local_conf.get('overwrite', None), False)
return app(root=roots,
redirect_to_fallback=redirect_to_fallback, overwrite=overwrite,
**local_conf)
return app(**vars(c))

@ -14,8 +14,6 @@ import logging
import warnings
import textwrap
DEFAULT_SERVER = "auto"
warnings.filterwarnings("ignore", "Python 2.5 support may be dropped in future versions of Bottle")
log = logging.getLogger('pypiserver.main')
@ -145,32 +143,18 @@ def usage():
def main(argv=None):
import pypiserver
if argv is None:
argv = sys.argv
global packages
command = "serve"
host = "0.0.0.0"
port = 8080
server = DEFAULT_SERVER
redirect_to_fallback = True
fallback_url = None
authed_ops_list = ['update']
password_file = None
overwrite = False
verbosity = 1
log_file = None
log_frmt = "%(asctime)s|%(levelname)s|%(thread)d|%(message)s"
log_req_frmt = "%(bottle.request)s"
log_res_frmt = "%(status)s"
log_err_frmt = "%(body)s: %(exception)s \n%(traceback)s"
welcome_file = None
cache_control = None
update_dry_run = True
update_directory = None
update_stable_only = True
c = pypiserver.default_config()
update_dry_run = True,
update_directory = None,
update_stable_only = True,
try:
opts, roots = getopt.getopt(argv[1:], "i:p:a:r:d:P:Uuvxoh", [
@ -200,36 +184,35 @@ def main(argv=None):
for k, v in opts:
if k in ("-p", "--port"):
try:
port = int(v)
c.port = int(v)
except Exception as ex:
sys.exit("Invalid port(%r)!" % v)
elif k in ("-a", "--authenticate"):
authed_ops_list = [a.lower()
c.authenticated = [a.lower()
for a in re.split("[, ]+", v.strip(" ,"))
if a]
if authed_ops_list == ['.']:
authed_ops_list = []
if c.authenticated == ['.']:
c.authenticated = []
else:
actions = ("list", "download", "update")
for a in authed_ops_list:
for a in c.authenticated:
if a not in actions:
errmsg = "Action '%s' for option `%s` not one of %s!"
sys.exit(errmsg % (a, k, actions))
elif k in ("-i", "--interface"):
host = v
c.host = v
elif k in ("-r", "--root"):
roots.append(v)
elif k == "--disable-fallback":
redirect_to_fallback = False
c.redirect_to_fallback = False
elif k == "--fallback-url":
fallback_url = v
c.fallback_url = v
elif k == "--server":
server = v
c.server = v
elif k == "--welcome":
welcome_file = v
c.welcome_file = v
elif k == "--version":
from pypiserver import __version__
print("pypiserver %s\n" % __version__)
print("pypiserver %s\n" % pypiserver.__version__)
return
elif k == "-U":
command = "update"
@ -240,75 +223,63 @@ def main(argv=None):
elif k == "-d":
update_directory = v
elif k in ("-P", "--passwords"):
password_file = v
c.password_file = v
elif k in ("-o", "--overwrite"):
overwrite = True
c.overwrite = True
elif k == "--log-file":
log_file = v
c.log_file = v
elif k == "--log-frmt":
log_frmt = v
c.log_frmt = v
elif k == "--log-req-frmt":
log_req_frmt = v
c.log_req_frmt = v
elif k == "--log-res-frmt":
log_res_frmt = v
c.log_res_frmt = v
elif k == "--log-err-frmt":
log_err_frmt = v
c.log_err_frmt = v
elif k == "--cache-control":
cache_control = v
c.cache_control = v
elif k == "-v":
verbosity += 1
c.verbosity += 1
elif k in ("-h", "--help"):
print(usage())
sys.exit(0)
if (not authed_ops_list and password_file != '.' or
authed_ops_list and password_file == '.'):
if (not c.authenticated and c.password_file != '.' or
c.authenticated and c.password_file == '.'):
auth_err = "When auth-ops-list is empty (-a=.), password-file (-P=%r) must also be empty ('.')!"
sys.exit(auth_err % password_file)
sys.exit(auth_err % c.password_file)
if len(roots) == 0:
roots.append(os.path.expanduser("~/packages"))
roots=[os.path.abspath(x) for x in roots]
c.root = roots
verbose_levels=[
logging.WARNING, logging.INFO, logging.DEBUG, logging.NOTSET]
log_level=list(zip(verbose_levels, range(verbosity)))[-1][0]
init_logging(level=log_level, filename=log_file, frmt=log_frmt)
log_level=list(zip(verbose_levels, range(c.verbosity)))[-1][0]
init_logging(level=log_level, filename=c.log_file, frmt=c.log_frmt)
if command == "update":
from pypiserver.manage import update_all_packages
update_all_packages(
roots, update_directory, update_dry_run, stable_only=update_stable_only)
update_all_packages(roots, update_directory,
dry_run=update_dry_run, stable_only=update_stable_only)
return
# Fixes #49:
# The gevent server adapter needs to patch some
# modules BEFORE importing bottle!
if server and server.startswith('gevent'):
if c.server and c.server.startswith('gevent'):
import gevent.monkey # @UnresolvedImport
gevent.monkey.patch_all()
from pypiserver.bottle import server_names, run
if server not in server_names:
if c.server not in server_names:
sys.exit("unknown server %r. choose one of %s" % (
server, ", ".join(server_names.keys())))
c.server, ", ".join(server_names.keys())))
from pypiserver import __version__, app
app = app(
root=roots,
redirect_to_fallback=redirect_to_fallback,
authenticated=authed_ops_list,
password_file=password_file,
fallback_url=fallback_url,
overwrite=overwrite,
log_req_frmt=log_req_frmt, log_res_frmt=log_res_frmt, log_err_frmt=log_err_frmt,
welcome_file=welcome_file,
cache_control=cache_control,
)
log.info("This is pypiserver %s serving %r on http://%s:%s\n\n",
__version__, ", ".join(roots), host, port)
run(app=app, host=host, port=port, server=server)
app = pypiserver.app(**vars(c))
run(app=app, host=c.host, port=c.port, server=c.server)
if __name__ == "__main__":

@ -12,18 +12,9 @@ import re
import sys
import pkg_resources
from . import Configuration
class Configuration(object):
def __init__(self, **kwds):
vars(self).update(kwds)
def __repr__(self, *args, **kwargs):
return 'Configuration(**%s)' % vars(self)
def __str__(self, *args, **kwargs):
return 'Configuration:\n%s' % '\n'.join('%16s = %s' % (k, v)
for k, v in sorted(vars(self).items()))
log = logging.getLogger(__file__)
def configure(root=None,
redirect_to_fallback=True,
@ -31,14 +22,31 @@ def configure(root=None,
authenticated=None,
password_file=None,
overwrite=False,
log_file=None,
log_frmt=None,
log_req_frmt=None,
log_res_frmt=None,
log_err_frmt=None,
welcome_file=None,
cache_control=None,
auther=None
auther=None,
host=None, port=None, server=None, verbosity=None, VERSION=None
):
"""
:param root:
A list of paths, derived from the packages specified on cmd-line.
:param redirect_to_fallback:
see :option:`--disable-fallback`
:param authenticated:
see :option:`--authenticate`
:param password_file:
see :option:`--passwords`
:param log_file:
see :option:`--log-file`
Not used, passed here for logging it.
:param log_frmt:
see :option:`--log-frmt`
Not used, passed here for logging it.
:param callable auther:
An API-only options that if it evaluates to a callable,
it is invoked to allow access to protected operations
@ -47,11 +55,25 @@ def configure(root=None,
auther(username, password): bool
When defined, `password_file` is ignored.
:param host:
see :option:`--interface`
Not used, passed here for logging it.
:param port:
see :option:`--port`
Not used, passed here for logging it.
:param server:
see :option:`--server`
Not used, passed here for logging it.
:param verbosity:
see :option:`-v`
Not used, passed here for logging it.
:param VERSION:
Not used, passed here for logging it.
:return: a 2-tuple (Configure, package-list)
"""
log.info("+++Invoked with: %s", Configuration(
log.info("+++Pypiserver invoked with: %s", Configuration(
root=root,
redirect_to_fallback=redirect_to_fallback,
fallback_url=fallback_url,
@ -59,11 +81,15 @@ def configure(root=None,
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
auther=auther,
host=host, port=port, server=server,
verbosity=verbosity, VERSION=VERSION
))
@ -128,7 +154,7 @@ def configure(root=None,
cache_control=cache_control,
auther=auther
)
log.info("+++Starting with: %s", config)
log.info("+++Pypiserver started with: %s", config)
return config, packages
@ -144,7 +170,6 @@ def auth_by_htpasswd_file(htPsswdFile, username, password):
mimetypes.add_type("application/octet-stream", ".egg")
mimetypes.add_type("application/octet-stream", ".whl")
log = logging.getLogger('pypiserver.core')
# --- the following two functions were copied from distribute's pkg_resources module
component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)