Merge one dictionary with another, recursively.
    Fields present in addition will be added to base if not present or merged
    if both values are dictionaries or lists (with append_lists=True). If
    the values are different data types, the value in addition will be discarded.
    No data from base is deleted or overwritten.
    This function does not modify either dictionary.
    Dictionaries inside of other container types (list, etc.) are not merged,
    as the rules for merging would be ambiguous.
    If values from base and addition are of differing types, the value in
    addition is discarded.

    This utility could be expanded to merge Mapping and Container types in the future,
    but currently works only with dict and list.

    Arguments:
        base (dict): The dictionary being added to.
        addition (dict): The dictionary with additional data.
        append_lists (bool): When ``True``, fields present in base and addition
                that are lists will also be merged. Extra values from addition
                will be appended to the list in base.

    Returns:
        dict: The merged base.

def dict_merge(base, addition, append_lists=False):
    """Merge one dictionary with another, recursively.
    Fields present in addition will be added to base if not present or merged
    if both values are dictionaries or lists (with append_lists=True). If
    the values are different data types, the value in addition will be discarded.
    No data from base is deleted or overwritten.
    This function does not modify either dictionary.
    Dictionaries inside of other container types (list, etc.) are not merged,
    as the rules for merging would be ambiguous.
    If values from base and addition are of differing types, the value in
    addition is discarded.

    This utility could be expanded to merge Mapping and Container types in the future,
    but currently works only with dict and list.

    Arguments:
        base (dict): The dictionary being added to.
        addition (dict): The dictionary with additional data.
        append_lists (bool): When ``True``, fields present in base and addition
                that are lists will also be merged. Extra values from addition
                will be appended to the list in base.

    Returns:
        dict: The merged base.
    """
    if not isinstance(base, dict) or not isinstance(addition, dict):
        raise TypeError("dict_merge only works with dicts.")

    new_base = deepcopy(base)
    for key, value in addition.items():
        # Simplest case: Key not in base, so add value to base
        if key not in new_base.keys():
            new_base[key] = value
        # If the value is a dict, and base's value is also a dict, merge
        # If there is a type disagreement, merging cannot and should not happen
        if isinstance(value, dict) and isinstance(new_base[key], dict):
            new_base[key] = dict_merge(new_base[key], value)
        # If value is a list, lists should be merged, and base is compatible
        elif append_lists and isinstance(value, list) and isinstance(new_base[key], list):
            new_list = deepcopy(new_base[key])
            [new_list.append(item) for item in value if item not in new_list]
            new_base[key] = new_list
        # If none of these trigger, discard value from addition implicitly

    return new_base