aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordirkf <fieldhouse@gmx.net>2025-10-31 12:20:26 +0000
committerdirkf <fieldhouse@gmx.net>2025-11-21 01:52:11 +0000
commit96419fa7064c7f77ccb1909e23150fde603f9f36 (patch)
treefd85f62f91de11c2887dc7c102203bb8b3d1eab7
parentcca41c9d2ca51fbfdc9a8c16f2f7b049b577300b (diff)
[utils] Support `filter` traversal key
Thx yt-dlp/yt-dlp#10653
-rw-r--r--test/test_traversal.py8
-rw-r--r--youtube_dl/compat.py6
-rw-r--r--youtube_dl/utils.py8
3 files changed, 22 insertions, 0 deletions
diff --git a/test/test_traversal.py b/test/test_traversal.py
index 2987970ba..21f81136f 100644
--- a/test/test_traversal.py
+++ b/test/test_traversal.py
@@ -473,6 +473,14 @@ class TestTraversal(_TestCase):
self.assertIs(traverse_obj(morsel, [(None,), any]), morsel,
msg='Morsel should not be implicitly changed to dict on usage')
+ def test_traversal_filter(self):
+ data = [None, False, True, 0, 1, 0.0, 1.1, '', 'str', {}, {0: 0}, [], [1]]
+
+ self.assertEqual(
+ traverse_obj(data, (Ellipsis, filter)),
+ [True, 1, 1.1, 'str', {0: 0}, [1]],
+ '`filter` should filter falsy values')
+
def test_get_first(self):
self.assertEqual(get_first([{'a': None}, {'a': 'spam'}], 'a'), 'spam')
diff --git a/youtube_dl/compat.py b/youtube_dl/compat.py
index ebe22bdf9..96b099a58 100644
--- a/youtube_dl/compat.py
+++ b/youtube_dl/compat.py
@@ -3452,6 +3452,8 @@ except ImportError:
except ImportError:
compat_map = map
+
+# compat_filter, compat_filter_fns
try:
from future_builtins import filter as compat_filter
except ImportError:
@@ -3459,6 +3461,9 @@ except ImportError:
from itertools import ifilter as compat_filter
except ImportError:
compat_filter = filter
+# "Is this function one or maybe the other filter()?"
+compat_filter_fns = tuple(set((filter, compat_filter)))
+
# compat_zip
try:
@@ -3675,6 +3680,7 @@ __all__ = [
'compat_etree_fromstring',
'compat_etree_iterfind',
'compat_filter',
+ 'compat_filter_fns',
'compat_get_terminal_size',
'compat_getenv',
'compat_getpass_getpass',
diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py
index c4262936e..29d62130a 100644
--- a/youtube_dl/utils.py
+++ b/youtube_dl/utils.py
@@ -53,6 +53,8 @@ from .compat import (
compat_etree_fromstring,
compat_etree_iterfind,
compat_expanduser,
+ compat_filter as filter,
+ compat_filter_fns,
compat_html_entities,
compat_html_entities_html5,
compat_http_client,
@@ -6283,6 +6285,7 @@ def traverse_obj(obj, *paths, **kwargs):
Read as: `{key: traverse_obj(obj, path) for key, path in dct.items()}`.
- `any`-builtin: Take the first matching object and return it, resetting branching.
- `all`-builtin: Take all matching objects and return them as a list, resetting branching.
+ - `filter`-builtin: Return the value if it is truthy, `None` otherwise.
`tuple`, `list`, and `dict` all support nested paths and branches.
@@ -6497,6 +6500,11 @@ def traverse_obj(obj, *paths, **kwargs):
objs = (list(filtered_objs),)
continue
+ # filter might be from __builtin__, future_builtins, or itertools.ifilter
+ if key in compat_filter_fns:
+ objs = filter(None, objs)
+ continue
+
if __debug__ and callable(key):
# Verify function signature
_try_bind_args(key, None, None)