# -*- coding: utf-8 -*- """ Copyright (C) 2017 Sebastian Golasch (plugin.video.netflix) Copyright (C) 2018 Caphm (original implementation module) Helper functions for retrieving values from nested dicts SPDX-License-Identifier: MIT See LICENSES/MIT.md for more information. """ def get_path(path, search_space, include_key=False): """Retrieve a value from a nested dict by following the path. Throws KeyError if any key along the path does not exist""" if not isinstance(path, (tuple, list)): path = [path] current_value = search_space[path[0]] if len(path) == 1: return (path[0], current_value) if include_key else current_value return get_path(path[1:], current_value, include_key) def get_path_safe(path, search_space, include_key=False, default=None): """Retrieve a value from a nested dict by following the path. Returns default if any key in the path does not exist.""" try: return get_path(path, search_space, include_key) except (KeyError, IndexError): return default def remove_path(path, search_space, is_empty, remove_remnants=True): """Remove a value from a nested dict by following a path. Also removes remaining empty dicts in the hierarchy if remove_remnants is True""" path = _ensure_listlike(path) if len(path) == 1: del search_space[path[0]] else: remove_path(path[1:], search_space[path[0]], is_empty) if remove_remnants and is_empty(search_space[path[0]]): del search_space[path[0]] def _ensure_listlike(path): return path if isinstance(path, (tuple, list)) else [path] def get_multiple_paths(path, search_space, default=None): """Retrieve multiple values from a nested dict by following the path. The path may branch into multiple paths at any point. A branch point is a list of different keys to follow down the path. Returns a nested dict structure with nested dicts for each branch point in the path. This essentially reduces the original nested dict by removing those layers that only have one key and keys not specified in the branch point. Keys specified in branch points that do not exist in the search space are silently ignored""" if not isinstance(search_space, (dict, list)): return default if isinstance(path[0], list): return _branch(path, search_space, default) current_value = search_space.get(path[0], default) return (current_value if len(path) == 1 else get_multiple_paths(path[1:], current_value, default)) def _branch(path, search_space, default): return {k: get_multiple_paths([k] + path[1:], search_space, default) for k in path[0] if k in search_space} def check_path_exists(path, search_space): """Return True if the path exists in search_space""" if not isinstance(search_space, dict): return False if path[0] in search_space: if len(path) > 1: current_value = search_space.get(path[0]) return check_path_exists(path[1:], current_value) return True return False