Repository: yt-dlp/yt-dlp
File: yt_dlp/utils.py (Python)

Code before:
        plugins = importlib.util.module_from_spec(plugins_spec)
        sys.modules[plugins_spec.name] = plugins
        plugins_spec.loader.exec_module(plugins)
        for name in dir(plugins):
            if name in namespace:
                continue
            if not name.endswith(suffix):
                continue
            klass = getattr(plugins, name)
            classes[name] = namespace[name] = klass
    return classes


def traverse_obj(
        obj, *paths, default=NO_DEFAULT, expected_type=None, get_all=True,
        casesense=True, is_user_input=False, traverse_string=False):
    """
    Safely traverse nested `dict`s and `Sequence`s

    >>> obj = [{}, {"key": "value"}]
    >>> traverse_obj(obj, (1, "key"))
    "value"

    Each of the provided `paths` is tested and the first producing a valid result will be returned.
    The next path will also be tested if the path branched but no results could be found.
    If the branching path is the last of `paths` always return a list.
    A value of None is treated as the absence of a value.

    The paths will be wrapped in `variadic`, so that `'key'` is conveniently the same as `('key', )`.

    The keys in the path can be one of:
        - `None`:           Return the current object.
        - `str`/`int`:      Return `obj[key]`.
        - `slice`:          Branch out and return all values in `obj[key]`.
        - `Ellipsis`:       Branch out and return a list of all values.
        - `tuple`/`list`:   Branch out and return a list of all matching values.
                            Read as: `[traverse_obj(obj, branch) for branch in branches]`.
        - `function`:       Branch out and return values filtered by the function.
                            Read as: `[value for key, value in obj if function(key, value)]`.
                            For `Sequence`s, `key` is the index of the value.
        - `dict`            Transform the current object and return a matching dict.
                            Read as: `{key: traverse_obj(obj, path) for key, path in dct.items()}`.

        `tuple`, `list`, and `dict` all support nested paths and branches

    @params paths           Paths which to traverse by.
    @param default          Value to return if the paths do not match.
    @param expected_type    If a `type`, only accept final values of this type.
                            If any other callable, try to call the function on each result.
    @param get_all          If `False`, return the first matching result, otherwise all matching ones.
    @param casesense        If `False`, consider string dictionary keys as case insensitive.

Review comment (suggestion):
Instead, update the @returns imo
```
    @returns                The result of the object traversal.
                            If `get_all=True`, and the last path branches,
                            then a list of results is returned instead.
```

Code after:
        plugins = importlib.util.module_from_spec(plugins_spec)
        sys.modules[plugins_spec.name] = plugins
        plugins_spec.loader.exec_module(plugins)
        for name in dir(plugins):
            if name in namespace:
                continue
            if not name.endswith(suffix):
                continue
            klass = getattr(plugins, name)
            classes[name] = namespace[name] = klass
    return classes


def traverse_obj(
        obj, *paths, default=NO_DEFAULT, expected_type=None, get_all=True,
        casesense=True, is_user_input=False, traverse_string=False):
    """
    Safely traverse nested `dict`s and `Sequence`s

    >>> obj = [{}, {"key": "value"}]
    >>> traverse_obj(obj, (1, "key"))
    "value"

    Each of the provided `paths` is tested and the first producing a valid result will be returned.
    The next path will also be tested if the path branched but no results could be found.
    A value of None is treated as the absence of a value.

    The paths will be wrapped in `variadic`, so that `'key'` is conveniently the same as `('key', )`.

    The keys in the path can be one of:
        - `None`:           Return the current object.
        - `str`/`int`:      Return `obj[key]`.
        - `slice`:          Branch out and return all values in `obj[key]`.
        - `Ellipsis`:       Branch out and return a list of all values.
        - `tuple`/`list`:   Branch out and return a list of all matching values.
                            Read as: `[traverse_obj(obj, branch) for branch in branches]`.
        - `function`:       Branch out and return values filtered by the function.
                            Read as: `[value for key, value in obj if function(key, value)]`.
                            For `Sequence`s, `key` is the index of the value.
        - `dict`            Transform the current object and return a matching dict.
                            Read as: `{key: traverse_obj(obj, path) for key, path in dct.items()}`.

        `tuple`, `list`, and `dict` all support nested paths and branches

    @params paths           Paths which to traverse by.
    @param default          Value to return if the paths do not match.
    @param expected_type    If a `type`, only accept final values of this type.
                            If any other callable, try to call the function on each result.
    @param get_all          If `False`, return the first matching result, otherwise all matching ones.
    @param casesense        If `False`, consider string dictionary keys as case insensitive.