1
0
mirror of https://github.com/yt-dlp/yt-dlp synced 2025-07-09 07:13:51 -05:00

[utils] traverse_obj: Always return list when branching (#5170)

Fixes #5162
Authored by: Grub4K
This commit is contained in:
Simon Sawicki
2022-10-09 03:27:32 +02:00
committed by GitHub
parent 3b55aaac59
commit f99bbfc983
2 changed files with 37 additions and 12 deletions

View File

@ -5294,7 +5294,7 @@ def load_plugins(name, suffix, namespace):
def traverse_obj(
obj, *paths, default=None, expected_type=None, get_all=True,
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
@ -5304,6 +5304,7 @@ def traverse_obj(
"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', )`.
@ -5342,6 +5343,7 @@ def traverse_obj(
@returns The result of the object traversal.
If successful, `get_all=True`, and the path branches at least once,
then a list of results is returned instead.
A list is always returned if the last path branches and no `default` is given.
"""
is_sequence = lambda x: isinstance(x, collections.abc.Sequence) and not isinstance(x, (str, bytes))
casefold = lambda k: k.casefold() if isinstance(k, str) else k
@ -5385,7 +5387,7 @@ def traverse_obj(
elif isinstance(key, dict):
iter_obj = ((k, _traverse_obj(obj, v)) for k, v in key.items())
yield {k: v if v is not None else default for k, v in iter_obj
if v is not None or default is not None}
if v is not None or default is not NO_DEFAULT}
elif isinstance(obj, dict):
yield (obj.get(key) if casesense or (key in obj)
@ -5426,18 +5428,22 @@ def traverse_obj(
return has_branched, objs
def _traverse_obj(obj, path):
def _traverse_obj(obj, path, use_list=True):
has_branched, results = apply_path(obj, path)
results = LazyList(x for x in map(type_test, results) if x is not None)
if results:
return results.exhaust() if get_all and has_branched else results[0]
for path in paths:
result = _traverse_obj(obj, path)
if get_all and has_branched:
return results.exhaust() if results or use_list else None
return results[0] if results else None
for index, path in enumerate(paths, 1):
use_list = default is NO_DEFAULT and index == len(paths)
result = _traverse_obj(obj, path, use_list)
if result is not None:
return result
return default
return None if default is NO_DEFAULT else default
def traverse_dict(dictn, keys, casesense=True):