from typing import Dict class InvalidKeyError(Exception): def __init__(self, key): self.message = "Invalid key: {}".format(key) super().__init__(key) class MissingKeyError(Exception): def __init__(self, key): self.message = "Missing key: {}".format(key) super().__init__(key) def merge(*dicts: [Dict]) -> Dict: # Base cases if len(dicts) == 0: return {} if len(dicts) == 1: return dicts[0] # We merge the first two dicts d1, d2 = dicts[0], dicts[1] output = d1.copy() for key, value in d2.items(): if type(value) == dict: # Merge the two sub-dictionaries output[key] = ( merge(output[key], value) if type(output.get(key)) == dict else value ) else: output[key] = value return merge(output, *dicts[2:]) def merge_with_skeleton(data: Dict, skel: Dict) -> Dict: """ Compare a dict with a given skeleton dict, and fill in default values where needed. """ # First, check for illegal keys for key in data: if key not in skel: raise InvalidKeyError(key) # Then, check the default values for key, value in skel.items(): if key not in data: # Raise error if there's not default value if value is None: raise MissingKeyError(key) # Replace with default value data[key] = value # Error if value is not same type as default value elif type(data[key]) != type(value) and value is not None: raise TypeError("Invalid value type") # Recurse into dicts elif type(value) == dict: data[key] = merge_with_skeleton(data[key], value) return data