Skip to content

Commit 2fec703

Browse files
Rework list_field as ListValue in 'fields.py' (#4705)
More consistent with `FlagValue`.
1 parent 9415f58 commit 2fec703

File tree

2 files changed

+69
-75
lines changed

2 files changed

+69
-75
lines changed

scapy/fields.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,6 +1619,70 @@ def getfield(self,
16191619
return s[len_pkt:], i
16201620

16211621

1622+
class _ListValueMeta(type):
1623+
"""
1624+
Wraps modifying methods for ``list`` base type.
1625+
1626+
Inspired from https://stackoverflow.com/questions/8858525/track-changes-to-lists-and-dictionaries-in-python#8859168.
1627+
"""
1628+
def __new__(
1629+
mcs,
1630+
name, # type: str
1631+
bases, # Tuple[type, ...]
1632+
attrs, # type: Dict[str, Any]
1633+
): # type: (...) -> type
1634+
# List names of `list` methods modifying the list.
1635+
for method_name in [
1636+
"append",
1637+
"clear",
1638+
"extend",
1639+
"insert",
1640+
"pop",
1641+
"remove",
1642+
"reverse", # Memo: Reverse *IN PLACE*.
1643+
"sort", # Memo: Stable sort *IN PLACE*.
1644+
"__delitem__",
1645+
"__iadd__",
1646+
"__imul__",
1647+
"__setitem__",
1648+
]:
1649+
# Wrap the method so that `Packet.clear_cache()` be automatically called.
1650+
attrs[method_name] = _ListValueMeta._wrap_method(getattr(list, method_name))
1651+
return type.__new__(mcs, name, bases, attrs)
1652+
1653+
@staticmethod
1654+
def _wrap_method(meth): # type: (Callable[[Any, ...], Any]) -> Callable[[Any, ...], Any]
1655+
def wrapped(
1656+
self, # type: ListValue
1657+
*args, # type: Any
1658+
**kwargs, # type: Any
1659+
): # type: (...) -> Any
1660+
# Automatically call `Packet.clear_cache()` when the `ListValue` is modified.
1661+
self.pkt.clear_cache(upwards=True, downwards=False)
1662+
1663+
# Call the wrapped method, and return its result.
1664+
return meth(self, *args, **kwargs)
1665+
return wrapped
1666+
1667+
1668+
class ListValue(list, metaclass=_ListValueMeta):
1669+
"""
1670+
Overrides the base ``list`` type for list fields bound with packets.
1671+
1672+
Ensures ``Packet.clear_cache()`` is called when the list is modified.
1673+
"""
1674+
def __init__(
1675+
self,
1676+
pkt, # type: Packet,
1677+
*args # type: Any
1678+
): # type: (...) -> None
1679+
# Call the `list.__init__()` super constructor.
1680+
super().__init__(*args)
1681+
1682+
#: Packet bound with this list field.
1683+
self.pkt = pkt
1684+
1685+
16221686
class PacketListField(_PacketField[List[BasePacket]]):
16231687
"""PacketListField represents a list containing a series of Packet instances
16241688
that might occur right in the middle of another Packet field.

scapy/packet.py

Lines changed: 5 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
Field,
3434
FlagsField,
3535
FlagValue,
36+
ListValue,
3637
MayEnd,
3738
MultiEnumField,
3839
MultipleTypeField,
@@ -701,10 +702,10 @@ def _ensure_bound_field_value(
701702
Ensures a field instance bound with ``self`` when applicable.
702703
"""
703704
if isinstance(value, list):
704-
# If `value` is a simple `list`, create a new `list_field` instance.
705-
# If `value` is already a `list_field` instance, we never know where this instance comes from, and what it's being used for.
706-
# Let's create a new `list_field` instance in any case.
707-
return list_field(
705+
# If `value` is a simple `list`, create a new `ListValue` instance.
706+
# If `value` is already a `ListValue` instance, we never know where this instance comes from, and what it's being used for.
707+
# Let's create a new `ListValue` instance in any case.
708+
return ListValue(
708709
self,
709710
# Recurse on list items.
710711
[self._ensure_bound_field_value(x) for x in value],
@@ -2171,77 +2172,6 @@ def route(self):
21712172
return (None, None, None)
21722173

21732174

2174-
#################
2175-
# list fields #
2176-
#################
2177-
2178-
2179-
class list_field_meta(type):
2180-
"""
2181-
Wraps modifying methods for ``list`` base type.
2182-
2183-
Inspired from https://stackoverflow.com/questions/8858525/track-changes-to-lists-and-dictionaries-in-python#8859168.
2184-
"""
2185-
def __new__(
2186-
mcs,
2187-
name, # type: str
2188-
bases, # Tuple[type, ...]
2189-
attrs, # type: Dict[str, Any]
2190-
): # type: (...) -> type
2191-
# List names of `list` methods modifying the list.
2192-
for method_name in [
2193-
"append",
2194-
"clear",
2195-
"extend",
2196-
"insert",
2197-
"pop",
2198-
"remove",
2199-
"reverse", # Memo: Reverse *IN PLACE*.
2200-
"sort", # Memo: Stable sort *IN PLACE*.
2201-
"__delitem__",
2202-
"__iadd__",
2203-
"__imul__",
2204-
"__setitem__",
2205-
]:
2206-
# Wrap the method so that `Packet.clear_cache()` be automatically called.
2207-
attrs[method_name] = list_field_meta._wrap_method(getattr(list, method_name))
2208-
return type.__new__(mcs, name, bases, attrs)
2209-
2210-
@staticmethod
2211-
def _wrap_method(meth): # type: (Callable[[Any, ...], Any]) -> Callable[[Any, ...], Any]
2212-
def wrapped(
2213-
self, # type: list_field
2214-
*args, # type: Any
2215-
**kwargs, # type: Any
2216-
): # type: (...) -> Any
2217-
# Automatically call `Packet.clear_cache()` when the `list_field` is modified.
2218-
self.pkt.clear_cache(upwards=True, downwards=False)
2219-
2220-
# Call the wrapped method, and return its result.
2221-
return meth(self, *args, **kwargs)
2222-
return wrapped
2223-
2224-
2225-
class list_field(list, metaclass=list_field_meta):
2226-
"""
2227-
Overrides the base ``list`` type for list fields bound with packets.
2228-
2229-
Ensures :meth:`Packet.clear_cache()` is called when the list is modified.
2230-
2231-
Lower case for the class name in order to avoid confusions with classes like ``PacketListField``.
2232-
"""
2233-
def __init__(
2234-
self,
2235-
pkt, # type: Packet,
2236-
*args # type: Any
2237-
): # type: (...) -> None
2238-
# Call the `list.__init__()` super constructor.
2239-
super().__init__(*args)
2240-
2241-
#: Packet bound with this list field.
2242-
self.pkt = pkt
2243-
2244-
22452175
####################
22462176
# packet classes #
22472177
####################

0 commit comments

Comments
 (0)