Skip to content

Instantly share code, notes, and snippets.

@brianrusso
Created May 12, 2017 16:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brianrusso/a70b1384bf878d407df9d84b6a48f3a9 to your computer and use it in GitHub Desktop.
Save brianrusso/a70b1384bf878d407df9d84b6a48f3a9 to your computer and use it in GitHub Desktop.
# If add is True (default, it will add them, else it merges.
def deepdictmerge(a, b, path=None, add=True):
if path is None: path = []
for key in b: # for every key in b..
if key not in a: # handle simple case where it's new
a[key] = b[key]
else: # key must be in a and b
# both dictionaries.
if isinstance(a[key], dict) and isinstance(b[key], dict):
deepdictmerge(a[key], b[key], path + [str(key)]) # recurse down
# if one is a dict and the other has a key..
elif isinstance(a[key], dict) and key in b:
raise Exception("a is a dict, but b has key set and is not a dict")
# same deal.. switched
elif key in a and isinstance(b[key], dict):
raise Exception("b is a dict, but a has key set and is not a dict")
else: # key is in a and b.. neither are dictionaries
if add: # if we're adding.. it's all good
a[key] += b[key]
else: # we are doing a regular merge
if a[key] != b[key]: # they are both set.. and don't match
raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
else: # they are both set.. but match
pass
return a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment