From a9df16db4720d4b7b4514cb3e2ea6e22bd9dac94 Mon Sep 17 00:00:00 2001 From: garret1317 Date: Mon, 11 Aug 2025 08:20:24 +0100 Subject: [PATCH 1/5] traverse_obj: allow traversal of dataclasses (for protobug) --- yt_dlp/utils/traversal.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yt_dlp/utils/traversal.py b/yt_dlp/utils/traversal.py index 76b51f53d1..ce2ed186b5 100644 --- a/yt_dlp/utils/traversal.py +++ b/yt_dlp/utils/traversal.py @@ -3,6 +3,7 @@ from __future__ import annotations import collections import collections.abc import contextlib +import dataclasses import functools import http.cookies import inspect @@ -233,6 +234,8 @@ def traverse_obj( result = list(map(apply_specials, obj.iterfind(xpath))) else: result = apply_specials(obj) + elif dataclasses.is_dataclass(obj): + result = getattr(obj, key) return branching, result if branching else (result,) From 0d71d6f4190e0adc044244b43141b6ac8faf5cd7 Mon Sep 17 00:00:00 2001 From: garret1317 Date: Mon, 18 Aug 2025 14:56:09 +0100 Subject: [PATCH 2/5] revert prev a9df16db4720d4b7b4514cb3e2ea6e22bd9dac94 --- yt_dlp/utils/traversal.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/yt_dlp/utils/traversal.py b/yt_dlp/utils/traversal.py index ce2ed186b5..76b51f53d1 100644 --- a/yt_dlp/utils/traversal.py +++ b/yt_dlp/utils/traversal.py @@ -3,7 +3,6 @@ from __future__ import annotations import collections import collections.abc import contextlib -import dataclasses import functools import http.cookies import inspect @@ -234,8 +233,6 @@ def traverse_obj( result = list(map(apply_specials, obj.iterfind(xpath))) else: result = apply_specials(obj) - elif dataclasses.is_dataclass(obj): - result = getattr(obj, key) return branching, result if branching else (result,) From 593b08e376f367b5d8efc14f15955f72f06ee681 Mon Sep 17 00:00:00 2001 From: garret1317 Date: Mon, 18 Aug 2025 14:57:05 +0100 Subject: [PATCH 3/5] convert the dataclass to a dict to get everything for free :) --- yt_dlp/utils/traversal.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yt_dlp/utils/traversal.py b/yt_dlp/utils/traversal.py index 76b51f53d1..65a94ccb9e 100644 --- a/yt_dlp/utils/traversal.py +++ b/yt_dlp/utils/traversal.py @@ -3,6 +3,7 @@ from __future__ import annotations import collections import collections.abc import contextlib +import dataclasses import functools import http.cookies import inspect @@ -116,6 +117,9 @@ def traverse_obj( branching = False result = None + if dataclasses.is_dataclass(obj): + obj = dataclasses.asdict(obj) + if obj is None and traverse_string: if key is ... or callable(key) or isinstance(key, slice): branching = True From 5b3dbcfa49cbcbe1b7fd064a5555b81757074a2a Mon Sep 17 00:00:00 2001 From: garret1317 Date: Mon, 18 Aug 2025 22:37:58 +0100 Subject: [PATCH 4/5] add basic dataclass tests (just a string) --- test/test_traversal.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test_traversal.py b/test/test_traversal.py index 52215f5a7b..a67f91e918 100644 --- a/test/test_traversal.py +++ b/test/test_traversal.py @@ -1,4 +1,5 @@ import http.cookies +import dataclasses import re import xml.etree.ElementTree @@ -439,6 +440,17 @@ class TestTraversal: assert traverse_obj(data, [..., filter]) == [True, 1, 1.1, 'str', {0: 0}, [1]], \ '`filter` should filter falsy values' + def test_traversal_dataclass(self): + @dataclasses.dataclass + class _TEST_DATACLASS: + string: str + + dataclass = _TEST_DATACLASS(string='yt-dlp') + assert traverse_obj(dataclass, ('string')) == 'yt-dlp', \ + 'Dataclasses should be traversable' + assert traverse_obj({'dataclass': dataclass}, ('dataclass', 'string')) == 'yt-dlp', \ + 'Dataclasses inside other objects should be traversable' + class TestTraversalHelpers: def test_traversal_require(self): From 7ffe9009c719a7ae9292fe120ab26be2b4335de4 Mon Sep 17 00:00:00 2001 From: garret1317 Date: Mon, 18 Aug 2025 22:55:29 +0100 Subject: [PATCH 5/5] Update test/test_traversal.py Co-authored-by: bashonly <88596187+bashonly@users.noreply.github.com> --- test/test_traversal.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_traversal.py b/test/test_traversal.py index a67f91e918..f78c0db4af 100644 --- a/test/test_traversal.py +++ b/test/test_traversal.py @@ -442,13 +442,13 @@ class TestTraversal: def test_traversal_dataclass(self): @dataclasses.dataclass - class _TEST_DATACLASS: - string: str + class _TestDataclass: + val: str - dataclass = _TEST_DATACLASS(string='yt-dlp') - assert traverse_obj(dataclass, ('string')) == 'yt-dlp', \ + dc = _TestDataclass(val='yt-dlp') + assert traverse_obj(dc, 'val') == 'yt-dlp', \ 'Dataclasses should be traversable' - assert traverse_obj({'dataclass': dataclass}, ('dataclass', 'string')) == 'yt-dlp', \ + assert traverse_obj({'dataclass': dc}, ('dataclass', 'val')) == 'yt-dlp', \ 'Dataclasses inside other objects should be traversable'