|
|
|
@ -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)
|
|
|
|
|