From 8dd8106a2756eb11ca68984055b0b08c5e09b713 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Wed, 7 Oct 2026 00:41:23 -0400 Subject: [PATCH] docs: prose sweep after the defect wave (foundation-docs) (#1103) - extraction.py: _open_output names which engines pass header fields (PCAP does, PCAP-NG passes none); run() lists the PCAP-NG/PCAP FormatError; _ofile's comment covers its class/writer/factory states. - engines/pcapng.py: run() documents its two FormatErrors. - dumpkit/pcap.py: PCAPIO's **kwargs names thiszone/sigfigs/snaplen. - dumpkit/common.py: a backslash in a JSON key escapes, not closes. - all.py: drop the commit and date from the utilities comment. - docs: extraction page documents _open_output and _flag_o; the engine gap table gains the PCAP-output rows; ext.rst's PoC engine calls record_header like the real Scapy engine. Comments, docstrings and .rst only; AST guard True for every .py. --- docs/source/ext.rst | 5 +++++ .../pcapkit/foundation/engines/index.rst | 11 ++++++++++ docs/source/pcapkit/foundation/extraction.rst | 2 ++ pcapkit/all.py | 6 ++--- pcapkit/dumpkit/common.py | 7 +++--- pcapkit/dumpkit/pcap.py | 6 +++-- pcapkit/foundation/engines/pcapng.py | 4 ++++ pcapkit/foundation/extraction.py | 22 ++++++++++--------- 8 files changed, 45 insertions(+), 18 deletions(-) diff --git a/docs/source/ext.rst b/docs/source/ext.rst index 6b9840df88..7071a51771 100644 --- a/docs/source/ext.rst +++ b/docs/source/ext.rst @@ -510,6 +510,11 @@ The following code snippet shows how to create a new engine class: f'Frame {extractor._frnum:>3d}: {packet2chain(packet)}' ) # print verbose message as `Frame XXX: Ethernet:IP:TCP:...` + # NOTE: The extractor's shared global-header step parses the global + # header with the built-in engine and, unless output is disabled, + # dumps it and sets the output format that `ext.format` reports. + ext.record_header() + # NOTE: Here we use the API entry point, i.e., scapy.sendrecv.sniff, # to read the input file and extract the network packets. The API # entry point returns an iterable instance, which is then converted diff --git a/docs/source/pcapkit/foundation/engines/index.rst b/docs/source/pcapkit/foundation/engines/index.rst index d7655c7433..6d15766bd4 100644 --- a/docs/source/pcapkit/foundation/engines/index.rst +++ b/docs/source/pcapkit/foundation/engines/index.rst @@ -95,6 +95,17 @@ offers, and the ones that cannot say so rather than quietly doing less: | | PCAP savefiles only, otherwise | | | :exc:`~pcapkit.utilities.exceptions.FormatError` | +-----------------------------------------------------------------+---------------------------------------------------------------+ +| :class:`~pcapkit.foundation.engines.dpkt.DPKT`, | no PCAP output, since each frame reaches the writer as a | +| :class:`~pcapkit.foundation.engines.scapy.Scapy`, | plain :obj:`dict` -- ``json`` is written instead, with a | +| :class:`~pcapkit.foundation.engines.pyshark.PyShark`, | :class:`~pcapkit.utilities.warnings.FormatWarning` | +| :class:`~pcapkit.foundation.engines.pypcap.PyPCAP`, | | +| :class:`~pcapkit.foundation.engines.pcap_ct.PCAP_CT` and | | +| :class:`~pcapkit.foundation.engines.pypcapfile.PyPCAPFile` | | ++-----------------------------------------------------------------+---------------------------------------------------------------+ +| :class:`~pcapkit.foundation.engines.pcapng.PCAPNG` | no PCAP output for a PCAP-NG input, which declares its link | +| | type and timestamp resolution per interface -- | +| | :exc:`~pcapkit.utilities.exceptions.FormatError` | ++-----------------------------------------------------------------+---------------------------------------------------------------+ Availability ------------ diff --git a/docs/source/pcapkit/foundation/extraction.rst b/docs/source/pcapkit/foundation/extraction.rst index a1f68bb7dc..0bcb5f2d1d 100644 --- a/docs/source/pcapkit/foundation/extraction.rst +++ b/docs/source/pcapkit/foundation/extraction.rst @@ -45,6 +45,7 @@ extracts parameters from a PCAP file. .. automethod:: make_name .. automethod:: record_header + .. automethod:: _open_output .. automethod:: record_frames .. autoattribute:: __output__ @@ -63,6 +64,7 @@ extracts parameters from a PCAP file. .. autoattribute:: _flag_e .. autoattribute:: _flag_f .. autoattribute:: _flag_q + .. autoattribute:: _flag_o .. autoattribute:: _flag_r .. autoattribute:: _flag_t .. autoattribute:: _flag_v diff --git a/pcapkit/all.py b/pcapkit/all.py index 02f44d8493..21e61578ea 100644 --- a/pcapkit/all.py +++ b/pcapkit/all.py @@ -141,8 +141,8 @@ 'scapy_ipv4_reassembly', 'scapy_ipv6_reassembly', 'scapy_tcp_reassembly', 'scapy_tcp_traceflow', # Scapy engine - # pcapkit.utilities -- unlisted since 769a17c78 (2022-05-29): its names stay - # reachable as attributes, but ``import *`` leaves out generic helpers such - # as ``warn``, ``reset``, ``configure`` and ``detect`` + # pcapkit.utilities -- unlisted: its names stay reachable as attributes, but + # ``import *`` leaves out generic helpers such as ``warn``, ``reset``, + # ``configure`` and ``detect`` #'beholder_ng', 'seekset_ng', # Decorators ] diff --git a/pcapkit/dumpkit/common.py b/pcapkit/dumpkit/common.py index 9d82f30bf7..9da80f4d25 100644 --- a/pcapkit/dumpkit/common.py +++ b/pcapkit/dumpkit/common.py @@ -247,9 +247,10 @@ def make_dumper(output: 'Type[ABCDumper]') -> 'Type[ABCDumper]': # concrete writer this applies to. escape_strings = issubclass(output, dictdumper.plist.PLIST) # NOTE: :class:`~dictdumper.json.JSON` escapes every string *value* it - # writes, but interpolates a mapping key into ``'"{item}": '`` raw, so a key - # carrying a ``"`` or a ``\`` closes the JSON string early (GitHub issue - # #1152). The defect is :mod:`dictdumper`'s, so it is worked around here. + # writes, but interpolates a mapping key into ``'"{item}": '`` raw: a ``"`` + # in a key closes the JSON string early, and a ``\`` escapes the character + # after it (GitHub issue #1152). The defect is :mod:`dictdumper`'s, so it is + # worked around here. escape_json_keys = issubclass(output, dictdumper.json.JSON) escape_keys = escape_strings or escape_json_keys diff --git a/pcapkit/dumpkit/pcap.py b/pcapkit/dumpkit/pcap.py index a3fb0ef1a8..979ee876a8 100644 --- a/pcapkit/dumpkit/pcap.py +++ b/pcapkit/dumpkit/pcap.py @@ -57,7 +57,8 @@ class PCAPIO(DumperBase): protocol: data link type byteorder: header byte order nanosecond: nanosecond-resolution file flag - **kwargs: arbitrary keyword arguments + **kwargs: further global header fields, i.e. ``thiszone``, ``sigfigs`` + and ``snaplen``, and arbitrary keyword arguments """ if TYPE_CHECKING: @@ -89,7 +90,8 @@ def __init__(self, fname: 'str', *, protocol: 'Enum_LinkType | StdlibIntEnum | A protocol: data link type byteorder: header byte order nanosecond: nanosecond-resolution file flag - **kwargs: arbitrary keyword arguments + **kwargs: further global header fields, i.e. ``thiszone``, + ``sigfigs`` and ``snaplen``, and arbitrary keyword arguments """ #: int: Frame counter. diff --git a/pcapkit/foundation/engines/pcapng.py b/pcapkit/foundation/engines/pcapng.py index 847e4e1905..5c61bccd36 100644 --- a/pcapkit/foundation/engines/pcapng.py +++ b/pcapkit/foundation/engines/pcapng.py @@ -144,6 +144,10 @@ def run(self) -> 'None': block, and then save the related information into the internal context storage. + Raises: + FormatError: If the first block is not a section header block, or if + the output format is PCAP. + """ ext = self._extractor diff --git a/pcapkit/foundation/extraction.py b/pcapkit/foundation/extraction.py index f7b55fe2a2..cf92df6b40 100644 --- a/pcapkit/foundation/extraction.py +++ b/pcapkit/foundation/extraction.py @@ -188,7 +188,8 @@ class Extractor(Generic[_P]): #: Input file object. _ifile: 'BufferedReader' - #: Output file object. + #: Output writer. It holds the writer class until :meth:`_open_output` + #: replaces it with the writer, or under split mode with a factory. _ofile: 'Dumper | Type[Dumper]' #: Position of the input stream at the previous end of stream, or @@ -668,7 +669,8 @@ def run(self) -> 'None': # pylint: disable=inconsistent-return-statements Raises: FormatError: If the selected engine cannot read the input's format -- - for the default engine, if it is neither PCAP nor PCAP-NG. + for the default engine, if it is neither PCAP nor PCAP-NG -- or if + the built-in PCAP-NG engine reads a PCAP-NG input to PCAP output. :rtype: None """ @@ -893,14 +895,14 @@ def record_header(self) -> 'EngineBase[_P]': def _open_output(self, **kwargs: 'Any') -> 'None': """Create the output writer. - The engines call this at their global-header step, passing the header's - link type, byte order and timestamp resolution as ``protocol``, - ``byteorder`` and ``nanosecond``, and the PCAP engine also its - ``thiszone``, ``sigfigs`` and ``snaplen``: :class:`~pcapkit.dumpkit.pcap.PCAPIO` - writes its own global header from them, and the other writers ignore - them. :meth:`run` calls it again, without arguments, after the engine - has started, for the engines that have no such step. Only the first - call has an effect. + The built-in engines call this at their global-header step. The PCAP + engine passes the header's fields as ``protocol``, ``byteorder``, + ``nanosecond``, ``thiszone``, ``sigfigs`` and ``snaplen``: + :class:`~pcapkit.dumpkit.pcap.PCAPIO` writes its own global header from + them, and the other writers ignore them. The PCAP-NG engine passes none. + :meth:`run` calls it again, without arguments, after the engine has + started, for the engines that have no such step. Only the first call has + an effect. Under split mode, i.e. :attr:`self._flag_f ` is :data:`True`, :attr:`self._ofile ` stays a factory,