From 159afb7b7c4235263f610f7c7e7bdd5f46f022e9 Mon Sep 17 00:00:00 2001 From: "Teppei.F" <37261985+T3pp31@users.noreply.github.com> Date: Mon, 1 Jun 2026 23:34:28 +0900 Subject: [PATCH] fields: preserve containers when copying fields AI-Assisted: yes (Codex) --- scapy/fields.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ test/fields.uts | 28 ++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/scapy/fields.py b/scapy/fields.py index 46064b726e2..8b9a9be6407 100644 --- a/scapy/fields.py +++ b/scapy/fields.py @@ -312,6 +312,54 @@ class _FieldContainer(object): """ __slots__ = ["fld"] + def copy(self): + # type: () -> Any + def _copy_attr(attr): + # type: (Any) -> Any + if hasattr(attr, "copy"): + return attr.copy() + return attr + + cls = self.__class__ + other = cls.__new__(cls) + + if hasattr(self, "__dict__"): + other.__dict__ = { + key: _copy_attr(value) + for key, value in self.__dict__.items() + } + + copied_slots = set() + for base in cls.__mro__: + slots = base.__dict__.get("__slots__", ()) + if isinstance(slots, str): + slots = (slots,) + for slot in slots: + if slot in ("__dict__", "__weakref__") or slot in copied_slots: + continue + slot_descriptor = base.__dict__.get(slot) + if getattr(cls, slot, None) is not slot_descriptor: + continue + copied_slots.add(slot) + try: + value = getattr(self, slot) + except AttributeError: + continue + setattr(other, slot, _copy_attr(value)) + + return other + + def __setattr__(self, attr, value): + # type: (str, Any) -> None + try: + object.__setattr__(self, attr, value) + except AttributeError as ex: + try: + fld = object.__getattribute__(self, "fld") + except AttributeError: + raise ex + setattr(fld, attr, value) + def __getattr__(self, attr): # type: (str) -> Any return getattr(self.fld, attr) diff --git a/test/fields.uts b/test/fields.uts index 38263f2a49f..a127056a1aa 100644 --- a/test/fields.uts +++ b/test/fields.uts @@ -15,6 +15,34 @@ #Field("foo", None, fmt="I").getfield(None, b"\x12\x34\x56\x78ABCD") #assert _ == ("ABCD",0x12345678) += FieldContainer copy +~ core field + +field_container = Emph(ByteField("foo", 0)) +field_container_copy = field_container.copy() + +assert type(field_container_copy) is Emph +assert type(field_container_copy.fld) is ByteField +assert field_container_copy.fld is not field_container.fld +assert field_container_copy.name == "foo" +assert field_container_copy.default == 0 + +class TEST_FIELD_CONTAINER_COPY_SOURCE(Packet): + fields_desc = [Emph(ByteField("foo", 0))] + +class TEST_FIELD_CONTAINER_COPY_TARGET(Packet): + fields_desc = [TEST_FIELD_CONTAINER_COPY_SOURCE, ByteField("bar", 0)] + foo = 7 + +source_field = TEST_FIELD_CONTAINER_COPY_SOURCE.fields_desc[0] +target_field = TEST_FIELD_CONTAINER_COPY_TARGET.fields_desc[0] + +assert type(target_field) is Emph +assert type(target_field.fld) is ByteField +assert target_field.fld is not source_field.fld +assert target_field.default == 7 +assert source_field.default == 0 + = ConditionnalField class ~ core field