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
|
import re as _re
|
||||||
|
|
||||||
version = __version__ = "1.1.9-dev.3"
|
version = __version__ = "1.1.9-dev.3"
|
||||||
__version_info__ = tuple(_re.split('[.-]', __version__))
|
__version_info__ = tuple(_re.split('[.-]', __version__))
|
||||||
__updated__ = "2015-12-20"
|
__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"
|
__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):
|
def app(**kwds):
|
||||||
|
"""
|
||||||
|
:param dict kwds:
|
||||||
|
May use ``**vars(default_config())`.
|
||||||
|
"""
|
||||||
from . import core, _app, bottle
|
from . import core, _app, bottle
|
||||||
|
|
||||||
bottle.debug(True)
|
bottle.debug(True)
|
||||||
@ -23,26 +74,26 @@ def app(**kwds):
|
|||||||
def paste_app_factory(global_config, **local_conf):
|
def paste_app_factory(global_config, **local_conf):
|
||||||
import os
|
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):
|
def _make_root(root):
|
||||||
root = root.strip()
|
root = root.strip()
|
||||||
if root.startswith("~"):
|
if root.startswith("~"):
|
||||||
return os.path.expanduser(root)
|
return os.path.expanduser(root)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
c = default_config()
|
||||||
|
|
||||||
root = local_conf.get("root")
|
root = local_conf.get("root")
|
||||||
if root:
|
if root:
|
||||||
roots = [_make_root(x) for x in root.split("\n") if x.strip()]
|
c.root = [_make_root(x) for x in root.split("\n") if x.strip()]
|
||||||
else:
|
|
||||||
roots = None
|
|
||||||
|
|
||||||
def str2bool(s, default):
|
upd_bool_attr_from_dict_str_item(c, 'redirect_to_fallback', local_conf)
|
||||||
if s is not None and s != '':
|
upd_bool_attr_from_dict_str_item(c, 'overwrite', local_conf)
|
||||||
return s.lower() not in ("no", "off", "0", "false")
|
|
||||||
return default
|
|
||||||
|
|
||||||
redirect_to_fallback = str2bool(
|
return app(**vars(c))
|
||||||
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)
|
|
||||||
|
@ -14,8 +14,6 @@ import logging
|
|||||||
import warnings
|
import warnings
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
DEFAULT_SERVER = "auto"
|
|
||||||
|
|
||||||
warnings.filterwarnings("ignore", "Python 2.5 support may be dropped in future versions of Bottle")
|
warnings.filterwarnings("ignore", "Python 2.5 support may be dropped in future versions of Bottle")
|
||||||
log = logging.getLogger('pypiserver.main')
|
log = logging.getLogger('pypiserver.main')
|
||||||
|
|
||||||
@ -145,32 +143,18 @@ def usage():
|
|||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
|
import pypiserver
|
||||||
|
|
||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
|
|
||||||
global packages
|
|
||||||
|
|
||||||
command = "serve"
|
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
|
c = pypiserver.default_config()
|
||||||
update_directory = None
|
|
||||||
update_stable_only = True
|
update_dry_run = True,
|
||||||
|
update_directory = None,
|
||||||
|
update_stable_only = True,
|
||||||
|
|
||||||
try:
|
try:
|
||||||
opts, roots = getopt.getopt(argv[1:], "i:p:a:r:d:P:Uuvxoh", [
|
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:
|
for k, v in opts:
|
||||||
if k in ("-p", "--port"):
|
if k in ("-p", "--port"):
|
||||||
try:
|
try:
|
||||||
port = int(v)
|
c.port = int(v)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
sys.exit("Invalid port(%r)!" % v)
|
sys.exit("Invalid port(%r)!" % v)
|
||||||
elif k in ("-a", "--authenticate"):
|
elif k in ("-a", "--authenticate"):
|
||||||
authed_ops_list = [a.lower()
|
c.authenticated = [a.lower()
|
||||||
for a in re.split("[, ]+", v.strip(" ,"))
|
for a in re.split("[, ]+", v.strip(" ,"))
|
||||||
if a]
|
if a]
|
||||||
if authed_ops_list == ['.']:
|
if c.authenticated == ['.']:
|
||||||
authed_ops_list = []
|
c.authenticated = []
|
||||||
else:
|
else:
|
||||||
actions = ("list", "download", "update")
|
actions = ("list", "download", "update")
|
||||||
for a in authed_ops_list:
|
for a in c.authenticated:
|
||||||
if a not in actions:
|
if a not in actions:
|
||||||
errmsg = "Action '%s' for option `%s` not one of %s!"
|
errmsg = "Action '%s' for option `%s` not one of %s!"
|
||||||
sys.exit(errmsg % (a, k, actions))
|
sys.exit(errmsg % (a, k, actions))
|
||||||
elif k in ("-i", "--interface"):
|
elif k in ("-i", "--interface"):
|
||||||
host = v
|
c.host = v
|
||||||
elif k in ("-r", "--root"):
|
elif k in ("-r", "--root"):
|
||||||
roots.append(v)
|
roots.append(v)
|
||||||
elif k == "--disable-fallback":
|
elif k == "--disable-fallback":
|
||||||
redirect_to_fallback = False
|
c.redirect_to_fallback = False
|
||||||
elif k == "--fallback-url":
|
elif k == "--fallback-url":
|
||||||
fallback_url = v
|
c.fallback_url = v
|
||||||
elif k == "--server":
|
elif k == "--server":
|
||||||
server = v
|
c.server = v
|
||||||
elif k == "--welcome":
|
elif k == "--welcome":
|
||||||
welcome_file = v
|
c.welcome_file = v
|
||||||
elif k == "--version":
|
elif k == "--version":
|
||||||
from pypiserver import __version__
|
print("pypiserver %s\n" % pypiserver.__version__)
|
||||||
print("pypiserver %s\n" % __version__)
|
|
||||||
return
|
return
|
||||||
elif k == "-U":
|
elif k == "-U":
|
||||||
command = "update"
|
command = "update"
|
||||||
@ -240,75 +223,63 @@ def main(argv=None):
|
|||||||
elif k == "-d":
|
elif k == "-d":
|
||||||
update_directory = v
|
update_directory = v
|
||||||
elif k in ("-P", "--passwords"):
|
elif k in ("-P", "--passwords"):
|
||||||
password_file = v
|
c.password_file = v
|
||||||
elif k in ("-o", "--overwrite"):
|
elif k in ("-o", "--overwrite"):
|
||||||
overwrite = True
|
c.overwrite = True
|
||||||
elif k == "--log-file":
|
elif k == "--log-file":
|
||||||
log_file = v
|
c.log_file = v
|
||||||
elif k == "--log-frmt":
|
elif k == "--log-frmt":
|
||||||
log_frmt = v
|
c.log_frmt = v
|
||||||
elif k == "--log-req-frmt":
|
elif k == "--log-req-frmt":
|
||||||
log_req_frmt = v
|
c.log_req_frmt = v
|
||||||
elif k == "--log-res-frmt":
|
elif k == "--log-res-frmt":
|
||||||
log_res_frmt = v
|
c.log_res_frmt = v
|
||||||
elif k == "--log-err-frmt":
|
elif k == "--log-err-frmt":
|
||||||
log_err_frmt = v
|
c.log_err_frmt = v
|
||||||
elif k == "--cache-control":
|
elif k == "--cache-control":
|
||||||
cache_control = v
|
c.cache_control = v
|
||||||
elif k == "-v":
|
elif k == "-v":
|
||||||
verbosity += 1
|
c.verbosity += 1
|
||||||
elif k in ("-h", "--help"):
|
elif k in ("-h", "--help"):
|
||||||
print(usage())
|
print(usage())
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if (not authed_ops_list and password_file != '.' or
|
if (not c.authenticated and c.password_file != '.' or
|
||||||
authed_ops_list and password_file == '.'):
|
c.authenticated and c.password_file == '.'):
|
||||||
auth_err = "When auth-ops-list is empty (-a=.), password-file (-P=%r) must also be empty ('.')!"
|
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:
|
if len(roots) == 0:
|
||||||
roots.append(os.path.expanduser("~/packages"))
|
roots.append(os.path.expanduser("~/packages"))
|
||||||
|
|
||||||
roots=[os.path.abspath(x) for x in roots]
|
roots=[os.path.abspath(x) for x in roots]
|
||||||
|
c.root = roots
|
||||||
|
|
||||||
verbose_levels=[
|
verbose_levels=[
|
||||||
logging.WARNING, logging.INFO, logging.DEBUG, logging.NOTSET]
|
logging.WARNING, logging.INFO, logging.DEBUG, logging.NOTSET]
|
||||||
log_level=list(zip(verbose_levels, range(verbosity)))[-1][0]
|
log_level=list(zip(verbose_levels, range(c.verbosity)))[-1][0]
|
||||||
init_logging(level=log_level, filename=log_file, frmt=log_frmt)
|
init_logging(level=log_level, filename=c.log_file, frmt=c.log_frmt)
|
||||||
|
|
||||||
if command == "update":
|
if command == "update":
|
||||||
from pypiserver.manage import update_all_packages
|
from pypiserver.manage import update_all_packages
|
||||||
update_all_packages(
|
update_all_packages(roots, update_directory,
|
||||||
roots, update_directory, update_dry_run, stable_only=update_stable_only)
|
dry_run=update_dry_run, stable_only=update_stable_only)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Fixes #49:
|
# Fixes #49:
|
||||||
# The gevent server adapter needs to patch some
|
# The gevent server adapter needs to patch some
|
||||||
# modules BEFORE importing bottle!
|
# modules BEFORE importing bottle!
|
||||||
if server and server.startswith('gevent'):
|
if c.server and c.server.startswith('gevent'):
|
||||||
import gevent.monkey # @UnresolvedImport
|
import gevent.monkey # @UnresolvedImport
|
||||||
gevent.monkey.patch_all()
|
gevent.monkey.patch_all()
|
||||||
|
|
||||||
from pypiserver.bottle import server_names, run
|
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" % (
|
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 = pypiserver.app(**vars(c))
|
||||||
app = app(
|
run(app=app, host=c.host, port=c.port, server=c.server)
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -12,18 +12,9 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
from . import Configuration
|
||||||
|
|
||||||
|
log = logging.getLogger(__file__)
|
||||||
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()))
|
|
||||||
|
|
||||||
def configure(root=None,
|
def configure(root=None,
|
||||||
redirect_to_fallback=True,
|
redirect_to_fallback=True,
|
||||||
@ -31,14 +22,31 @@ def configure(root=None,
|
|||||||
authenticated=None,
|
authenticated=None,
|
||||||
password_file=None,
|
password_file=None,
|
||||||
overwrite=False,
|
overwrite=False,
|
||||||
|
log_file=None,
|
||||||
|
log_frmt=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,
|
welcome_file=None,
|
||||||
cache_control=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:
|
:param callable auther:
|
||||||
An API-only options that if it evaluates to a callable,
|
An API-only options that if it evaluates to a callable,
|
||||||
it is invoked to allow access to protected operations
|
it is invoked to allow access to protected operations
|
||||||
@ -47,11 +55,25 @@ def configure(root=None,
|
|||||||
auther(username, password): bool
|
auther(username, password): bool
|
||||||
|
|
||||||
When defined, `password_file` is ignored.
|
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)
|
:return: a 2-tuple (Configure, package-list)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
log.info("+++Invoked with: %s", Configuration(
|
log.info("+++Pypiserver invoked with: %s", Configuration(
|
||||||
root=root,
|
root=root,
|
||||||
redirect_to_fallback=redirect_to_fallback,
|
redirect_to_fallback=redirect_to_fallback,
|
||||||
fallback_url=fallback_url,
|
fallback_url=fallback_url,
|
||||||
@ -59,11 +81,15 @@ def configure(root=None,
|
|||||||
password_file=password_file,
|
password_file=password_file,
|
||||||
overwrite=overwrite,
|
overwrite=overwrite,
|
||||||
welcome_file=welcome_file,
|
welcome_file=welcome_file,
|
||||||
|
log_file=log_file,
|
||||||
|
log_frmt=log_frmt,
|
||||||
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,
|
||||||
cache_control=cache_control,
|
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,
|
cache_control=cache_control,
|
||||||
auther=auther
|
auther=auther
|
||||||
)
|
)
|
||||||
log.info("+++Starting with: %s", config)
|
log.info("+++Pypiserver started with: %s", config)
|
||||||
|
|
||||||
return config, packages
|
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", ".egg")
|
||||||
mimetypes.add_type("application/octet-stream", ".whl")
|
mimetypes.add_type("application/octet-stream", ".whl")
|
||||||
|
|
||||||
log = logging.getLogger('pypiserver.core')
|
|
||||||
|
|
||||||
# --- the following two functions were copied from distribute's pkg_resources module
|
# --- the following two functions were copied from distribute's pkg_resources module
|
||||||
component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
|
component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
|
||||||
|
Loading…
Reference in New Issue
Block a user