Source code for aiida_vasp.utils.dict_merge

"""
Functions to merge dictionaries
"""

import collections
from copy import deepcopy


[docs] def recursive_merge_orig(left: dict, right: dict) -> dict: """Recursively merge two dictionaries into a single dictionary. If any key is present in both ``left`` and ``right`` dictionaries, the value from the ``right`` dictionary is assigned to the key. :param left: first dictionary :param right: second dictionary :return: the recursively merged dictionary """ # Note that a deepcopy is not necessary, since this function is called recusively. right = right.copy() for key, value in left.items(): if key in right: if isinstance(value, collections.abc.Mapping) and isinstance(right[key], collections.abc.Mapping): right[key] = recursive_merge_orig(value, right[key]) merged = left.copy() merged.update(right) return merged
[docs] def recursive_merge(left: dict, right: dict) -> dict: """ Recursively merge two dictionaries into a single dictionary, supporting special operations for keys. If a key is present in both ``left`` and ``right`` dictionaries, the value from ``right`` is used. If both values are dictionaries, they are merged recursively. Special operations (when the value in ``right`` is a dictionary with one of these keys): - ``$!del``: Delete this key from the result. - ``$!append``: Append a value to a list at this key. - ``$!extend``: Extend a list at this key with another list. - ``$!replace``: Replace the value at this key entirely. :param dict left: First dictionary. :param dict right: Second dictionary. :return: The recursively merged dictionary. :rtype: dict **Examples** Basic merge:: >>> recursive_merge({'a': 1, 'b': {'c': 2}}, {'b': {'c': 3}, 'd': 4}) {'a': 1, 'b': {'c': 3}, 'd': 4} Nested merge and list extend:: >>> recursive_merge({'a': {'x': 1, 'y': {'z': 2}}, 'b': [1, 2]}, {'a': {'y': {'z': 3}}, 'b': {'$!extend': [3, 4]}}) {'a': {'x': 1, 'y': {'z': 3}}, 'b': [1, 2, 3, 4]} Special: append and delete:: >>> recursive_merge({'a': [1, 2], 'b': {'c': 5}}, {'a': {'$!append': 3}, 'b': {'$!del': True}}) {'a': [1, 2, 3]} >>> recursive_merge({'a': [1, 2], 'b': {'c': 5}}, {'a': {'$!append': 3}, 'b': '$!del'}}) {'a': [1, 2, 3]} Replace:: >>> recursive_merge({'a': {'x': 1}}, {'a': {'$!replace': {'y': 2}}}) {'a': {'y': 2}} """ # Here a deepcopy is necessary as in-place modification is used left = deepcopy(left) for key, value_right in right.items(): if key in left: # Apply special operations if '$!del' == value_right: del left[key] continue if isinstance(value_right, dict): if '$!del' in value_right: del left[key] continue if '$!append' in value_right: left[key] = left[key].copy() left[key].append(value_right['$!append']) continue if '$!extend' in value_right: left[key] = left[key].copy() left[key].extend(value_right['$!extend']) continue if '$!replace' in value_right: left[key] = value_right['$!replace'] continue # Nested update if isinstance(value_right, collections.abc.Mapping) and isinstance(left[key], collections.abc.Mapping): left[key] = recursive_merge(left[key], value_right) else: left[key] = value_right else: left[key] = value_right return left