diff options
Diffstat (limited to 'cloudinit/mergers/m_dict.py')
-rw-r--r-- | cloudinit/mergers/m_dict.py | 73 |
1 files changed, 51 insertions, 22 deletions
diff --git a/cloudinit/mergers/m_dict.py b/cloudinit/mergers/m_dict.py index 929d3865..2c1c845f 100644 --- a/cloudinit/mergers/m_dict.py +++ b/cloudinit/mergers/m_dict.py @@ -20,30 +20,59 @@ class Merger(object): def __init__(self, merger, opts): self._merger = merger - self._not_overwrite = 'not_overwrite' in opts + # Affects merging behavior... + self._method = 'replace' + for m in ['replace', 'no_replace']: + if m in opts: + self._method = m + break + # Affect how recursive merging is done on other primitives + self._recurse_str = 'recurse_str' in opts + self._recurse_dict = True + self._recurse_array = 'recurse_array' in opts + self._allow_delete = 'allow_delete' in opts + + def __str__(self): + s = ('DictMerger: (method=%s,recurse_str=%s,' + 'recurse_dict=%s,recurse_array=%s,allow_delete=%s)') + s = s % (self._method, + self._recurse_str, + self._recurse_dict, + self._recurse_array, + self._allow_delete) + return s + + def _do_dict_replace(self, value, merge_with, do_replace=True): + + def merge_same_key(old_v, new_v): + if do_replace: + return new_v + if isinstance(new_v, (list, tuple)) and self._recurse_array: + return self._merger.merge(old_v, new_v) + if isinstance(new_v, (str, basestring)) and self._recurse_str: + return self._merger.merge(old_v, new_v) + if isinstance(new_v, (dict)) and self._recurse_dict: + return self._merger.merge(old_v, new_v) + # Otherwise leave it be... + return old_v - # This merging algorithm will attempt to merge with - # another dictionary, on encountering any other type of object - # it will not merge with said object, but will instead return - # the original value - # - # On encountering a dictionary, it will create a new dictionary - # composed of the original and the one to merge with, if 'overwrite' - # is enabled then keys that exist in the original will be overwritten - # by keys in the one to merge with (and associated values). Otherwise - # if not in overwrite mode the 2 conflicting keys themselves will - # be merged. - def _on_dict(self, value, merge_with): - if not isinstance(merge_with, (dict)): - return value - merged = dict(value) for (k, v) in merge_with.items(): - if k in merged: - if self._not_overwrite: - # Attempt to merge them.... - merged[k] = self._merger.merge(merged[k], v) + if k in value: + if v is None and self._allow_delete: + value.pop(k) else: - merged[k] = v + value[k] = merge_same_key(value[k], v) else: - merged[k] = v + value[k] = v + return value + + def _on_dict(self, value, merge_with): + if not isinstance(merge_with, (dict)): + return value + if self._method == 'replace': + merged = self._do_dict_replace(dict(value), merge_with) + elif self._method == 'no_replace': + merged = self._do_dict_replace(dict(value), merge_with, False) + else: + raise NotImplementedError("Unknown merge type %s" % (self._method)) return merged |