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:
parent
2fe615f22e
commit
24eddb292a
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user