Rewritten dcd.cfg.Config
Config now supports full hierarchical and overlay-capable usage. A generic tree overlay function is in aux.
This commit is contained in:
parent
139381e7fc
commit
d72659088f
3 changed files with 54 additions and 31 deletions
29
dcd/aux.py
29
dcd/aux.py
|
@ -77,3 +77,32 @@ def hex_to_raw(text, separator=" "):
|
||||||
return bytearray(output)
|
return bytearray(output)
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
class DeleteOverlay:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def overlay_tree(base, overlay):
|
||||||
|
"""
|
||||||
|
For every key path in `overlay`, sets the leaf value of the same
|
||||||
|
key path in `base`. Any missing non-leaf keys will be created.
|
||||||
|
|
||||||
|
To delete a key, set its overlay value to DeleteOverlay.
|
||||||
|
"""
|
||||||
|
|
||||||
|
stack = [(base, overlay)]
|
||||||
|
|
||||||
|
while stack:
|
||||||
|
base_p, over_p = stack.pop()
|
||||||
|
|
||||||
|
for k, v in over_p.items():
|
||||||
|
if v is DeleteOverlay:
|
||||||
|
del base_p[k]
|
||||||
|
else:
|
||||||
|
if type(v) is dict:
|
||||||
|
if k not in base_p or type(base_p[k]) is not dict:
|
||||||
|
base_p[k] = {}
|
||||||
|
|
||||||
|
stack.append((base_p[k], over_p[k]))
|
||||||
|
|
||||||
|
else:
|
||||||
|
base_p[k] = v
|
||||||
|
|
56
dcd/cfg.py
56
dcd/cfg.py
|
@ -4,6 +4,18 @@
|
||||||
import jsmin
|
import jsmin
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from . import aux
|
||||||
|
|
||||||
|
def _read_json(path):
|
||||||
|
"""
|
||||||
|
Reads a data structure from a JSON file pointed to by `path`.
|
||||||
|
|
||||||
|
Removes any C/JS-like comments from it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
with open(path) as f:
|
||||||
|
return json.loads(jsmin.jsmin(f.read()))
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
"""
|
"""
|
||||||
A hierarchical key-value container that can be loaded from JSON or a Python dict.
|
A hierarchical key-value container that can be loaded from JSON or a Python dict.
|
||||||
|
@ -11,48 +23,30 @@ class Config:
|
||||||
Any contained dicts are automatically converted to Config instances as well.
|
Any contained dicts are automatically converted to Config instances as well.
|
||||||
|
|
||||||
Supports sequential overwriting - so one can load a default config and then
|
Supports sequential overwriting - so one can load a default config and then
|
||||||
progressively overwrite it with overlays:
|
progressively overlay more on top:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
>>> raw
|
|
||||||
{'a': 1, 'b': {'ba': 2, 'bb': 3, 'bc': {'bca': None}}, 'c': 4}
|
|
||||||
>>> overlay
|
|
||||||
{'b': {'bc': {'bca': 42}}}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
while stack:
|
|
||||||
current = stack[-1]
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
stack.pop()
|
|
||||||
|
|
||||||
for key in overlay.keys():
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>>> c = Config.from_file("/usr/lib/app/default.cfg")
|
||||||
|
>>> c.update_from_file("/etc/app/config.cfg")
|
||||||
|
>>> c.update({"key", value, "subtree": {"key": "value"}})
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, d, readonly=False):
|
def __init__(self, d=None, readonly=False):
|
||||||
|
if d is None:
|
||||||
|
d = {}
|
||||||
|
|
||||||
assert type(d) == dict
|
assert type(d) == dict
|
||||||
object.__setattr__(self, "raw", d)
|
object.__setattr__(self, "raw", d)
|
||||||
object.__setattr__(self, "readonly", readonly)
|
object.__setattr__(self, "readonly", readonly)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_file(cls, path, readonly=False):
|
def from_file(cls, path, readonly=False):
|
||||||
with open(path) as f:
|
return cls(_read_json(path), readonly)
|
||||||
initial = json.loads(jsmin.jsmin(f.read()))
|
|
||||||
|
|
||||||
return cls(initial, readonly)
|
|
||||||
|
|
||||||
def update(self, d):
|
def update(self, d):
|
||||||
|
aux.overlay_tree(self.raw, d)
|
||||||
|
|
||||||
|
def update_from_file(self, path):
|
||||||
|
self.update(_read_json(path))
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
return self.raw.keys()
|
return self.raw.keys()
|
||||||
|
|
0
setup.py
Normal file → Executable file
0
setup.py
Normal file → Executable file
Loading…
Add table
Add a link
Reference in a new issue