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 os
|
||||||
import sys
|
import sys
|
||||||
|
import functools
|
||||||
|
|
||||||
from argparse import ArgumentParser, ArgumentError, Namespace
|
from argparse import ArgumentParser, ArgumentError, Namespace
|
||||||
from argparse import _UNRECOGNIZED_ARGS_ATTR
|
from argparse import _UNRECOGNIZED_ARGS_ATTR
|
||||||
|
@ -13,10 +14,8 @@ import logging
|
||||||
logging.basicConfig(stream=sys.stderr, level=logging.WARNING)
|
logging.basicConfig(stream=sys.stderr, level=logging.WARNING)
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
CFGSEP = '.'
|
ENVSEP = NSSEP = '_' # -- underscore _
|
||||||
ENVSEP = '_'
|
CLISEP = CFGSEP = '-' # -- dash -
|
||||||
CLISEP = '-'
|
|
||||||
|
|
||||||
MULTIVALUESEP = ','
|
MULTIVALUESEP = ','
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -66,8 +65,8 @@ def convert_multivalues(d, multivaluesep=MULTIVALUESEP):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def cfg_to_dict(f, base=None, cfgsep=CFGSEP, clisep=CLISEP):
|
def dict_from_cfg(f, base=None, cfgsep=CFGSEP, clisep=CLISEP):
|
||||||
'''read a configuration file; convert to CLI-parseable form
|
'''read a configuration file, normalizing fields to CLI-parseable form
|
||||||
|
|
||||||
:param: f, a filename or file-like object (readable via
|
:param: f, a filename or file-like object (readable via
|
||||||
ConfigParser.read() [filename] or ConfigParser.fp() [open file]
|
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:
|
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:
|
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
|
return d
|
||||||
|
|
||||||
|
|
||||||
def env_to_dict(env=os.environ, base=None, envsep=ENVSEP, clisep=CLISEP):
|
def dict_from_envdict(env=os.environ, base=None, envsep=ENVSEP, clisep=CLISEP):
|
||||||
'''read environment, return keys starting with 'base'
|
'''read environment, normalizing all keys starting with 'base' to CLI form
|
||||||
|
|
||||||
:param: env, if nothing is supplied, os.environ
|
:param: env, if nothing is supplied, os.environ
|
||||||
:param: base [optional], envar prefix filter selection criterion
|
: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:
|
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:
|
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:
|
When invoked as:
|
||||||
|
|
||||||
env_to_dict(os.environ)
|
dict_from_envdict(os.environ)
|
||||||
|
|
||||||
This function will return a dict that looks like this:
|
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
|
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:
|
if not base:
|
||||||
return d
|
return d
|
||||||
newd = dict()
|
newd = dict()
|
||||||
|
@ -179,8 +186,9 @@ def strip_tag_from_key(base, d, clisep=CLISEP):
|
||||||
if oldk.startswith(tag):
|
if oldk.startswith(tag):
|
||||||
newk = oldk[len(tag):]
|
newk = oldk[len(tag):]
|
||||||
if newk in newd:
|
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)
|
tag, oldk)
|
||||||
|
logger.info("strip_tag: returning unchanged dict().")
|
||||||
return d
|
return d
|
||||||
newd[newk] = v
|
newd[newk] = v
|
||||||
else:
|
else:
|
||||||
|
@ -188,6 +196,17 @@ def strip_tag_from_key(base, d, clisep=CLISEP):
|
||||||
return newd
|
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):
|
def argv_from_env(args, tag, **kw):
|
||||||
'''read a config file and produce argparse-compatible invocation
|
'''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/faq/docbook/',
|
||||||
'--sourcedir', '/path/howto/linuxdoc/']
|
'--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)
|
listify_values = kw.get('convert_multivalues', convert_multivalues)
|
||||||
if listify_values is not None:
|
if listify_values is not None:
|
||||||
d = listify_values(d)
|
d = listify_values(d)
|
||||||
d = strip_tag_from_key(tag, d)
|
d = strip_tag(tag, d)
|
||||||
d = dict_to_argv_longform(d)
|
d = dict_to_argv_longform(d)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
@ -258,26 +277,15 @@ def argv_from_cfg(args, tag, **kw):
|
||||||
'--linuxdoc-sgml2html', '/usr/bin/sgml2html',
|
'--linuxdoc-sgml2html', '/usr/bin/sgml2html',
|
||||||
'--docbook-xsltproc', '/usr/bin/xsltproc']
|
'--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)
|
listify_values = kw.get('convert_multivalues', convert_multivalues)
|
||||||
if listify_values is not None:
|
if listify_values is not None:
|
||||||
d = listify_values(d, **kw)
|
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)
|
d = dict_to_argv_longform(d, **kw)
|
||||||
return d
|
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):
|
class DefaultFreeArgumentParser(ArgumentParser):
|
||||||
'''subclass of stock argparse.ArgumentParser; suppress default generation
|
'''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']
|
||||||
'''
|
'''
|
||||||
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):
|
configfile='configfile', order=order):
|
||||||
'''construct a CascadingConfig
|
'''construct a CascadingConfig
|
||||||
|
|
||||||
|
@ -384,12 +393,17 @@ class CascadingConfig(object):
|
||||||
# -- a wee-bit hackish; but this is crucial to the proper functioning
|
# -- a wee-bit hackish; but this is crucial to the proper functioning
|
||||||
# of CascadingConfig
|
# 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.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.cli, _ = self.parser(argv)
|
||||||
self.environment, _ = self.parser(argv_from_env(env, tag))
|
self.environment, _ = self.parser(argv_from_env(env, tag))
|
||||||
|
|
||||||
|
@ -421,6 +435,46 @@ class CascadingConfig(object):
|
||||||
|
|
||||||
self.resolve()
|
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):
|
def resolve(self, order=None):
|
||||||
if order is None:
|
if order is None:
|
||||||
order = self.order
|
order = self.order
|
||||||
|
@ -436,7 +490,7 @@ class CascadingConfig(object):
|
||||||
logger.info("Source %s: replacing %s=%s with %s=%s",
|
logger.info("Source %s: replacing %s=%s with %s=%s",
|
||||||
sourcename, name, oldval, name, newval)
|
sourcename, name, oldval, name, newval)
|
||||||
setattr(config, name, newval)
|
setattr(config, name, newval)
|
||||||
return config
|
self.config = config
|
||||||
|
|
||||||
|
|
||||||
def sample(args):
|
def sample(args):
|
||||||
|
@ -451,9 +505,10 @@ def sample(args):
|
||||||
type=str)
|
type=str)
|
||||||
parser.add_argument('--configfile', '--cfg', '--config-file', type=str,
|
parser.add_argument('--configfile', '--cfg', '--config-file', type=str,
|
||||||
default="/home/mabrown/tmp/ldptool.cfg")
|
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
|
import pprint
|
||||||
pprint.pprint(uniconf.resolve())
|
pprint.pprint(dict_from_ns(config))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Reference in New Issue