Add configlayers.py.
This commit is contained in:
parent
fb61212395
commit
39c09be237
1 changed files with 95 additions and 0 deletions
95
voussoirkit/configlayers.py
Normal file
95
voussoirkit/configlayers.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
'''
|
||||
The purpose of this file is to work with JSON-based config files, where you
|
||||
load a default configuration, then overlay a user-supplied configuration on
|
||||
top, overwriting the matching keys and keeping default values for the rest.
|
||||
The functions will then suggest that the user config needs to be re-saved if
|
||||
the default key set contains keys that the user key set does not.
|
||||
'''
|
||||
import copy
|
||||
import json
|
||||
|
||||
from voussoirkit import pathclass
|
||||
|
||||
def recursive_dict_keys(d):
|
||||
'''
|
||||
Given a dictionary, return a set containing all of its keys and the keys of
|
||||
all other dictionaries that appear as values within. The subkeys will use \\
|
||||
to indicate their lineage.
|
||||
|
||||
{'hi': {'ho': 'neighbor'}}
|
||||
|
||||
returns
|
||||
|
||||
{'hi', 'hi\\ho'}
|
||||
'''
|
||||
keys = set(d.keys())
|
||||
for (key, value) in d.items():
|
||||
if isinstance(value, dict):
|
||||
subkeys = {f'{key}\\{subkey}' for subkey in recursive_dict_keys(value)}
|
||||
keys.update(subkeys)
|
||||
return keys
|
||||
|
||||
def recursive_dict_update(target, supply):
|
||||
'''
|
||||
Update target using supply, but when the value is a dictionary update the
|
||||
insides instead of replacing the dictionary itself. This prevents keys that
|
||||
exist in the target but don't exist in the supply from being erased.
|
||||
Note that we are modifying target in place.
|
||||
|
||||
eg:
|
||||
target = {'hi': 'ho', 'neighbor': {'name': 'Wilson'}}
|
||||
supply = {'neighbor': {'behind': 'fence'}}
|
||||
|
||||
result: {'hi': 'ho', 'neighbor': {'name': 'Wilson', 'behind': 'fence'}}
|
||||
whereas a regular dict.update would have produced:
|
||||
{'hi': 'ho', 'neighbor': {'behind': 'fence'}}
|
||||
'''
|
||||
for (key, value) in supply.items():
|
||||
if isinstance(value, dict):
|
||||
existing = target.get(key, None)
|
||||
if existing is None:
|
||||
target[key] = value
|
||||
else:
|
||||
recursive_dict_update(target=existing, supply=value)
|
||||
else:
|
||||
target[key] = value
|
||||
|
||||
def layer_json(target, supply):
|
||||
'''
|
||||
target is the dictionary into which the final values will be placed
|
||||
(presumably loaded from a default set), and supply is a layer being applied
|
||||
on top of the target (presumably a user-supplied set). needs_rewrite is
|
||||
True if the target contains keys that the supply did not, indicating that
|
||||
the supply is incomplete.
|
||||
'''
|
||||
target_keys = recursive_dict_keys(target)
|
||||
supply_keys = recursive_dict_keys(supply)
|
||||
needs_rewrite = target_keys > supply_keys
|
||||
recursive_dict_update(target=target, supply=supply)
|
||||
return (target, needs_rewrite)
|
||||
|
||||
def load_file(filepath, defaults):
|
||||
'''
|
||||
Given a filepath to a user-supplied config file, and a dict of default
|
||||
values, return a new dict containing the user-supplied values overlaid onto
|
||||
the defaults, and needs_rewrite indicating that the user config is missing
|
||||
some keys from the default config.
|
||||
'''
|
||||
path = pathclass.Path(filepath)
|
||||
user_config_exists = path.exists
|
||||
|
||||
# This config will hold the final values. We start by loading it with the
|
||||
# defaults, so that as we go through the user's config we can overwrite the
|
||||
# user-specified keys, and the keys which the user does not specify will
|
||||
# remain default.
|
||||
config = copy.deepcopy(defaults)
|
||||
needs_rewrite = False
|
||||
|
||||
if user_config_exists:
|
||||
with open(path.absolute_path, 'r', encoding='utf-8') as handle:
|
||||
user_config = json.load(handle)
|
||||
(config, needs_rewrite) = layer_json(config, user_config)
|
||||
else:
|
||||
needs_rewrite = True
|
||||
|
||||
return (config, needs_rewrite)
|
Loading…
Reference in a new issue