Skip to content

Commit 9bafbc3

Browse files
guedougg-gvaladon
authored andcommitted
Pickle - support extra slots
1 parent b9f54f2 commit 9bafbc3

File tree

2 files changed

+63
-24
lines changed

2 files changed

+63
-24
lines changed

scapy/packet.py

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -79,28 +79,6 @@
7979
_T = TypeVar("_T", Dict[str, Any], Optional[Dict[str, Any]])
8080

8181

82-
def _rebuild_pkt(
83-
cls, # type: Type[Packet]
84-
fields, # type: Dict[str, Any]
85-
payload, # type: Optional[Packet]
86-
metadata, # type: Dict[str, Any]
87-
):
88-
# type: (...) -> Packet
89-
"""Helper for unpickling Packet instances via field values."""
90-
# Create the instance using the field values
91-
pkt = cls(**fields)
92-
if payload is not None:
93-
pkt.add_payload(payload)
94-
# Restore metadata
95-
pkt.time = metadata['time']
96-
pkt.sent_time = metadata['sent_time']
97-
pkt.direction = metadata['direction']
98-
pkt.sniffed_on = metadata['sniffed_on']
99-
pkt.wirelen = metadata['wirelen']
100-
pkt.comments = metadata['comments']
101-
return pkt
102-
103-
10482
class Packet(
10583
BasePacket,
10684
_CanvasDumpExtended,
@@ -256,6 +234,31 @@ def comment(self, value):
256234
else:
257235
self.comments = None
258236

237+
def _rebuild_pkt(
238+
cls, # type: Type[Packet]
239+
fields, # type: Dict[str, Any]
240+
payload, # type: Optional[Packet]
241+
metadata, # type: Dict[str, Any]
242+
extra_slots={}, # type: Dict[str, Any]
243+
):
244+
# type: (...) -> Packet
245+
"""Helper for unpickling Packet instances via field values."""
246+
# Create the instance using the field values
247+
pkt = cls(**fields)
248+
if payload is not None:
249+
pkt.add_payload(payload)
250+
# Restore metadata
251+
pkt.time = metadata['time']
252+
pkt.sent_time = metadata['sent_time']
253+
pkt.direction = metadata['direction']
254+
pkt.sniffed_on = metadata['sniffed_on']
255+
pkt.wirelen = metadata['wirelen']
256+
pkt.comments = metadata['comments']
257+
# Restore any extra __slots__ defined by subclasses
258+
for attr, value in extra_slots.items():
259+
setattr(pkt, attr, value)
260+
return pkt
261+
259262
def __reduce__(self):
260263
# type: () -> Tuple[Any, ...]
261264
"""Used by pickling methods.
@@ -279,9 +282,14 @@ def __reduce__(self):
279282
'wirelen': self.wirelen,
280283
'comments': self.comments,
281284
}
285+
# Collect any extra __slots__ defined by subclasses
286+
extra_slots = {}
287+
for attr in type(self).__all_slots__ - set(Packet.__slots__):
288+
if hasattr(self, attr):
289+
extra_slots[attr] = getattr(self, attr)
282290
return (
283-
_rebuild_pkt,
284-
(self.__class__, fields, payload, metadata),
291+
self._rebuild_pkt,
292+
(self.__class__, fields, payload, metadata, extra_slots),
285293
)
286294

287295
def __deepcopy__(self,

test/regression.uts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,37 @@ p2 = pickle.loads(pickle.dumps(p))
653653
assert p2.src == '10.0.0.1'
654654
assert raw(p2) == raw(p)
655655

656+
= Pickle preserves custom __slots__ from subclasses
657+
658+
import pickle
659+
import scapy.packet as _pkt_mod
660+
661+
class _PickleTestPacket(Packet):
662+
__slots__ = ["custom_id", "custom_tag"]
663+
name = "PickleTestPacket"
664+
fields_desc = [ByteField("val", 0)]
665+
666+
# Make the class discoverable by pickle
667+
_pkt_mod._PickleTestPacket = _PickleTestPacket
668+
_PickleTestPacket.__module__ = 'scapy.packet'
669+
670+
p = _PickleTestPacket(val=42)
671+
p.custom_id = 0x123
672+
p.custom_tag = "hello"
673+
p2 = pickle.loads(pickle.dumps(p))
674+
assert p2.val == 42
675+
assert p2.custom_id == 0x123
676+
assert p2.custom_tag == "hello"
677+
678+
# Slots not explicitly set are not serialized
679+
p3 = _PickleTestPacket(val=7)
680+
assert not hasattr(p3, 'custom_id')
681+
p4 = pickle.loads(pickle.dumps(p3))
682+
assert p4.val == 7
683+
assert not hasattr(p4, 'custom_id')
684+
685+
del _pkt_mod._PickleTestPacket
686+
656687
= Usage test
657688

658689
from scapy.main import _usage

0 commit comments

Comments
 (0)