"""
Configuration handling
.. todo::
switch ruamel.yaml to round trip loading
to preserver order and comments?
Authors:
Thomas A. Hopf
"""
import ruamel.yaml as yaml
[docs]class MissingParameterError(Exception):
"""
Exception for missing parameters
"""
[docs]class InvalidParameterError(Exception):
"""
Exception for invalid parameter settings
"""
[docs]def parse_config(config_str, preserve_order=False):
"""
Parse a configuration string
Parameters
----------
config_str : str
Configuration to be parsed
preserve_order : bool, optional (default: True)
Preserve formatting of input configuration
string
Returns
-------
dict
Configuration dictionary
"""
try:
if preserve_order:
return yaml.load(config_str, Loader=yaml.RoundTripLoader)
else:
return yaml.safe_load(config_str)
except yaml.parser.ParserError as e:
raise InvalidParameterError(
"Could not parse input configuration. "
"Formatting mistake in config file? "
"See ParserError above for details."
) from e
[docs]def read_config_file(filename, preserve_order=False):
"""
Read and parse a configuration file.
Parameters
----------
filename : str
Path of configuration file
Returns
-------
dict
Configuration dictionary
"""
with open(filename) as f:
return parse_config(f, preserve_order)
[docs]def write_config_file(out_filename, config):
"""
Save configuration data structure in YAML file.
Parameters
----------
out_filename : str
Filename of output file
config : dict
Config data that will be written to file
"""
if isinstance(config, yaml.comments.CommentedBase):
dumper = yaml.RoundTripDumper
else:
dumper = yaml.Dumper
with open(out_filename, "w") as f:
f.write(
yaml.dump(config, Dumper=dumper, default_flow_style=False)
)
[docs]def check_required(params, keys):
"""
Verify if required set of parameters is present in configuration
Parameters
----------
params : dict
Dictionary with parameters
keys : list-like
Set of parameters that has to be present in params
Raises
------
MissingParameterError
"""
missing = [k for k in keys if k not in params]
if len(missing) > 0:
raise MissingParameterError(
"Missing required parameters: {} \nGiven: {}".format(
", ".join(missing), params
)
)
[docs]def iterate_files(outcfg, subset=None):
"""
Generator function to iterate a list of file
items in an outconfig
Parameters
----------
outcfg : dict(str)
Configuration to extract file items for iteration from
subset : list(str)
List of keys in outcfg to restrict iteration to
Returns
-------
tuple(str, str, int)
Generator over tuples (file path, entry key, index).
index will be None if this is a single file entry
(i.e. ending with _file rather than _files).
"""
for k, v in outcfg.items():
# skip items if there is a subset filter and it matches
if subset is not None and k not in subset:
continue
# also skip in case file has a null value
if v is None:
continue
# only look at file entries, so skip everything else
# if not (k.endswith("_file") or k.endswith("_files")):
# continue
if k.endswith("_file"):
yield (v, k, None)
elif k.endswith("_files"):
for i, f in enumerate(v):
yield (f, k, i)
else:
# skip any other entries
pass