CastagnaIT/plugin.video.netflix

View on GitHub
resources/lib/common/pathops.py

Summary

Maintainability
A
0 mins
Test Coverage
# -*- 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[str(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