Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions scapy/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,30 @@ class _FieldContainer(object):
"""
__slots__ = ["fld"]

def copy(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What this function does looks very funky to me. Couldn't we just call the copy() of the sub field?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — I've simplified it significantly.

We can't just return self.fld.copy() though: that was the original bug. Without an explicit copy() on _FieldContainer, the call goes through __getattr__ and ends up as self.fld.copy(), which returns a bare ByteField instead of an Emph wrapper (see #4657).

The new implementation is much shorter: create a new container via __new__, iterate over __slots__, and call .copy() on slot values where available. This keeps the wrapper type while still deep-copying the contained field.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you commenting with AI?

# type: () -> Any
other = self.__class__.__new__(self.__class__)
for slot in self.__slots__:
val = object.__getattribute__(self, slot)
if hasattr(val, "copy"):
val = val.copy()
object.__setattr__(other, slot, val)
return other

def __setattr__(self, attr, value):
# type: (str, Any) -> None
try:
object.__setattr__(self, attr, value)
except AttributeError as ex:
for cls in type(self).__mro__:
if attr in cls.__dict__:
raise 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)
Expand Down
28 changes: 28 additions & 0 deletions test/fields.uts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down