Skip to content

Instantly share code, notes, and snippets.

@LexVocoder
Created November 1, 2018 17:15
Show Gist options
  • Save LexVocoder/05522ae9098cbb9118bcbe53539b4622 to your computer and use it in GitHub Desktop.
Save LexVocoder/05522ae9098cbb9118bcbe53539b4622 to your computer and use it in GitHub Desktop.
# This is experimental!
def type_as_str(v):
return type(v).__name__
def deep_merge_mappings(a, b, *rest, sequence_behavior='append'):
"""
merges b into a and return merged result
sequence_behavior - specifies what happens if a & b are (or contain) non-string sequences. It must be one of the following:
'append': modifies a, appending b to it
'clobber': overwrites a with b
'error': raises TypeError in this situation
NOTE: tuples and arbitrary objects are not handled as it is totally ambiguous what should happen
"""
def helper(path, parameter_number, a, b, *rest):
def raise_type_error():
raise TypeError('cannot merge {type_as_str(b)} into {type_as_str(a)} in parameter {parameter_number}, path {path}'.format({
path=path, parameter_number=parameter_number, a=a, b=b
}))
key = None
# ## debug output
# sys.stderr.write("DEBUG: %s to %s\n" %(b,a))
try:
if a is None or isinstance(a, str) or isinstance(a, unicode) or isinstance(a, int) or isinstance(a, long) or isinstance(a, float):
# border case for first run or if a is a primitive
a = b
elif isinstance(a, list):
if sequence_behavior == 'append':
# lists can be only appended
if isinstance(b, list):
# merge lists
a.extend(b)
else:
# append to list
a.append(b)
elif sequence_behavior == 'clobber':
a = b
else:
raise_type_error()
elif isinstance(a, dict):
# mappings must be merged
if isinstance(b, dict):
for key in b:
if key in a:
a[key] = helper(path + '.' + key, parameter_number, a[key], b[key])
else:
a[key] = b[key]
else:
raise_type_error()
else:
raise_type_error()
except TypeError, e:
six.raise_from(TypeError('in parameter {parameter_number}, path {path}: {e}'.format(path=path, parameter_number=parameter_number, e=e)), e)
if len(rest) >= 1:
return helper(path, parameter_number + 1, a, rest[0], *rest[1:])
else:
return a
return helper('', 1, a, b, *rest)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment