Skip to content

Commit 94ba1d3

Browse files
polybassagpotter2
andauthored
Fix PacketList pickling on Python <3.6 (#3113)
* Add pickle test for PacketList * test different NamedTuple for pickle * Improve pickling Co-authored-by: gpotter2 <[email protected]>
1 parent 43738be commit 94ba1d3

File tree

3 files changed

+41
-16
lines changed

3 files changed

+41
-16
lines changed

scapy/compat.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,24 @@ class Sized(object): # type: ignore
179179
if sys.version_info >= (3, 7):
180180
from typing import NamedTuple
181181
else:
182-
NamedTuple = lambda name, params: collections.namedtuple(name, list(x[0] for x in params)) # noqa: E501
182+
# Hack for Python < 3.7 - Implement NamedTuple pickling
183+
def _unpickleNamedTuple(name, len_params, *args):
184+
return collections.namedtuple(
185+
name,
186+
args[:len_params]
187+
)(*args[len_params:])
188+
189+
def NamedTuple(name, params):
190+
tup_params = tuple(x[0] for x in params)
191+
cls = collections.namedtuple(name, tup_params)
192+
193+
class _NT(cls):
194+
def __reduce__(self):
195+
"""Used by pickling methods"""
196+
return (_unpickleNamedTuple,
197+
(name, len(tup_params)) + tup_params + tuple(self))
198+
_NT.__name__ = cls.__name__
199+
return _NT
183200

184201
# Python 3.8 Only
185202
if sys.version_info >= (3, 8):

scapy/packet.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ def __init__(self,
186186
self.post_transforms = [post_transform]
187187

188188
_PickleType = Tuple[
189-
bytes,
190189
Union[EDecimal, float],
191190
Optional[Union[EDecimal, float, None]],
192191
Optional[int],
@@ -195,31 +194,24 @@ def __init__(self,
195194
]
196195

197196
def __reduce__(self):
198-
# type: () -> Tuple[Type[Packet], Tuple[()], Packet._PickleType]
197+
# type: () -> Tuple[Type[Packet], Tuple[bytes], Packet._PickleType]
199198
"""Used by pickling methods"""
200-
return (self.__class__, (), (
201-
self.build(),
199+
return (self.__class__, (self.build(),), (
202200
self.time,
203201
self.sent_time,
204202
self.direction,
205203
self.sniffed_on,
206204
self.wirelen,
207205
))
208206

209-
def __getstate__(self):
210-
# type: () -> Packet._PickleType
211-
"""Mark object as pickable"""
212-
return self.__reduce__()[2]
213-
214207
def __setstate__(self, state):
215208
# type: (Packet._PickleType) -> Packet
216209
"""Rebuild state using pickable methods"""
217-
self.__init__(state[0]) # type: ignore
218-
self.time = state[1]
219-
self.sent_time = state[2]
220-
self.direction = state[3]
221-
self.sniffed_on = state[4]
222-
self.wirelen = state[5]
210+
self.time = state[0]
211+
self.sent_time = state[1]
212+
self.direction = state[2]
213+
self.sniffed_on = state[3]
214+
self.wirelen = state[4]
223215
return self
224216

225217
def __deepcopy__(self,

test/regression.uts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4224,6 +4224,22 @@ srl, rl = pl.sr(lookahead=None)
42244224
assert len(srl) == 1
42254225
assert len(rl) == 7
42264226

4227+
= pickle test
4228+
import pickle
4229+
import io
4230+
4231+
srl, rl = PacketList([Raw(b"1"), Raw(b"1"), Raw(b"2"), Raw(b"3"), Raw(b"4"), Raw(b"3"), Raw(b"1"), Raw(b"1"), Raw(b"4")]).sr()
4232+
assert len(srl) == 4
4233+
4234+
f = io.BytesIO()
4235+
4236+
pickle.dump(srl, f)
4237+
4238+
unp = pickle.loads(f.getvalue())
4239+
4240+
assert len(unp) == len(srl)
4241+
assert all(bytes(a[0]) == bytes(b[0]) for a, b in zip(unp, srl))
4242+
42274243
= plot()
42284244

42294245
import mock

0 commit comments

Comments
 (0)