From 5bc4a747dd50d94dee4356e03039d471398dd758 Mon Sep 17 00:00:00 2001 From: pratyu2h Date: Tue, 3 Nov 2026 13:25:14 +0530 Subject: [PATCH] fix(dumpkit): preserve renamed builtin keys in fields() (#2733) Signed-off-by: pratyu2h --- pcapkit/dumpkit/common.py | 7 +++++-- tests/dumpkit/test_common_unit.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pcapkit/dumpkit/common.py b/pcapkit/dumpkit/common.py index 4e9e6b33b4..0f4924ed1c 100644 --- a/pcapkit/dumpkit/common.py +++ b/pcapkit/dumpkit/common.py @@ -440,11 +440,14 @@ def fields(info: 'Info | MultiDict[Any, Any]') -> 'dict[Any, Any]': extension header, maps to the :class:`list` of all its values, in order (GitHub issue :issue:`1484`). The values are left as they are, so each comes back through :meth:`DictDumper.object_hook` when - written. The keys :func:`excluded` names are left out. + The keys :func:`excluded` names are left out, while a key + renamed for clashing with a builtin name is kept under its original + name (:issue:`2733`). """ skip = excluded(info) - pairs = [(key, val) for key, val in info.items(multi=True) if key not in skip] + keymap = getattr(info, '__map__', {}) + pairs = [(key, val) for key, val in info.items(multi=True) if keymap.get(key, key) not in skip] counts = collections.Counter(key for key, _ in pairs) result = {} # type: dict[Any, Any] for key, val in pairs: diff --git a/tests/dumpkit/test_common_unit.py b/tests/dumpkit/test_common_unit.py index 53696b05c9..b75f24d5eb 100644 --- a/tests/dumpkit/test_common_unit.py +++ b/tests/dumpkit/test_common_unit.py @@ -225,6 +225,23 @@ def test_make_dumper_object_hook_conversions_and_fallbacks(self) -> None: dumper._append_fallback(123, file) self.assertEqual(file.getvalue(), "{'base': '123'}") + def test_make_dumper_dumps_renamed_builtin_key(self) -> None: + """A key renamed for clashing with a builtin is kept under its original name (:issue:`2733`).""" + from pcapkit.corekit.infoclass import Info + from pcapkit.dumpkit.common import make_dumper + from pcapkit.foundation.reassembly.data.tcp import Datagram + + Dumper = make_dumper(BaseDumper) + dumper = Dumper() + + info = Info(items='catalog', normal='value') + self.assertEqual(dumper.object_hook(info), {'items': 'catalog', 'normal': 'value'}) + + datagram = Datagram(True, 'tcp-id', (0,), b'header', b'payload', b'raw_packet', []) + dumped = dumper.object_hook(datagram) + self.assertIn('packet', dumped) + self.assertEqual(dumped['packet'], b'raw_packet') + def test_make_dumper_object_hook_leaves_strings_and_enum_renderings_unescaped(self) -> None: """GitHub issue #772: escaping is now the writer's own job, not this hook's.