summaryrefslogtreecommitdiff
path: root/cloudinit/mergers/m_dict.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/mergers/m_dict.py')
-rw-r--r--cloudinit/mergers/m_dict.py73
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