Source code for zensols.config.diff
"""A class to diff configurations.
Using this module requires the ``deepdiff`` package::
pip install deepdiff
"""
__author__ = 'Paul Landes'
from typing import Dict
import re
import collections
from zensols.util import APIError
from . import Configurable, DictionaryConfig
[docs]
class ConfigurableDiffer(DictionaryConfig):
"""A class to diff configurations. Each section of configuration contains
properties of the changed options.
"""
_SEC_PROP_REGEX = re.compile(r"^root\['(.+)'\]\['(.+)'\]$")
[docs]
def __init__(self, config_a: Configurable, config_b: Configurable,
change_format: str = '{} -> {}'):
super().__init__()
self._config_a = config_a
self._config_b = config_b
self._change_format = change_format
self._init = False
def _diff(self) -> DictionaryConfig:
try:
from deepdiff import DeepDiff
except ModuleNotFoundError as e:
m = "DeepDiff module is not installed, use: 'pip install deepdiff'"
raise APIError(m) from e
da = DictionaryConfig.from_config(self._config_a)
db = DictionaryConfig.from_config(self._config_b)
dd = DeepDiff(da.asdict(), db.asdict())
if 'values_changed' not in dd:
keys = ', '.join(dd.keys())
raise APIError(f'No values changed, found keys: {keys}')
vc = dd['values_changed']
changes = collections.defaultdict(dict)
for sec_prop, vals in vc.items():
m: re.Match = self._SEC_PROP_REGEX.match(sec_prop)
if m is None:
raise ValueError(f'Unknown diff format: {sec_prop}')
sec, prop = m.groups()
cstr = self._change_format.format(
vals['old_value'], vals['new_value'])
changes[sec][prop] = cstr
return DictionaryConfig(changes)
def _get_config(self) -> Dict[str, Dict[str, str]]:
if not self._init:
self._dict_config = self._diff().asdict()
self._init = True
return super()._get_config()