Skip to content

Commit eac2ff2

Browse files
committed
fields: preserve containers when copying fields
1 parent 66ef96a commit eac2ff2

2 files changed

Lines changed: 76 additions & 0 deletions

File tree

scapy/fields.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,54 @@ class _FieldContainer(object):
312312
"""
313313
__slots__ = ["fld"]
314314

315+
def copy(self):
316+
# type: () -> Any
317+
def _copy_attr(attr):
318+
# type: (Any) -> Any
319+
if hasattr(attr, "copy"):
320+
return attr.copy()
321+
return attr
322+
323+
cls = self.__class__
324+
other = cls.__new__(cls)
325+
326+
if hasattr(self, "__dict__"):
327+
other.__dict__ = {
328+
key: _copy_attr(value)
329+
for key, value in self.__dict__.items()
330+
}
331+
332+
copied_slots = set()
333+
for base in cls.__mro__:
334+
slots = base.__dict__.get("__slots__", ())
335+
if isinstance(slots, str):
336+
slots = (slots,)
337+
for slot in slots:
338+
if slot in ("__dict__", "__weakref__") or slot in copied_slots:
339+
continue
340+
slot_descriptor = base.__dict__.get(slot)
341+
if getattr(cls, slot, None) is not slot_descriptor:
342+
continue
343+
copied_slots.add(slot)
344+
try:
345+
value = getattr(self, slot)
346+
except AttributeError:
347+
continue
348+
setattr(other, slot, _copy_attr(value))
349+
350+
return other
351+
352+
def __setattr__(self, attr, value):
353+
# type: (str, Any) -> None
354+
try:
355+
object.__setattr__(self, attr, value)
356+
except AttributeError as ex:
357+
try:
358+
fld = object.__getattribute__(self, "fld")
359+
except AttributeError:
360+
raise ex
361+
setattr(fld, attr, value)
362+
315363
def __getattr__(self, attr):
316364
# type: (str) -> Any
317365
return getattr(self.fld, attr)

test/fields.uts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,34 @@
1515
#Field("foo", None, fmt="I").getfield(None, b"\x12\x34\x56\x78ABCD")
1616
#assert _ == ("ABCD",0x12345678)
1717

18+
= FieldContainer copy
19+
~ core field
20+
21+
field_container = Emph(ByteField("foo", 0))
22+
field_container_copy = field_container.copy()
23+
24+
assert type(field_container_copy) is Emph
25+
assert type(field_container_copy.fld) is ByteField
26+
assert field_container_copy.fld is not field_container.fld
27+
assert field_container_copy.name == "foo"
28+
assert field_container_copy.default == 0
29+
30+
class TEST_FIELD_CONTAINER_COPY_SOURCE(Packet):
31+
fields_desc = [Emph(ByteField("foo", 0))]
32+
33+
class TEST_FIELD_CONTAINER_COPY_TARGET(Packet):
34+
fields_desc = [TEST_FIELD_CONTAINER_COPY_SOURCE, ByteField("bar", 0)]
35+
foo = 7
36+
37+
source_field = TEST_FIELD_CONTAINER_COPY_SOURCE.fields_desc[0]
38+
target_field = TEST_FIELD_CONTAINER_COPY_TARGET.fields_desc[0]
39+
40+
assert type(target_field) is Emph
41+
assert type(target_field.fld) is ByteField
42+
assert target_field.fld is not source_field.fld
43+
assert target_field.default == 7
44+
assert source_field.default == 0
45+
1846

1947
= ConditionnalField class
2048
~ core field

0 commit comments

Comments
 (0)