Source code for pywrapid.utils.dict_tools

#!/usr/bin/python3
"""
Collection of helper functions
"""
from copy import deepcopy
from typing import Optional


[docs] def dict_merge( base: dict, data: dict, path: Optional[list] = None, raise_on_conflict: bool = False ) -> dict: """Recursive merge of dict objects into new dict The merge will make a deep copy of the base dict and merge the data dict into it. It will not modify the base dict being passed in. If the same key exists in base and data, the value from data will be used unless raise_on_conflict is True, in which case a ValueError will be raised. Args: base (dict): The base dict to merge into data (dict): The data dict to merge into base path (list, optional): _description_. Defaults to None. raise_on_conflict (bool): Raise on conflict instead of overwriting base Raises: ValueError: Raised on leaf conflict when raise_on_conflict is True Returns: Merged dict """ copy = deepcopy(base) if path is None: path = [] for key in data: if key in copy: if isinstance(copy[key], dict) and isinstance(data[key], dict): copy[key] = dict_merge( copy[key], data[key], path=path + [str(key)], raise_on_conflict=raise_on_conflict, ) elif data[key] is None: continue elif copy[key] == data[key]: continue # same leaf value, skip traversal if children are the same else: if raise_on_conflict: raise ValueError(f"Conflict at {'.'.join(path + [str(key)])}") copy[key] = data[key] else: copy[key] = data[key] return copy
[docs] def dict_keys_exist( data: dict, expected_keys: list, allow_empty: bool = False, raise_on_fail: bool = True, ) -> bool: """Validates if expected keys exist (optionally; and has data) in a given dict. Args: data (dict): The dict to run validation on. expected_keys (list): List of keys that should exist. allow_empty (bool, optional): Is key object allowed to have empty value. raise_on_fail (bool, optional): Raise if validation fails. Raises: ValueError: Raised on all validation errors when raise_on_fail = True. Returns: bool: True when validation is passed. """ if not expected_keys: if raise_on_fail: raise ValueError("No expected values supplied") return False missed = [] for key in expected_keys: if key not in data: missed.append(key) elif data[key] in ["", (), [], {}, None] and allow_empty is False: if raise_on_fail: raise ValueError(f"Empty value in: {key}") return False if len(missed) > 0: if raise_on_fail: raise ValueError(f"Missing expected items: {missed}") return False return True