From 3f7ba463637cbb4db6a033a4d5d531b41903c76b Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Tue, 6 Oct 2026 12:45:45 -0400 Subject: [PATCH] docs(protocols): second-round pass over the internet-layer prose (#719) - registry attribute comments (IPv4, HOPOPT, IPv6-Opts, IPv6-Route, HIP, MH x3): a non-string value is a (parser, constructor) pair, and a missing named method falls back to the unassigned/none handler - HOPOPT/IPv6-Opts Raises: name the field the code checks (schema.len), fix the copy-pasted tun/jumbo names, and say what the length check is - HIP: _read_locator returns IPv6Address/LocatorData; PUZZLE and SOLUTION raise on a HIPv1 length mismatch, not on any non-v1 version - IPv6-Opts make documents Destination, not Hop-by-Hop, options - "Header Extensive Length" -> "Header Extension Length"; split :class: role rejoined; shorter IPv6 walk comment; past-tense rationale restated in the present (IPv6-Route, IPv6-Opts, MH) Docstrings and comments only; AST-identical to origin/main. --- pcapkit/protocols/internet/hip.py | 22 ++++++----- pcapkit/protocols/internet/hopopt.py | 29 ++++++++------- pcapkit/protocols/internet/ipv4.py | 10 ++--- pcapkit/protocols/internet/ipv6.py | 47 +++++++++++------------- pcapkit/protocols/internet/ipv6_opts.py | 33 +++++++++-------- pcapkit/protocols/internet/ipv6_route.py | 22 +++++------ pcapkit/protocols/internet/mh.py | 46 ++++++++++++----------- 7 files changed, 105 insertions(+), 104 deletions(-) diff --git a/pcapkit/protocols/internet/hip.py b/pcapkit/protocols/internet/hip.py index 47298ceec5..43c2e90343 100644 --- a/pcapkit/protocols/internet/hip.py +++ b/pcapkit/protocols/internet/hip.py @@ -401,10 +401,10 @@ class HIP(IPv6_Ext[Data_HIP, Schema_HIP], Internet[Data_HIP, Schema_HIP], #: DefaultDict[Enum_Parameter, str | tuple[ParameterParser, ParameterConstructor]]: #: Parameter code to method mapping, c.f. :meth:`_read_hip_param` and/or - #: :meth:`_make_hip_param`. Method names are expected to be referred to the - #: class by ``_read_param_${name}`` and/or ``_make_param_${name}``, and if - #: such name not found, the value should then be a method that can parse the - #: parameter by itself. + #: :meth:`_make_hip_param`. A :obj:`str` value names the methods + #: ``_read_param_${name}`` and ``_make_param_${name}``, falling back to + #: ``_read_param_unassigned`` / ``_make_param_unassigned`` when they do not + #: exist; any other value is a ``(parser, constructor)`` pair of callables. __parameter__ = collections.defaultdict( lambda: 'unassigned', { @@ -971,10 +971,10 @@ def _read_locator(locator: 'Schema_Locator') -> 'Data_LocatorData | IPv6Address' locator: locator data Returns: - * If ``kind`` is ``0`` and ``size`` is ``16``, - returns an :class:`~ipaddress.IPv4Address` object. - * If ``kind`` is ``1`` and ``size`` is ``20``, - returns a :class:`~pcapkit.protocols.data.internet.hip.Locator` object. + * If ``kind`` is ``0`` and the locator is 16 octets, + an :class:`~ipaddress.IPv6Address` object. + * If ``kind`` is ``1`` and the locator is 20 octets, + a :class:`~pcapkit.protocols.data.internet.hip.LocatorData` object. Raises: ProtocolError: in other cases @@ -1052,7 +1052,8 @@ def _read_param_puzzle(self, schema: 'Schema_PuzzleParameter', *, version: 'int' Parsed parameter data. Raises: - ProtocolError: The parameter is **ONLY** supported in HIPv1. + ProtocolError: If ``version`` is ``1`` and ``schema.len`` is **NOT** + ``12``. """ if version == 1 and schema.len != 12: @@ -1110,7 +1111,8 @@ def _read_param_solution(self, schema: 'Schema_SolutionParameter', *, version: ' Parsed parameter data. Raises: - ProtocolError: The parameter is **ONLY** supported in HIPv1. + ProtocolError: If ``version`` is ``1`` and ``schema.len`` is **NOT** + ``20``, or if ``schema.len - 4`` is odd. """ if version == 1 and schema.len != 20: diff --git a/pcapkit/protocols/internet/hopopt.py b/pcapkit/protocols/internet/hopopt.py index 34cbe140d7..0cb6b5a847 100644 --- a/pcapkit/protocols/internet/hopopt.py +++ b/pcapkit/protocols/internet/hopopt.py @@ -14,7 +14,7 @@ Octets Bits Name Description ======= ========= =================== ================================= 0 0 ``hopopt.next`` Next Header - 1 8 ``hopopt.length`` Header Extensive Length + 1 8 ``hopopt.length`` Header Extension Length 2 16 ``hopopt.options`` Options ======= ========= =================== ================================= @@ -189,10 +189,10 @@ class HOPOPT(IPv6_Ext[Data_HOPOPT, Schema_HOPOPT], #: DefaultDict[Enum_Option, str | tuple[OptionParser, OptionConstructor]]: Option #: code to method mapping, c.f. :meth:`_read_hopopt_options` and/or - #: :meth:`_make_hopopt_options`. Method names are expected to be referred - #: to the class by ``_read_opt_${name}`` and/or ``_make_opt_${name}``, and - #: if such name not found, the value should then be a method that can parse - #: the option by itself. + #: :meth:`_make_hopopt_options`. A :obj:`str` value names the methods + #: ``_read_opt_${name}`` and ``_make_opt_${name}``, falling back to + #: ``_read_opt_none`` / ``_make_opt_none`` when they do not exist; any other + #: value is a ``(parser, constructor)`` pair of callables. __option__ = collections.defaultdict( lambda: 'none', { @@ -477,7 +477,7 @@ def _hopopt_option_length(schema_len: 'int') -> 'int': whole option, which is what every ``_read_opt_*`` below reports back as the parsed option's own ``.length``, is two octets more. This is the ``+2``/``-2`` mismatch between the two (see ``Data_PadOption.length`` - vs. ``Schema_PadOption.length`` below). The read-side half lives in this + vs. ``Schema_PadOption.len`` below). The read-side half lives in this one helper so the arithmetic has a single home. Do NOT drop the ``+ 2``. @@ -516,7 +516,8 @@ def _read_hopopt_options(self, length: 'int') -> 'Option': Extracted HOPOPT options Raises: - ProtocolError: If the threshold is **NOT** matching. + ProtocolError: If the parsed options do not total exactly ``length`` + octets. """ counter = 0 # length of read options @@ -657,7 +658,7 @@ def _read_opt_tun(self, schema: 'Schema_TunnelEncapsulationLimitOption', *, opti Parsed option data. Raises: - ProtocolError: If ``hopopt.tun.length`` is **NOT** ``1``. + ProtocolError: If ``schema.len`` is **NOT** ``1``. """ if schema.len != 1: @@ -691,7 +692,7 @@ def _read_opt_ra(self, schema: 'Schema_RouterAlertOption', *, options: 'Option') Parsed option data. Raises: - ProtocolError: If ``hopopt.tun.length`` is **NOT** ``2``. + ProtocolError: If ``schema.len`` is **NOT** ``2``. """ if schema.len != 2: @@ -868,7 +869,7 @@ def _read_opt_pdm(self, schema: 'Schema_PDMOption', *, options: 'Option') -> 'Da Parsed option data. Raises: - ProtocolError: If ``hopopt.pdm.length`` is **NOT** ``10``. + ProtocolError: If ``schema.len`` is **NOT** ``10``. """ if schema.len != 10: @@ -990,7 +991,7 @@ def _read_opt_rpl(self, schema: 'Schema_RPLOption', *, options: 'Option') -> 'Da Parsed option data. Raises: - ProtocolError: If ``hopopt.rpl.length`` is **NOT** ``4``. + ProtocolError: If ``schema.len`` is **NOT** ``4``. """ if schema.len != 4: @@ -1168,7 +1169,7 @@ def _read_opt_jumbo(self, schema: 'Schema_JumboPayloadOption', *, options: 'Opti Parsed option data. Raises: - ProtocolError: If ``hopopt.jumbo.length`` is **NOT** ``4``. + ProtocolError: If ``schema.len`` is **NOT** ``4``. """ if schema.len != 4: @@ -1212,7 +1213,7 @@ def _read_opt_home(self, schema: 'Schema_HomeAddressOption', *, options: 'Option Parsed option data. Raises: - ProtocolError: If ``hopopt.jumbo.length`` is **NOT** ``16``. + ProtocolError: If ``schema.len`` is **NOT** ``16``. """ if schema.len != 16: @@ -1250,7 +1251,7 @@ def _read_opt_ip_dff(self, schema: 'Schema_IPDFFOption', *, options: 'Option') - Parsed option data. Raises: - ProtocolError: If ``hopopt.ip_dff.length`` is **NOT** ``2``. + ProtocolError: If ``schema.len`` is **NOT** ``2``. """ if schema.len != 2: diff --git a/pcapkit/protocols/internet/ipv4.py b/pcapkit/protocols/internet/ipv4.py index fd22768b1d..4d7b703e3b 100644 --- a/pcapkit/protocols/internet/ipv4.py +++ b/pcapkit/protocols/internet/ipv4.py @@ -192,10 +192,10 @@ class IPv4(IP[Data_IPv4, Schema_IPv4], #: DefaultDict[Enum_OptionNumber, str | tuple[OptionParser, OptionConstructor]]: #: Option code to method mapping, c.f. :meth:`_read_ipv4_options` and/or - #: :meth:`_make_ipv4_options`. Method names are expected to be referred - #: to the class by ``_read_opt_${name}`` and/or ``_make_opt_${name}``, and - #: if such name not found, the value should then be a method that can parse - #: the option by itself. + #: :meth:`_make_ipv4_options`. A :obj:`str` value names the methods + #: ``_read_opt_${name}`` and ``_make_opt_${name}``, falling back to + #: ``_read_opt_unassigned`` / ``_make_opt_unassigned`` when they do not + #: exist; any other value is a ``(parser, constructor)`` pair of callables. __option__ = collections.defaultdict( lambda: 'unassigned', { @@ -589,7 +589,7 @@ def _read_ipv4_options(self, length: 'int') -> 'Option': Extracted IPv4 options. Raises: - ProtocolError: If the threshold is **NOT** matching. + ProtocolError: If the parsed options total more than ``length`` octets. """ counter = 0 # length of read option list diff --git a/pcapkit/protocols/internet/ipv6.py b/pcapkit/protocols/internet/ipv6.py index 3ed2c45c11..1cff0c112e 100644 --- a/pcapkit/protocols/internet/ipv6.py +++ b/pcapkit/protocols/internet/ipv6.py @@ -321,11 +321,11 @@ def _make_data(cls, data: 'Data_IPv6') -> 'dict[str, Any]': # type: ignore[over } def _read_ip_hextet(self) -> 'tuple[int, int, int]': - """Read first four hextets of IPv6. + """Read the first four octets of IPv6. Returns: - Parsed hextets data, including version number, traffic class and - flow label. + Parsed data of those four octets: version number, traffic class + and flow label. """ _htet = self._read_fileng(4).hex() @@ -350,7 +350,7 @@ def _decode_next_layer(self, ipv6: 'Data_IPv6', proto: 'Optional[int]' = None, Arguments: ipv6: info buffer - proto: next layer protocol name + proto: next layer protocol index length: valid (*not padding*) length packet: packet info (passed from :meth:`self.unpack `) @@ -365,7 +365,7 @@ def _decode_next_layer(self, ipv6: 'Data_IPv6', proto: 'Optional[int]' = None, raw_len = ipv6.payload # payload length _protos = [] # ProtoChain buffer - # traverse if next header is an extensive header + # traverse if next header is an extension header payload = self.__header__.get_payload() while True: try: @@ -405,30 +405,25 @@ def _decode_next_layer(self, ipv6: 'Data_IPv6', proto: 'Optional[int]' = None, payload = payload[next_.length:] # A layer with no ``next`` field cannot safely continue the walk. - # This is a *structural* check -- does the parsed info even carry a - # ``next`` attribute? -- not a fixed set of codes, and deliberately - # so: HOPOPT, IPv6-Route, IPv6-Opts, MH, HIP, IPv6-Frag and AH all - # have dedicated parsers whose data carries ``next``, and Shim6 and - # any recognised header whose own parser raised are both handled by - # :class:`~pcapkit.protocols.internet.ipv6_ext.IPv6_Ext`, which also - # carries ``next`` (possibly :data:`None`, on an overrun -- see its - # module docstring). Every IANA extension header code without a - # dedicated parser -- ``253`` and ``254`` today, and whatever IANA - # assigns next -- has no generic fallback either (see - # :attr:`__generic_ext_codes__`'s docstring for why), so - # :meth:`_import_next_layer` returns a plain - # :class:`~pcapkit.protocols.misc.raw.Raw`, whose info carries no - # ``next`` at all. Reading ``info.next`` on that unconditionally - # would raise ``AttributeError`` and let a further-out - # :func:`~pcapkit.utilities.decorators.beholder` degrade the *whole* - # packet. Stopping here instead keeps this layer's own fields (still - # recorded above, in ``self._exthdr`` and in the packet dict) and - # reports no further next header, exactly like the overrun case. + # This is a *structural* check, not a fixed set of codes: every + # dedicated parser and + # :class:`~pcapkit.protocols.internet.ipv6_ext.IPv6_Ext` carry + # ``next`` (possibly :data:`None`, on an overrun) -- Shim6 has no + # dedicated parser but is registered to ``IPv6_Ext`` directly -- + # while every other code with no dedicated parser -- ``253``, + # ``254``, or whatever IANA assigns next -- resolves to plain + # :class:`~pcapkit.protocols.misc.raw.Raw`, which does not (see + # :attr:`__generic_ext_codes__`). Reading + # ``info.next`` on that would raise ``AttributeError`` and let a + # further-out :func:`~pcapkit.utilities.decorators.beholder` degrade + # the *whole* packet. Stopping here instead keeps this layer's own + # fields (recorded above, in ``self._exthdr`` and in the packet + # dict) and reports no further next header, as on an overrun. # # This has to run -- and, on a hit, has to set ``proto`` -- # *before* the fragment-header special case below: IPv6-Frag - # always carries a real ``next`` (the ``hasattr`` check above - # never actually fires for it), and that ``next`` is the real + # always carries a real ``next`` (this ``hasattr`` check never + # fires for it), and that ``next`` is the real # transport layer's code, which the fragment branch's own # ``break`` must leave in ``proto`` for the final # ``super()._decode_next_layer`` call below the loop to dispatch diff --git a/pcapkit/protocols/internet/ipv6_opts.py b/pcapkit/protocols/internet/ipv6_opts.py index 39501d38a3..e951287638 100644 --- a/pcapkit/protocols/internet/ipv6_opts.py +++ b/pcapkit/protocols/internet/ipv6_opts.py @@ -14,7 +14,7 @@ Octets Bits Name Description ======= ========= =================== ================================= 0 0 ``opt.next`` Next Header - 1 8 ``opt.length`` Header Extensive Length + 1 8 ``opt.length`` Header Extension Length 2 16 ``opt.options`` Options ======= ========= =================== ================================= @@ -193,10 +193,10 @@ class IPv6_Opts(IPv6_Ext[Data_IPv6_Opts, Schema_IPv6_Opts], #: DefaultDict[Enum_Option, str | tuple[OptionParser, OptionConstructor]]: Option #: code to method mapping, c.f. :meth:`_read_ipv6_opts` and/or - #: :meth:`_make_ipv6_opts`. Method names are expected to be referred to the - #: class by ``_read_opt_${name}`` and/or ``_make_opt_${name}``, and if such - #: name not found, the value should then be a method that can parse the - #: option by itself. + #: :meth:`_make_ipv6_opts`. A :obj:`str` value names the methods + #: ``_read_opt_${name}`` and ``_make_opt_${name}``, falling back to + #: ``_read_opt_none`` / ``_make_opt_none`` when they do not exist; any other + #: value is a ``(parser, constructor)`` pair of callables. __option__ = collections.defaultdict( lambda: 'none', { @@ -334,7 +334,7 @@ def make(self, next_default: Default value of next header type. next_namespace: Namespace of next header type. next_reversed: If the namespace of next header type is reversed. - options: Hop-by-Hop Options. + options: Destination Options. payload: Payload of current protocol. **kwargs: Arbitrary keyword arguments. @@ -469,7 +469,7 @@ def _ipv6_opts_option_length(schema_len: 'int') -> 'int': whole option, which is what every ``_read_opt_*`` below reports back as the parsed option's own ``.length``, is two octets more. This is the ``+2``/``-2`` mismatch between the two (see ``Data_PadOption.length`` - vs. ``Schema_PadOption.length`` below). The read-side half lives in this + vs. ``Schema_PadOption.len`` below). The read-side half lives in this one helper so the arithmetic has a single home. Do NOT drop the ``+ 2``. @@ -504,7 +504,8 @@ def _read_ipv6_opts(self, length: 'int') -> 'Option': Extracted IPv6-Opts options Raises: - ProtocolError: If the threshold is **NOT** matching. + ProtocolError: If the parsed options do not total exactly ``length`` + octets. """ counter = 0 # length of read options @@ -646,7 +647,7 @@ def _read_opt_tun(self, schema: 'Schema_TunnelEncapsulationLimitOption', *, opti Parsed option data. Raises: - ProtocolError: If ``ipv6_opts.tun.length`` is **NOT** ``1``. + ProtocolError: If ``schema.len`` is **NOT** ``1``. """ if schema.len != 1: @@ -680,7 +681,7 @@ def _read_opt_ra(self, schema: 'Schema_RouterAlertOption', *, options: 'Option') Parsed option data. Raises: - ProtocolError: If ``ipv6_opts.tun.length`` is **NOT** ``2``. + ProtocolError: If ``schema.len`` is **NOT** ``2``. """ if schema.len != 2: @@ -857,7 +858,7 @@ def _read_opt_pdm(self, schema: 'Schema_PDMOption', *, options: 'Option') -> 'Da Parsed option data. Raises: - ProtocolError: If ``ipv6_opts.pdm.length`` is **NOT** ``10``. + ProtocolError: If ``schema.len`` is **NOT** ``10``. """ if schema.len != 10: @@ -979,7 +980,7 @@ def _read_opt_rpl(self, schema: 'Schema_RPLOption', *, options: 'Option') -> 'Da Parsed option data. Raises: - ProtocolError: If ``ipv6_opts.rpl.length`` is **NOT** ``4``. + ProtocolError: If ``schema.len`` is **NOT** ``4``. """ if schema.len != 4: @@ -1157,7 +1158,7 @@ def _read_opt_jumbo(self, schema: 'Schema_JumboPayloadOption', *, options: 'Opti Parsed option data. Raises: - ProtocolError: If ``ipv6_opts.jumbo.length`` is **NOT** ``4``. + ProtocolError: If ``schema.len`` is **NOT** ``4``. """ if schema.len != 4: @@ -1201,7 +1202,7 @@ def _read_opt_home(self, schema: 'Schema_HomeAddressOption', *, options: 'Option Parsed option data. Raises: - ProtocolError: If ``ipv6_opts.jumbo.length`` is **NOT** ``16``. + ProtocolError: If ``schema.len`` is **NOT** ``16``. """ if schema.len != 16: @@ -1239,7 +1240,7 @@ def _read_opt_ip_dff(self, schema: 'Schema_IPDFFOption', *, options: 'Option') - Parsed option data. Raises: - ProtocolError: If ``ipv6_opts.ip_dff.length`` is **NOT** ``2``. + ProtocolError: If ``schema.len`` is **NOT** ``2``. """ if schema.len != 2: @@ -1279,7 +1280,7 @@ def _make_pad_options(self, offset: 'int') -> 'tuple[list[Schema_PadOption], int option to 8 octets on its own leaves the options area a multiple of 8 octets long, whereas :meth:`read` sizes it as ``hdr_ext_len * 8 + 6`` -- six short of a multiple of 8 -- so the - constructed header declared 6 octets more than it actually carried. + header would declare 6 octets more than it carried. A ``PadN`` option spends two octets on its own type and ``Opt Data Len`` fields before any padding data, so occupying diff --git a/pcapkit/protocols/internet/ipv6_route.py b/pcapkit/protocols/internet/ipv6_route.py index fd7396e3fd..fdf4bd6383 100644 --- a/pcapkit/protocols/internet/ipv6_route.py +++ b/pcapkit/protocols/internet/ipv6_route.py @@ -13,7 +13,7 @@ Octets Bits Name Description ======= ========= ==================== =============================== 0 0 ``route.next`` Next Header - 1 8 ``route.length`` Header Extensive Length + 1 8 ``route.length`` Header Extension Length 2 16 ``route.type`` Routing Type 3 24 ``route.seg_left`` Segments Left 4 32 ``route.data`` Type-Specific Data @@ -99,10 +99,10 @@ class IPv6_Route(IPv6_Ext[Data_IPv6_Route, Schema_IPv6_Route], ########################################################################## #: DefaultDict[Enum_Routing, str | tuple[TypeParser, TypeConstructor]]: Type - #: code to method mapping. Method names are expected to be referred to - #: the class by ``_read_data_type_${name}`` and/or ``_make_data_type_${name}``, - #: and if such name not found, the value should then be a method that can - #: parse the routing type by itself. + #: code to method mapping. A :obj:`str` value names the methods + #: ``_read_data_type_${name}`` and ``_make_data_type_${name}``, falling back + #: to ``_read_data_type_none`` / ``_make_data_type_none`` when they do not + #: exist; any other value is a ``(parser, constructor)`` pair of callables. __routing__ = collections.defaultdict( lambda: 'none', { @@ -229,16 +229,16 @@ def _make_hdr_ext_len(data_length: 'int') -> 'int': :mod:`pcapkit.protocols.schema.internet.ipv6_route` is the inverse, used on the read side. - This is the *only* place ``Hdr Ext Len`` is computed on the write side. - Computing it separately per branch in :meth:`make` let the copies - drift apart, so keep one helper. Do NOT "simplify" the ``- 4`` / ``/ 8`` + This is the *only* place ``Hdr Ext Len`` is computed on the write side: + separate per-branch copies in :meth:`make` drift apart, so keep one + helper. Do NOT "simplify" the ``- 4`` / ``/ 8`` away: the units either side differ (octets vs. 8-octet units), and dropping the offset silently reinterprets the field. Args: data_length: packed length, in octets, of the type-specific data - (i.e. ``len(data_val.pack())`` for a :class:`~pcapkit.protocols. - schema.schema.Schema`-based payload, or the padded raw + (i.e. ``len(data_val.pack())`` for a + :class:`~pcapkit.protocols.schema.schema.Schema`-based payload, or the padded raw :obj:`bytes` length for the unknown/raw-bytes case). Returns: @@ -337,7 +337,7 @@ def make(self, @classmethod def register_routing(cls, code: 'Enum_Routing', meth: 'str | tuple[TypeParser, TypeConstructor]') -> 'None': - """Register an routing data parser. + """Register a routing data parser. Args: code: IPv6-Route data type code. diff --git a/pcapkit/protocols/internet/mh.py b/pcapkit/protocols/internet/mh.py index 4964a23354..0f7356fde5 100644 --- a/pcapkit/protocols/internet/mh.py +++ b/pcapkit/protocols/internet/mh.py @@ -1160,10 +1160,10 @@ class MH(IPv6_Ext[Data_MH, Schema_MH], ########################################################################## #: DefaultDict[Enum_Packet, str | tuple[PacketParser, PacketConstructor]]: - #: Message type to method mapping. Method names are expected to be referred - #: to the class by ``_read_msg_${name}`` and/or ``_make_msg_${name}``, - #: and if such name not found, the value should then be a method that can - #: parse the message type by itself. + #: Message type to method mapping. A :obj:`str` value names the methods + #: ``_read_msg_${name}`` and ``_make_msg_${name}``, falling back to + #: ``_read_msg_unknown`` / ``_make_msg_unknown`` when they do not exist; any + #: other value is a ``(parser, constructor)`` pair of callables. __message__ = collections.defaultdict( lambda: 'unknown', { @@ -1195,10 +1195,10 @@ class MH(IPv6_Ext[Data_MH, Schema_MH], ) # type: DefaultDict[Enum_Packet | int, str | tuple[PacketParser, PacketConstructor]] #: DefaultDict[Enum_Option, str | tuple[OptionParser, OptionConstructor]]: - #: Option type to method mapping. Method names are expected to be referred - #: to the class by ``_read_opt_${name}`` and/or ``_make_opt_${name}``, - #: and if such name not found, the value should then be a method that can - #: parse the option by itself. + #: Option type to method mapping. A :obj:`str` value names the methods + #: ``_read_opt_${name}`` and ``_make_opt_${name}``, falling back to + #: ``_read_opt_none`` / ``_make_opt_none`` when they do not exist; any other + #: value is a ``(parser, constructor)`` pair of callables. __option__ = collections.defaultdict( lambda: 'none', { @@ -1277,10 +1277,10 @@ class MH(IPv6_Ext[Data_MH, Schema_MH], ) # type: DefaultDict[Enum_Option | int, str | tuple[OptionParser, OptionConstructor]] #: DefaultDict[Enum_CGAExtension, str | tuple[ExtensionParser, ExtensionConstructor]]: - #: CGA extension type to method mapping. Method names are expected to be referred - #: to the class by ``_read_ext_${name}`` and/or ``_make_ext_${name}``, - #: and if such name not found, the value should then be a method that can - #: parse the CGA extension by itself. + #: CGA extension type to method mapping. A :obj:`str` value names the + #: methods ``_read_ext_${name}`` and ``_make_ext_${name}``, falling back to + #: ``_read_ext_none`` / ``_make_ext_none`` when they do not exist; any other + #: value is a ``(parser, constructor)`` pair of callables. __extension__ = collections.defaultdict( lambda: 'none', { @@ -8083,7 +8083,9 @@ def _make_opt_mesg_id(self, type: 'Enum_Option', option: 'Optional[Data_MesgIDOp type: Option type. option: Option data model. timestamp: NTP timestamp, c.f., :rfc:`1305`. - interval: Timestamp interval (since UNIX-epoch). + interval: Moment to encode when neither ``option`` nor + ``timestamp`` is given (``option`` supplies its own + ``ntp_timestamp``); defaults to the current time. **kwargs: Arbitrary keyword arguments. Returns: @@ -8131,7 +8133,7 @@ def _make_opt_cga_pr(self, type: 'Enum_Option', option: 'Optional[Data_CGAParame def _make_opt_cga_param(self, type: 'Enum_Option', option: 'Optional[Data_CGAParametersOption]' = None, *, parameters: 'Optional[list[Schema_CGAParameter | Data_CGAParameter | dict[str, Any] | bytes]]' = None, **kwargs: 'Any') -> 'Schema_CGAParametersOption': - """Make MH CGA paramters option. + """Make MH CGA parameters option. Args: type: Option type. @@ -9295,12 +9297,12 @@ def _make_fid_suboption(self, code: 'Enum_FlowIDSuboption', Note: The data model parameter is named ``option`` rather than ``data`` to match :meth:`_make_opt_fid` and the rest of this module -- and because - naming it ``data`` made it **shadow a field**. The traffic selector and - unassigned sub-options both carry a field of their own called ``data``, - so a caller's ``data=`` bound to the model parameter instead of landing - in ``**kwargs``, and the ``kwargs.get('data')`` fallback below could - never see it: the payload was silently dropped and the length written as - though it were empty. No exception, just a lost field. + ``data`` would **shadow a field**. The traffic selector and unassigned + sub-options both carry a field of their own called ``data``, so a + caller's ``data=`` would bind to the model parameter instead of landing + in ``**kwargs``, and the ``kwargs.get('data')`` fallback below would + never see it: the payload would be silently dropped and the length + written as though it were empty, with no exception. Dispatch is on ``code`` rather than on ``isinstance`` of the schema or data class, for the reason given in :meth:`_read_fid_suboptions`. @@ -9653,7 +9655,7 @@ def _make_ani_suboption(self, code: 'Enum_ANISuboption', The data model parameter is named ``option`` rather than ``data`` so that it cannot shadow a sub-option field of that name -- see - :meth:`_make_fid_suboption`, where it did. Dispatch is on ``code`` + :meth:`_make_fid_suboption`. Dispatch is on ``code`` rather than on ``isinstance``, for the reason given in :meth:`_read_fid_suboptions`. @@ -10288,7 +10290,7 @@ def _make_lcmp_suboption(self, code: 'Enum_LMAControlledMAGSuboption', Note: The data model parameter is named ``option`` rather than ``data`` so that it cannot shadow a sub-option field of that name -- see - :meth:`_make_fid_suboption`, where it did. Dispatch is on ``code`` + :meth:`_make_fid_suboption`. Dispatch is on ``code`` rather than on ``isinstance``, for the reason given in :meth:`_read_fid_suboptions`.