mirror of https://github.com/tLDP/python-tldp
adding some logic for repproducing received configurations
This commit is contained in:
parent
e7af014a73
commit
372ae734de
|
@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
import os
|
||||
import sys
|
||||
import functools
|
||||
|
||||
from argparse import ArgumentParser, ArgumentError, Namespace
|
||||
from argparse import _UNRECOGNIZED_ARGS_ATTR
|
||||
|
@ -13,10 +14,8 @@ import logging
|
|||
logging.basicConfig(stream=sys.stderr, level=logging.WARNING)
|
||||
logger = logging.getLogger()
|
||||
|
||||
CFGSEP = '.'
|
||||
ENVSEP = '_'
|
||||
CLISEP = '-'
|
||||
|
||||
ENVSEP = NSSEP = '_' # -- underscore _
|
||||
CLISEP = CFGSEP = '-' # -- dash -
|
||||
MULTIVALUESEP = ','
|
||||
|
||||
try:
|
||||
|
@ -66,8 +65,8 @@ def convert_multivalues(d, multivaluesep=MULTIVALUESEP):
|
|||
return d
|
||||
|
||||
|
||||
def cfg_to_dict(f, base=None, cfgsep=CFGSEP, clisep=CLISEP):
|
||||
'''read a configuration file; convert to CLI-parseable form
|
||||
def dict_from_cfg(f, base=None, cfgsep=CFGSEP, clisep=CLISEP):
|
||||
'''read a configuration file, normalizing fields to CLI-parseable form
|
||||
|
||||
:param: f, a filename or file-like object (readable via
|
||||
ConfigParser.read() [filename] or ConfigParser.fp() [open file]
|
||||
|
@ -89,7 +88,7 @@ def cfg_to_dict(f, base=None, cfgsep=CFGSEP, clisep=CLISEP):
|
|||
|
||||
When invoked as:
|
||||
|
||||
cfg_to_dict(f) # -- where f is the pathname or open filehandle
|
||||
dict_from_cfg(f) # -- where f is the pathname or open filehandle
|
||||
|
||||
This function will return a dict that looks like this:
|
||||
|
||||
|
@ -116,8 +115,8 @@ def cfg_to_dict(f, base=None, cfgsep=CFGSEP, clisep=CLISEP):
|
|||
return d
|
||||
|
||||
|
||||
def env_to_dict(env=os.environ, base=None, envsep=ENVSEP, clisep=CLISEP):
|
||||
'''read environment, return keys starting with 'base'
|
||||
def dict_from_envdict(env=os.environ, base=None, envsep=ENVSEP, clisep=CLISEP):
|
||||
'''read environment, normalizing all keys starting with 'base' to CLI form
|
||||
|
||||
:param: env, if nothing is supplied, os.environ
|
||||
:param: base [optional], envar prefix filter selection criterion
|
||||
|
@ -141,7 +140,7 @@ def env_to_dict(env=os.environ, base=None, envsep=ENVSEP, clisep=CLISEP):
|
|||
|
||||
When invoked as:
|
||||
|
||||
env_to_dict(os.environ, 'SSH')
|
||||
dict_from_envdict(os.environ, 'SSH')
|
||||
|
||||
This function will return a dict that looks like this:
|
||||
|
||||
|
@ -150,7 +149,7 @@ def env_to_dict(env=os.environ, base=None, envsep=ENVSEP, clisep=CLISEP):
|
|||
|
||||
When invoked as:
|
||||
|
||||
env_to_dict(os.environ)
|
||||
dict_from_envdict(os.environ)
|
||||
|
||||
This function will return a dict that looks like this:
|
||||
|
||||
|
@ -170,7 +169,15 @@ def env_to_dict(env=os.environ, base=None, envsep=ENVSEP, clisep=CLISEP):
|
|||
return d
|
||||
|
||||
|
||||
def strip_tag_from_key(base, d, clisep=CLISEP):
|
||||
def prepend_tag(base, d, sep=CLISEP):
|
||||
newd = dict()
|
||||
tag = ''.join((base, sep))
|
||||
for k, v in d.items():
|
||||
newd[''.join((tag, k.upper()))] = v
|
||||
return newd
|
||||
|
||||
|
||||
def strip_tag(base, d, clisep=CLISEP):
|
||||
if not base:
|
||||
return d
|
||||
newd = dict()
|
||||
|
@ -179,8 +186,9 @@ def strip_tag_from_key(base, d, clisep=CLISEP):
|
|||
if oldk.startswith(tag):
|
||||
newk = oldk[len(tag):]
|
||||
if newk in newd:
|
||||
logger.debug("Duplicate key found when stripping %s from %s",
|
||||
logger.debug("Duplicate key found when stripping %s from %s.",
|
||||
tag, oldk)
|
||||
logger.info("strip_tag: returning unchanged dict().")
|
||||
return d
|
||||
newd[newk] = v
|
||||
else:
|
||||
|
@ -188,6 +196,17 @@ def strip_tag_from_key(base, d, clisep=CLISEP):
|
|||
return newd
|
||||
|
||||
|
||||
def dict_from_ns(ns):
|
||||
return vars(ns)
|
||||
|
||||
|
||||
def ns_from_dict(d):
|
||||
ns = Namespace()
|
||||
for k, v in d.items():
|
||||
setattr(ns, k, v)
|
||||
return ns
|
||||
|
||||
|
||||
def argv_from_env(args, tag, **kw):
|
||||
'''read a config file and produce argparse-compatible invocation
|
||||
|
||||
|
@ -214,11 +233,11 @@ def argv_from_env(args, tag, **kw):
|
|||
'--sourcedir', '/path/faq/docbook/',
|
||||
'--sourcedir', '/path/howto/linuxdoc/']
|
||||
'''
|
||||
d = env_to_dict(args, base=tag.upper(), **kw)
|
||||
d = dict_from_envdict(args, base=tag.upper(), **kw)
|
||||
listify_values = kw.get('convert_multivalues', convert_multivalues)
|
||||
if listify_values is not None:
|
||||
d = listify_values(d)
|
||||
d = strip_tag_from_key(tag, d)
|
||||
d = strip_tag(tag, d)
|
||||
d = dict_to_argv_longform(d)
|
||||
return d
|
||||
|
||||
|
@ -258,26 +277,15 @@ def argv_from_cfg(args, tag, **kw):
|
|||
'--linuxdoc-sgml2html', '/usr/bin/sgml2html',
|
||||
'--docbook-xsltproc', '/usr/bin/xsltproc']
|
||||
'''
|
||||
d = cfg_to_dict(args, base=tag, **kw)
|
||||
d = dict_from_cfg(args, base=tag, **kw)
|
||||
listify_values = kw.get('convert_multivalues', convert_multivalues)
|
||||
if listify_values is not None:
|
||||
d = listify_values(d, **kw)
|
||||
d = strip_tag_from_key(tag, d, **kw)
|
||||
d = strip_tag(tag, d, **kw)
|
||||
d = dict_to_argv_longform(d, **kw)
|
||||
return d
|
||||
|
||||
|
||||
def dict_from_namespace(ns):
|
||||
return vars(ns)
|
||||
|
||||
|
||||
def namespace_from_dict(d):
|
||||
ns = Namespace()
|
||||
for k, v in d.items():
|
||||
setattr(ns, k, v)
|
||||
return ns
|
||||
|
||||
|
||||
class DefaultFreeArgumentParser(ArgumentParser):
|
||||
'''subclass of stock argparse.ArgumentParser; suppress default generation
|
||||
|
||||
|
@ -360,8 +368,9 @@ class CascadingConfig(object):
|
|||
order = ['cli', 'environment', 'userconfig', 'systemconfig', 'defaults']
|
||||
'''
|
||||
order = ['cli', 'environment', 'userconfig', 'systemconfig', 'defaults']
|
||||
mine = ['--dump_cli', '--dump_env', '--dump_cfg', '--debug_options']
|
||||
|
||||
def __init__(self, tag, parser, argv=sys.argv[1:], env=os.environ,
|
||||
def __init__(self, tag, argparser, argv=sys.argv[1:], env=os.environ,
|
||||
configfile='configfile', order=order):
|
||||
'''construct a CascadingConfig
|
||||
|
||||
|
@ -384,12 +393,17 @@ class CascadingConfig(object):
|
|||
# -- a wee-bit hackish; but this is crucial to the proper functioning
|
||||
# of CascadingConfig
|
||||
#
|
||||
assert hasattr(parser, 'parse_known_args_no_defaults')
|
||||
assert hasattr(argparser, 'parse_known_args_no_defaults')
|
||||
for opt in self.mine:
|
||||
synonym = opt.replace(NSSEP, CLISEP)
|
||||
argparser.add_argument(opt, synonym, action='store_true')
|
||||
|
||||
self.tag = tag
|
||||
self.order = order
|
||||
self.parser = parser.parse_known_args_no_defaults
|
||||
self.argparser = argparser
|
||||
self.parser = argparser.parse_known_args_no_defaults
|
||||
|
||||
self.defaults = parser.parse_args([]) # -- "compiled-in" defaults
|
||||
self.defaults = argparser.parse_args([]) # -- "compiled-in" defaults
|
||||
self.cli, _ = self.parser(argv)
|
||||
self.environment, _ = self.parser(argv_from_env(env, tag))
|
||||
|
||||
|
@ -421,6 +435,46 @@ class CascadingConfig(object):
|
|||
|
||||
self.resolve()
|
||||
|
||||
# -- clean up after ourselves (and report in, if asked)
|
||||
#
|
||||
diagfunc = False
|
||||
for opt in self.mine:
|
||||
opt = opt.lstrip(CLISEP)
|
||||
if getattr(self.config, opt, False):
|
||||
diagfunc = getattr(self, opt)
|
||||
delattr(self.config, opt)
|
||||
if diagfunc:
|
||||
sys.exit(diagfunc())
|
||||
|
||||
def dump_env(self):
|
||||
d = dict_from_ns(self.config)
|
||||
d = prepend_tag(self.tag.upper(), d, sep=ENVSEP)
|
||||
for k, v in d.items():
|
||||
if isinstance(v, (list, tuple)):
|
||||
v = MULTIVALUESEP.join(v)
|
||||
print('{}={}'.format(k, v))
|
||||
return 0
|
||||
|
||||
def dump_cfg(self):
|
||||
d = dict_from_ns(self.config)
|
||||
return 0
|
||||
|
||||
def dump_cli(self):
|
||||
d = dict_from_ns(self.config)
|
||||
cli = list()
|
||||
for k, v in d.items():
|
||||
k = ''.join(('--', k.replace(NSSEP, CLISEP)))
|
||||
if isinstance(v, (list, tuple)):
|
||||
for val in v:
|
||||
cli.extend((k, str(val)))
|
||||
else:
|
||||
cli.extend((k, str(v)))
|
||||
print(' '.join(cli))
|
||||
return 0
|
||||
|
||||
def debug_options(self):
|
||||
return 0
|
||||
|
||||
def resolve(self, order=None):
|
||||
if order is None:
|
||||
order = self.order
|
||||
|
@ -436,7 +490,7 @@ class CascadingConfig(object):
|
|||
logger.info("Source %s: replacing %s=%s with %s=%s",
|
||||
sourcename, name, oldval, name, newval)
|
||||
setattr(config, name, newval)
|
||||
return config
|
||||
self.config = config
|
||||
|
||||
|
||||
def sample(args):
|
||||
|
@ -451,9 +505,10 @@ def sample(args):
|
|||
type=str)
|
||||
parser.add_argument('--configfile', '--cfg', '--config-file', type=str,
|
||||
default="/home/mabrown/tmp/ldptool.cfg")
|
||||
uniconf = CascadingConfig(tag, parser, sys.argv[1:])
|
||||
cc = CascadingConfig(tag, parser, sys.argv[1:])
|
||||
config = cc.config
|
||||
import pprint
|
||||
pprint.pprint(uniconf.resolve())
|
||||
pprint.pprint(dict_from_ns(config))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Reference in New Issue