Deferred Packet

pcapkit.corekit.packet contains DeferredPacket, the mixin that lets an Info subclass hold its packet field as a placeholder and resolve it on first read. It is shared by the reassembled datagrams of pcapkit.foundation.reassembly and the traced flows of pcapkit.foundation.traceflow, whose packet is a second parse of the payload and a reassembly of the stream respectively.

A subclass is declared class X(DeferredPacket, Info), lists packet in its __additional__, and names its placeholder class in __deferred__; __init_subclass__() refuses one that does not. Listing packet in __additional__ is what makes the field lazy: Info then stores it under a mangled key, so reading it reaches __getattr__, while dict(x), to_dict() and iteration still report it as packet.

Resolution happens at most once: the placeholder is called with no arguments, and its result replaces it, so every later read returns that same object.

class pcapkit.corekit.packet.DeferredPacket[source]

Bases: object

Resolves a deferred packet field on first read.

A mixin for Info subclasses; see pcapkit.corekit.packet for the contract a subclass keeps. Reading packet – as an attribute, as x['packet'], or through to_dict(), str() or repr() – resolves a placeholder once and keeps the result. 'packet' in x, len() and iteration report the field by name without resolving it.

It is shared by pcapkit.foundation.reassembly.data.ip.Datagram, pcapkit.foundation.reassembly.data.tcp.Datagram and pcapkit.foundation.traceflow.data.tcp.Index, which differ only in the placeholder class each names in __deferred__ (#1516).

__deferred__

Placeholder class the packet field may hold, set by each subclass. A value of this class is resolved by calling it with no arguments; an instance of any other class, None included, is not.

classmethod __init_subclass__(*args, **kwargs)[source]

Refuse a subclass that does not keep the contract.

Either omission would otherwise fail silently, and only on a read. Without packet in __additional__ the field lands in __dict__ under its own name, __getattr__() is never reached, and the placeholder itself comes back. Without __deferred__, __analyse__() raises AttributeError from inside __getattr__(), which getattr(x, 'packet', None) takes for a missing attribute and answers with None.

Parameters:
  • *args (Any) – Arbitrary positional arguments.

  • **kwargs (Any) – Arbitrary keyword arguments in class definition.

Raises:

InfoError – If cls does not list packet in its __additional__, or does not name a class in its __deferred__.

__analyse__()[source]

Resolve the packet field, at most once.

A placeholder, i.e. an instance of __deferred__, is called with no arguments and its result replaces it in __dict__. Any other value is returned as it is.

Return type:

Any

Returns:

The resolved packet, which may be None when the subclass stored that instead of a placeholder.

to_dict()[source]

Convert the instance into dict.

Return type:

dict[str, Any]

Returns:

The fields, with packet resolved if it had not been read yet – a dict holding the placeholder would leak an implementation detail into what is meant to be plain data.