Add direct confdef support in ConfigManager

master
Tom Wilson 6 years ago
parent e4d1e145e4
commit dabd811e8e

@ -176,14 +176,15 @@ class ConfDefinition(TableDef):
class ConfigManager(): class ConfigManager():
def __init__(self): def __init__(self):
self.root_config = {} self.root_config = {}
self.confdefs = {}
def load(self, source): def load(self, source):
""" """
Load a config source into the ConfigManager. Load a config source into the ConfigManager, replacing any existing config.
Args: Args:
source: Either a dict config to load directly, a filepath to a TOML file, source: Either a dict config to load directly, a filepath to a TOML file,
or a open file. or an open file.
""" """
if isinstance(source, dict): # load from dict if isinstance(source, dict): # load from dict
self.root_config = source self.root_config = source
@ -193,54 +194,117 @@ class ConfigManager():
else: # load from file else: # load from file
self.root_config = toml.load(source) self.root_config = toml.load(source)
def get_config(self, table_name, conf_def): def load_overlay(self, source):
"""
Load a config source into the ConfigManager, merging it over the top of any existing
config. Dicts will be recursively processed with keys being merged and existing values
being replaced by the new source. This includes lists, which will be treated as any other
value and completely replaced.
Args:
source: Either a dict config to load directly, a filepath to a TOML file,
or an open file.
"""
if isinstance(source, dict): # load from dict
new_source = source
elif isinstance(source, str): # load from pathname
with open(source, 'r') as conf_file:
new_source = toml.load(conf_file)
else: # load from file
new_source = toml.load(source)
self._overlay(new_source, self.root_config)
def add_confdef(self, name, confdef):
"""
Stores a ConfigDefinition for future use when validating the corresponding config table
Args:
name (str) : Then name to store the config definition under.
confdef (ConfigDefinition): The populated ConfigDefinition to store.
"""
self.confdefs[name]=confdef
def add_confdefs(self, confdefs):
"""
Stores multiple ConfigDefinitions at once for future use when validating the corresponding config tables
Args:
confdefs : A dict of populated ConfigDefinitions to store, using their keys as names.
"""
self.confdefs.update(confdefs)
def get_missing_confdefs(self):
"""
Returns a list of config table names that do not have a corresponding ConfigDefinition
stored in the ConfigManager.
"""
return list(self.root_config.keys() - self.confdefs.keys())
def _overlay(self, src, dest):
for key in src:
# If the key is also in the dest and both are dicts, merge them.
if key in dest and isinstance(src[key], dict) and isinstance(dest[key], dict):
self._overlay(src[key], dest[key])
else:
# Otherwise it's either an existing value to be replaced or needs to be added.
dest[key] = src[key]
def validate_and_get_config(self, config_name, conf_def=None):
""" """
Get a config dict called ``table_name`` and validate Get a config dict called ``table_name`` and validate
it against ``conf_def`` before returning it. it against the corresponding config definition stored in the ConfigManager.
If ``conf_def`` is supplied, it gets used instead. Returns a validated
config dictionary.
Note that as part of validation, optional keys that are missing will be Note that as part of validation, optional keys that are missing will be
filled in with their default values (see ``TableDef``). filled in with their default values (see ``TableDef``).
Args: Args:
table_name: (str) Name of the config dict to find. table_name: (str) Name of the config dict to find.
conf_def: (ConfDefinition) Config definition to validate against. conf_def: (ConfDefinition) Optional config definition to validate against.
""" """
if not isinstance(conf_def, ConfDefinition): if not isinstance(conf_def, ConfDefinition):
raise TypeError("Supplied config definition must be an instance " conf_def = self.confdefs[config_name]
"of ConfDefinition")
if table_name not in self.root_config: if config_name not in self.root_config:
raise InvalidConfigError( raise InvalidConfigError(
"Config must contain table: " + table_name) "Config must contain table: " + config_name)
try: try:
conf_def.validate(self.root_config[table_name]) conf_def.validate(self.root_config[config_name])
except InvalidConfigError as e: except InvalidConfigError as e:
e.args = ("Module: " + table_name,) + e.args e.args = ("Module: " + config_name,) + e.args
raise raise
return self.root_config[table_name] return self.root_config[config_name]
def get_configs(self, conf_defs): def validate_and_get_configs(self, config_names):
""" """
Get multiple configs at once, validating each one. Get multiple configs from the root table at once, validating each one with the
corresponding confdef stored in the ConfigManager.
Args: Args:
conf_defs: (dict) A dictionary of ConfigDefinitions. The keys are used conf_defs: A list of config names to get. If dictionary is supplied, uses the values
as the name to find each config dict, which is then validated against as ConfigDefinitions rather than looking up a stored one in the ConfigManager.
the corresponding conf def.
Returns: Returns:
A dict of config dicts, with keys matching those passed in ``conf_defs``. A dict of config dicts, with keys matching those passed in ``config_names``.
""" """
config_values = {} config_values = {}
for name, conf_def in conf_defs.items(): if isinstance(config_names, dict):
config_values[name] = self.get_config(name, conf_def) for name, conf_def in config_names.items():
config_values[name] = self.validate_and_get_config(name, conf_def)
else:
for name in config_names:
config_values[name] = self.validate_and_get_config(name)
return config_values return config_values
def get_plugin_configs(self, plugin_classes): def get_config_names(self):
config_values = {} """
for plugin_name, plugin_class in plugin_classes.items(): Returns a list of names of top level config tables
conf_def = ConfDefinition() """
plugin_class.define_config(conf_def) return list(self.root_config.keys())
config_values[plugin_name] = self.get_config(plugin_name, conf_def)
return config_values
def dump_toml(self): def dump_toml(self):
return toml.dumps(self.root_config) return toml.dumps(self.root_config)

Loading…
Cancel
Save