diff --git a/docs/source/pcapkit/corekit/fields/field.rst b/docs/source/pcapkit/corekit/fields/field.rst index 718e5746e0..c7b6012b2f 100644 --- a/docs/source/pcapkit/corekit/fields/field.rst +++ b/docs/source/pcapkit/corekit/fields/field.rst @@ -6,7 +6,7 @@ Base Fields .. versionremoved:: 1.5.0 ``pcapkit.corekit.fields.field.NoValueType``, now - :class:`pcapkit.corekit.sentinels.NoValueType` (:issue:`922`, + :class:`pcapkit.corekit.sentinels.NoValueType` (:issue:`911`, :issue:`1744`), and ``pcapkit.corekit.fields.field.NoValue``, now :data:`pcapkit.corekit.sentinels.NO_VALUE` (:issue:`937`). diff --git a/pcapkit/const/ftp/command.py b/pcapkit/const/ftp/command.py index 88e978fb7a..3d038947a0 100644 --- a/pcapkit/const/ftp/command.py +++ b/pcapkit/const/ftp/command.py @@ -37,8 +37,8 @@ class FEATCode(EnumRegistry, StrEnum): minting the per-command ones at import time as an incidental side effect of building :class:`Command`'s own rows. GitHub issue :issue:`860`: that import-time mutation was the same defect shape :issue:`775` removed - from :class:`~pcapkit.const.pcapng.filter_type.FilterType`, just not - previously noticed here. ``_missing_`` still unmints for a keyword that + from :class:`~pcapkit.const.pcapng.filter_type.FilterType`. ``_missing_`` + still unmints for a keyword that turns up on the wire but names none of these -- the extendability the owner conditionally asked to keep on GitHub issue :issue:`860`: unsure what a FEAT value absent from the RFC's list means in the CSV table, the owner @@ -243,16 +243,16 @@ class defines neither ``get`` nor ``_missing_`` of its own to reconcile class Command(EnumRegistry, StrEnum): """[Command] FTP Command - .. note:: + .. versionchanged:: 1.5.0 - Neither ``_missing_`` nor ``get()`` mints any more. The owner ruled on + Neither ``_missing_`` nor ``get()`` mints a member. The owner ruled on GitHub issue :issue:`860` that ``get`` should not mint: only IANA-registered values are legitimate members, a new one is properly created through :meth:`register`, and ``get`` is not given enough information to create one. Concretely true here -- a bare wire command word carries no - :attr:`feat`/:attr:`desc`/:attr:`type`/:attr:`conf`, so minting one used - to register a permanent member with all four hollowed out to their - defaults; :meth:`register` is the path that can actually supply them. + :attr:`feat`/:attr:`desc`/:attr:`type`/:attr:`conf`, so a minted member + had all four hollowed out to their defaults; :meth:`register` is the + path that can actually supply them. """ @@ -568,10 +568,10 @@ def get(key: 'str', default: 'Optional[str]' = None) -> 'Command': # unchanged. Two calls naming the same command in different # case, e.g. ``get('xyzw')`` and ``get('XYZW')``, therefore build # results that are *not* equal -- each is exactly what its own - # caller passed, which minting's ``_member_map_`` cache used to - # paper over by returning the *first* casing seen for every - # later call regardless of case. Losing that is the one - # observable behaviour change in GitHub issue #860's conversion. + # caller passed. Minting instead returned, through the + # ``_member_map_`` cache, the *first* casing seen for every later + # call regardless of case; that is the one observable behaviour + # change in GitHub issue #860's conversion. return Command._unregistered_member(default if default is not None else key, name) return Command[name] # type: ignore[misc] diff --git a/pcapkit/const/http/method.py b/pcapkit/const/http/method.py index 8d6d4c6159..b648bab3eb 100644 --- a/pcapkit/const/http/method.py +++ b/pcapkit/const/http/method.py @@ -31,7 +31,10 @@ class Method(EnumRegistry, StrEnum): legitimate members, and a new one is created through :meth:`register`, which can supply :attr:`safe` and :attr:`idempotent` where a bare wire method verb carries neither. ``get()`` matches ``key`` case-sensitively, - as :rfc:`9110` makes the method token (:issue:`896`). + as :rfc:`9110` makes the method token (:issue:`896`), and checks it + against member values as well as names (:issue:`908`). A member's + :class:`str` payload is its value, so ``Method.GET == 'GET'`` + (:issue:`870`). """ @@ -199,21 +202,13 @@ def _unregistered_member(cls, value: 'str', name: 'str') -> 'Method': declared, never reformatted. Only :attr:`name` -- the identifier, not the value -- is canonicalised. - A *registered* member of this class used to carry something - different here: GitHub issue :issue:`870` found that - :meth:`__new__` called ``str.__new__(cls)`` with no - argument at all, so every one of the 40 declared members' - own :class:`str` payload was permanently empty regardless - of ``value`` (``str(Method.GET) == ''``, and - ``Method.GET == 'GET'`` was :obj:`False`) -- true on - ``main`` at ``60b85e3a4`` as well as when this docstring - was first written. :issue:`870` fixed :meth:`__new__` to - ``str.__new__(cls, value)``, mirroring + A *registered* member carries its value the same way: + :meth:`__new__` passes ``value`` to ``str.__new__``, as :class:`~pcapkit.const.ftp.command.Command`'s own - ``__new__``, so a registered member's payload now agrees - with an *unregistered* member built through this method: - both carry their own value as real :class:`str` content - (``str(Method('frob')) == 'frob'``). + ``__new__`` does (:issue:`870`), so both kinds of member + carry their own value as real :class:`str` content + (``str(Method.GET) == 'GET'`` and + ``str(Method('frob')) == 'frob'``). name: Bare label for the unregistered member -- here, the canonical upper-case form of ``value``, matching the name every *registered* member of this class is looked up by, @@ -231,32 +226,31 @@ def get(cls, key: 'str', default: 'Optional[str]' = None) -> 'Method': """Backport support for original codes. Delegates to :meth:`~pcapkit.corekit.enum.EnumLookup.get` for the - lookup itself, per GitHub issue :issue:`908`: the previous override checked - only ``_member_map_`` (names), never ``_value2member_map_`` - (values), so the two IANA methods whose member *name* differs from - their *value* -- ``BASELINE_CONTROL`` / ``'BASELINE-CONTROL'`` and + lookup itself, so ``key`` is checked against member values as well + as names (GitHub issue :issue:`908`). That is what resolves the two + IANA methods whose member *name* differs from their *value* -- + ``BASELINE_CONTROL`` / ``'BASELINE-CONTROL'`` and ``VERSION_CONTROL`` / ``'VERSION-CONTROL'``, a hyphen being unusable - in a Python identifier -- failed to resolve through ``get`` even - though :meth:`_missing_` (and so the constructor) already found - them via that same value-side lookup. - - The base is a :class:`classmethod` - (:meth:`~pcapkit.corekit.enum.EnumLookup.get`), and a zero-argument - ``super()`` needs a first argument to bind, so this override had to - move from :class:`staticmethod` to :class:`classmethod` to delegate - at all -- see GitHub issue :issue:`908`'s own correction of the fix it - originally proposed. Callers are unaffected by the switch itself: - ``Method.get('X')`` binds identically either way. - - ``default`` keeps its existing meaning here rather than adopting - the base's ``NO_DEFAULT`` sentinel and its value-only fallback -- - widening it would be caller-visible, since the base's ``default`` - must already name a *registered* value (resolved through - ``_value2member_map_``), while this override's ``default`` instead - supplies the *value* of a freshly minted unregistered member. - Nothing in this tree calls ``get`` with a non-``None`` ``default`` - to notice today, but widening the signature is a separate change - from this defect and stays out of scope. + in a Python identifier -- which :meth:`_missing_` (and so the + constructor) also finds by value. + + It is a :class:`classmethod`, as the base is, because a + zero-argument ``super()`` needs a first argument to bind + (:issue:`908`). ``Method.get('X')`` binds the same as it would on a + :class:`staticmethod`. + + ``default`` does not follow the base's ``NO_DEFAULT`` sentinel and + value-only fallback: the base's ``default`` must name a + *registered* value (resolved through ``_value2member_map_``), while + this override's ``default`` supplies the *value* of the + unregistered member it builds on a miss. + + .. versionchanged:: 1.5.0 + + A non-:class:`str` ``key`` raises :exc:`ValueError` + (:issue:`908`). ``get(42)`` and ``get(None)`` raised + :exc:`AttributeError` from ``key.upper()``, and ``get(b'GET')`` + raised :exc:`TypeError`. Args: key: Key to get enum item. Looked up case-**sensitively**, @@ -267,26 +261,20 @@ def get(cls, key: 'str', default: 'Optional[str]' = None) -> 'Method': command codes are not. Checked against both member names and values, via the base's own precedence -- name before value -- so a value-only match such as - ``'BASELINE-CONTROL'`` now resolves too, closing GitHub - issue :issue:`908`. + ``'BASELINE-CONTROL'`` resolves too (:issue:`908`). default: Value for the unregistered member built when ``key`` matches neither a name nor a value. ``None``, the - default, uses ``key`` itself -- unchanged from before - GitHub issue :issue:`908`. + default, uses ``key`` itself. Raises: ValueError: If ``key`` is not a :class:`str`. This reaches the caller from the base's non-``str`` branch, which calls ``cls(key)`` and so ``_missing_``, and is **not** caught by the ``except KeyError`` fallback below -- only a failed - *name* lookup is. The non-``str`` surface therefore moved - with GitHub issue :issue:`908`: ``get(42)`` and ``get(None)`` used - to raise :exc:`AttributeError` from ``key.upper()``, and - ``get(b'GET')`` used to *return* a member whose name and - value were both the :class:`bytes` object. Raising is the - intended behaviour -- a ``bytes``-valued ``Method`` is not a - thing this registry should hand back -- but it is a change, - so it is stated rather than left to be discovered. + *name* lookup is. Raising is the intended behaviour for + ``get(b'GET')`` as well: a member whose name and + value were both the :class:`bytes` object is not a thing + this registry should hand back. :meta private: """ diff --git a/pcapkit/const/ipv6/smf_dpd_mode.py b/pcapkit/const/ipv6/smf_dpd_mode.py index 18e966a284..11ff5a2ff2 100644 --- a/pcapkit/const/ipv6/smf_dpd_mode.py +++ b/pcapkit/const/ipv6/smf_dpd_mode.py @@ -5,7 +5,7 @@ .. module:: pcapkit.const.ipv6.smf_dpd_mode -This module contains the constant enumeration for **Simplified Multicast Forwarding Duplicate Packet Detection (``SMF_DPD``) Options**, +This module contains the constant enumeration for **Simplified Multicast Forwarding Duplicate Packet Detection (SMF_DPD) Options**, which is automatically generated from :class:`pcapkit.vendor.ipv6.smf_dpd_mode.SMFDPDMode`. """ diff --git a/pcapkit/const/pcapng/option_type.py b/pcapkit/const/pcapng/option_type.py index 419a0205da..3e075b83a2 100644 --- a/pcapkit/const/pcapng/option_type.py +++ b/pcapkit/const/pcapng/option_type.py @@ -344,15 +344,15 @@ def get(key: 'int | str', default: 'int' = -1, *, namespace: 'str' = 'opt') -> ' temp_ns.update(OptionType.__members_ns__.get(namespace, {})) if key in temp_ns: return temp_ns[key] - # NOTE: GitHub issue #860. This used to extend_enum(...) a + # NOTE: GitHub issue #860. An extend_enum(...) here would mint a # permanent member as a side effect of a mere lookup -- exactly # the ``get``/``_missing_`` minting the owner's ruling forbids, # here on the wire-facing path # (:meth:`pcapkit.protocols.misc.pcapng.PCAPNG._make_pcapng_options` # calls this with raw option-code bytes off the wire). # ``_unregistered_member`` never touches ``__members_ns__`` - # either, so a genuinely unknown option code no longer grows - # either lookup table. + # either, so a genuinely unknown option code grows neither + # lookup table. return OptionType._unregistered_member(key, '%s_unknown' % namespace) if key in OptionType.__members__: return getattr(OptionType, key) @@ -360,12 +360,12 @@ def get(key: 'int | str', default: 'int' = -1, *, namespace: 'str' = 'opt') -> ' # string, so ``get(member.value)`` resolves it as ``cls(value)`` would. if key in OptionType._value2member_map_: # type: ignore[misc] return OptionType._value2member_map_[key] # type: ignore[misc,return-value] - # NOTE: same ruling, the str-keyed path: this used to mint ``key`` - # itself as the member's name with ``default`` as its value. Neither + # NOTE: same ruling, the str-keyed path: rather than mint ``key`` + # itself as the member's name with ``default`` as its value -- neither # ``get`` nor ``_missing_`` has enough information to register one - # properly -- there is no namespace, description or anything else - # IANA would attach to it -- so this now returns an unregistered - # pseudo-member with the same attributes the old mint would have set. + # properly, as there is no namespace, description or anything else + # IANA would attach to it -- this returns an unregistered + # pseudo-member with the same attributes such a mint would set. return OptionType._unregistered_member(default, key) @classmethod diff --git a/pcapkit/const/reg/apptype/apptype.py b/pcapkit/const/reg/apptype/apptype.py index 2e7f12bd7f..6f5142d737 100644 --- a/pcapkit/const/reg/apptype/apptype.py +++ b/pcapkit/const/reg/apptype/apptype.py @@ -27,7 +27,18 @@ class TransportProtocol(EnumLookup, IntEnum): - """Transport layer protocol.""" + """Transport layer protocol. + + .. versionchanged:: 1.5.0 + + An ``aenum.IntEnum`` rather than an ``aenum.IntFlag`` (:issue:`808`), + numbered sequentially: ``sctp`` is ``3`` and ``dccp`` is ``4``, where + they were ``4`` and ``8``, and no :class:`int` is decoded as a ``|`` + composite (:issue:`860`). :meth:`get` refuses a name no member + declares, with :exc:`~pcapkit.utilities.exceptions.EnumKeyError`, + rather than minting a member for it (:issue:`808`, :issue:`923`). + + """ # mypy has no aenum plugin, so this class is a plain class to it: every # member below is cast rather than left as a bare auto()-valued one -- @@ -222,6 +233,15 @@ class AppType(EnumRegistry, StrEnum): makes it subclassable at all, since :mod:`aenum` refuses to extend an enumeration that already has members. + .. versionchanged:: 1.5.0 + + Split by transport protocol (:issue:`732`): :mod:`pcapkit.const.reg.apptype` + is a package, and every assignment is a member of one of the four + registries above rather than of this class. ``get()`` raises + :exc:`ValueError` for a port outside ``0``-``65535`` (:issue:`758`), and + returns an unregistered member rather than minting one for a port the + registry does not assign (:issue:`860`). + Note: The rows below are the registry entries whose **transport protocol column is empty**. A blank transport is not an assignment to anything, so there @@ -2528,9 +2548,9 @@ def _dispatch(cls, key: 'int', proto: 'TransportProtocol | str | int') -> 'Type[ of the work for :issue:`808` -- so it is looked up as the one whole value it already is, exactly like any other bare int: refused when that whole value names no registry, resolved when it - happens to equal one instead. Since GitHub issue :issue:`860` moved - this class off power-of-two spacing, a hand-built composite - is no longer guaranteed to be the former -- see + happens to equal one instead. With the sequential values of + GitHub issue :issue:`860`, a hand-built composite + is not guaranteed to be the former -- see :meth:`_dispatch`'s own body for a value that resolves. Returns: @@ -2755,10 +2775,10 @@ def register(cls, port: 'int', name: 'str') -> 'AppType': # pylint: disable=arg The counterpart of :meth:`register_alias`: that method requires ``port`` to already carry a member and adds a further name for it; this requires the opposite -- ``port`` unclaimed in this registry -- - and mints the first. :meth:`get` can no longer do this itself as of + and mints the first. :meth:`get` does not do this itself, per GitHub issue :issue:`860` (the owner's ruling there: only IANA-registered values are legitimate, a new entry is properly created through :meth:`register`, - and ``get`` lacks the information to create one), so this is now the only + and ``get`` lacks the information to create one), so this is the only way to add a service this registry does not already carry. Deliberately scoped like :meth:`register_alias` to **this** @@ -2900,9 +2920,9 @@ def _unregistered_member(cls, value: 'int', name: 'str', its :data:`__registry__` side table alike. Used by :meth:`_missing_` and :meth:`get` for a port neither resolves - to a real, IANA-registered service, so that such a lookup no longer - grows either table -- contrast :meth:`register`, the explicit, - caller-named path that still does. + to a real, IANA-registered service, so that such a lookup grows + neither table -- contrast :meth:`register`, the explicit, + caller-named path that does. Reconstructs :attr:`svc`, :attr:`port` and :attr:`proto` the same way :meth:`__new__` would: diff --git a/pcapkit/corekit/enum.py b/pcapkit/corekit/enum.py index 18488ef5e9..bce15d46eb 100644 --- a/pcapkit/corekit/enum.py +++ b/pcapkit/corekit/enum.py @@ -35,7 +35,7 @@ into, which is :meth:`EnumLookup._validate_value`. Legality is every enumeration's concern; mutation is only the open registries'. -Supporting measurement, taken on this tree at the time of the split: **0** of the +Supporting measurement, taken when :issue:`877` split the tiers: **0** of the 26 non-registry enumerations define ``register``, ``register_alias``, ``register_aliases`` or ``_unregistered_member``, and there are **0** :class:`EnumRegistry` subclasses outside :mod:`pcapkit.const`. The mutating half diff --git a/pcapkit/corekit/fields/field.py b/pcapkit/corekit/fields/field.py index 302d398651..a841933d64 100644 --- a/pcapkit/corekit/fields/field.py +++ b/pcapkit/corekit/fields/field.py @@ -9,7 +9,7 @@ .. versionremoved:: 1.5.0 ``pcapkit.corekit.fields.field.NoValueType``, now - :class:`pcapkit.corekit.sentinels.NoValueType` (:issue:`922`, + :class:`pcapkit.corekit.sentinels.NoValueType` (:issue:`911`, :issue:`1744`), and ``pcapkit.corekit.fields.field.NoValue``, now :data:`pcapkit.corekit.sentinels.NO_VALUE` (:issue:`937`). diff --git a/pcapkit/corekit/fields/misc.py b/pcapkit/corekit/fields/misc.py index 95ff592b75..79218d5c19 100644 --- a/pcapkit/corekit/fields/misc.py +++ b/pcapkit/corekit/fields/misc.py @@ -586,7 +586,7 @@ def nested_packet_context(packet: 'dict[str, Any]') -> 'dict[str, Any]': the inner ``length`` overwrite the outer one mid-pack. Two consequences of it being a copy rather than a live view, both - deliberate and neither reached by any current call site. A name + deliberate and neither reached by any call site in this package. A name deleted from the returned mapping is simply gone, rather than reverting to the enclosing schema's value. And the copy is taken when this function is called, so a later mutation of ``packet`` is not @@ -602,8 +602,8 @@ def nested_packet_context(packet: 'dict[str, Any]') -> 'dict[str, Any]': raised on :issue:`439`, that a :class:`~collections.ChainMap` here corrupted the :class:`~abc.ABCMeta` cache :class:`Schema ` subclasses shared on CPython - <= 3.10. The suspicion is *probably* wrong and can no longer be - tested: the cache keys on the **exact type queried**, so asking about a + <= 3.10. The suspicion is *probably* wrong, and this code cannot + test it: the cache keys on the **exact type queried**, so asking about a :class:`~collections.ChainMap` instance caches lookups for :class:`~collections.ChainMap` and not for :class:`dict`, the poisoning observed in :issue:`439` came from ordinary code asking @@ -842,7 +842,7 @@ def __call__(self, packet: 'dict[str, Any]') -> 'Self': Returns: Updated field instance. - This method will return a new instance of :class:`ConditionalField` + This method will return a new instance of :class:`ForwardMatchField` instead of updating the current instance. """ diff --git a/pcapkit/corekit/fields/numbers.py b/pcapkit/corekit/fields/numbers.py index 8b5b4dd0cf..657eef92e9 100644 --- a/pcapkit/corekit/fields/numbers.py +++ b/pcapkit/corekit/fields/numbers.py @@ -818,8 +818,7 @@ def _unregistered_member(namespace: 'Type[StdlibEnum] | Type[AenumEnum]', wire value should resolve to a real member of the registry the field names -- ``isinstance`` against it and every ancestor holds, and it renders and dispatches exactly like a declared one -- provided - building it never grows the registry, which is the whole reason the - field does not call ``get()`` unconditionally. + building it never grows the registry. This is what gets there: it calls ``namespace``'s own storage base's ``__new__`` directly -- :class:`str` or :class:`int`, whichever ``namespace`` derives from -- which skips ``namespace``'s *own* @@ -850,12 +849,13 @@ def _unregistered_member(namespace: 'Type[StdlibEnum] | Type[AenumEnum]', this way, which is what keeps this safe to return from ``post_process``. A direct call to :meth:`~pcapkit.const.reg.apptype.apptype.AppType.get` for the same port - is a different matter and deliberately so: asking the - registry for a name is an explicit request for a named member, so - it still mints one -- measured on this tree, - ``AppType.get(54321, proto=tcp)`` returns ``PORT_54321_tcp`` and - takes ``TCP.__members__`` from 6147 to 6148, and a second call - with the same port returns that member rather than raising. Two + does not come through here, and it does not mint either: it + builds an unregistered member of its own, through the registry's + ``_unregistered_member`` -- measured on this tree, + ``AppType.get(54321, proto=tcp)`` returns + ````, ``TCP.__members__`` stays at + 6147, and a second call with the same port returns an equal + member rather than the same one. Two unregistered members for the same value also compare equal without being identical, since :class:`~pcapkit.const.reg.apptype.apptype.AppType` and diff --git a/pcapkit/corekit/infoclass.py b/pcapkit/corekit/infoclass.py index 4dcbd26e8d..8e325afbf1 100644 --- a/pcapkit/corekit/infoclass.py +++ b/pcapkit/corekit/infoclass.py @@ -391,6 +391,15 @@ def __new__(cls, *args: 'VT', **kwargs: 'VT') -> 'Self': # pylint: disable=unus the same signature as specified in class variables' type annotations, which is inspired by :pep:`557` (:mod:`dataclasses`). + Note: + Whether ``cls`` still needs finalising is read from its *own* + ``__dict__``, never inherited (GitHub issue :issue:`1490`). Each + class is finalised on its first construction, whatever its + ancestors' state, so a subclass of a :attr:`FinalisedState.BASE` or + :attr:`FinalisedState.FINAL` ancestor still gets its own + ``__builtin__``, ``__excluded__`` and generated ``__init__``, and a + ``@final`` class under a ``BASE`` ancestor is refused like any other. + Args: *args: Arbitrary positional arguments. **kwargs: Arbitrary keyword arguments. @@ -401,15 +410,6 @@ def __new__(cls, *args: 'VT', **kwargs: 'VT') -> 'Self': # pylint: disable=unus :func:`info_final`, i.e. it carries ``__final__`` in its own ``__dict__`` without :attr:`FinalisedState.FINAL`. - Note: - Whether ``cls`` still needs finalising is read from its *own* - ``__dict__``, never inherited (GitHub issue :issue:`1490`). Each - class is finalised on its first construction, whatever its - ancestors' state, so a subclass of a :attr:`FinalisedState.BASE` or - :attr:`FinalisedState.FINAL` ancestor still gets its own - ``__builtin__``, ``__excluded__`` and generated ``__init__``, and a - ``@final`` class under a ``BASE`` ancestor is refused like any other. - """ # NOTE: ``final`` is applied *after* the class object exists, so # :meth:`__init_subclass__` has already run and returned by the time a diff --git a/pcapkit/corekit/module.py b/pcapkit/corekit/module.py index fa853796c6..e5ce8c9583 100644 --- a/pcapkit/corekit/module.py +++ b/pcapkit/corekit/module.py @@ -43,6 +43,12 @@ class can be imported by ``from module import name``.""" def klass(self) -> 'Type[_T]': """Import class from module. + .. versionchanged:: 1.5.0 + + A missing class raises + :exc:`~pcapkit.utilities.exceptions.ProtocolError` rather than a bare + :exc:`AttributeError` (:issue:`832`). + Raises: pcapkit.utilities.exceptions.ProtocolError: If :attr:`name` is :data:`~pcapkit.corekit.sentinels.NULL` -- the caller building diff --git a/pcapkit/corekit/sentinels.py b/pcapkit/corekit/sentinels.py index 1e0d6c278f..36860f3d54 100644 --- a/pcapkit/corekit/sentinels.py +++ b/pcapkit/corekit/sentinels.py @@ -15,7 +15,7 @@ The owner's ruling on GitHub issue :issue:`911` chose one shared module over one module per sentinel, and this module is the only import path for all four: the module of the class that uses each one does not re-export it -(:issue:`1744`), per the ruling on :issue:`1715` that a moved name keeps no +(:issue:`1744`), per the ruling recorded on :issue:`1103` that a moved name keeps no re-export at its old path. :mod:`pcapkit.corekit.module`, :mod:`pcapkit.corekit.fields.field`, :mod:`pcapkit.corekit.enum` and :mod:`pcapkit.protocols.protocol` import the object they use from here, but diff --git a/pcapkit/vendor/__main__.py b/pcapkit/vendor/__main__.py index fb0cd9eae9..fd31c8697f 100644 --- a/pcapkit/vendor/__main__.py +++ b/pcapkit/vendor/__main__.py @@ -100,16 +100,14 @@ def _snapshot_and_restore(vendor: 'Type[Vendor]') -> 'Iterator[None]': not backed up stops the target before it starts. Both leave the previous file exactly as it was, for different reasons. - A symlinked destination has a related divergence from the pre-:issue:`872` - behaviour -- documented against the write path by rounds 4-8 (deleted - along with ``_write_atomic``, though the underlying behaviour persists - here instead): ``open(const_file, 'w')`` writes *through* a symlink, + A symlinked destination is handled differently by the write and by the + restore: ``open(const_file, 'w')`` writes *through* a symlink, into whatever file it points at, leaving the link itself untouched. ``os.replace(backup, const_file)`` on restore instead replaces the *link itself* with a regular file. Measured, for ``link.py`` symlinked to ``real.py``: after a failure and restore, ``real.py`` is left however the crawler's failed write left it (truncated, in this case -- nothing - here restores the file a symlink used to point at), ``link.py`` is now a + here restores the file the symlink pointed at), ``link.py`` is a regular file holding the backup's content, and ``os.path.islink(link.py)`` is :data:`False`. ``find pcapkit/const -type l`` is still empty, so this remains latent. diff --git a/pcapkit/vendor/default.py b/pcapkit/vendor/default.py index 7720787380..dd85ae1b7f 100644 --- a/pcapkit/vendor/default.py +++ b/pcapkit/vendor/default.py @@ -79,7 +79,7 @@ .. module:: {MODL.replace('vendor', 'const')} -This module contains the constant enumeration for **{name}**, +This module contains the constant enumeration for **{name.replace('``', '')}**, which is automatically generated from :class:`{MODL}.{NAME}`. """ @@ -262,10 +262,10 @@ def split_references(text: 'str') -> 'list[str]': """Split an IANA reference cell into its citations. IANA's CSV exports have spelt a cell citing two documents in two ways - (:issue:`1664`). It used to be ``[RFC1072][RFC6247]``, or one citation - per line of a quoted cell. The port number registry began writing - ``RFC 3501`` by 2026-09-29, and every registry re-exported on 2026-10-13 - (17:26Z to 17:54Z) now reads ``[RFC 1072]; [RFC 6247]``. Both give + (:issue:`1664`). The older form is ``[RFC1072][RFC6247]``, or one + citation per line of a quoted cell. The port number registry began + writing ``RFC 3501`` by 2026-09-29, and every registry re-exported on + 2026-10-13 (17:26Z to 17:54Z) reads ``[RFC 1072]; [RFC 6247]``. Both give ``['RFC1072', 'RFC6247']`` here, so every crawler renders them identically: @@ -491,25 +491,18 @@ def _dest_path(cls) -> 'str': """Resolve the ``const/`` file this crawler's module mirrors. :attr:`~Vendor.__module__` sits somewhere under the :mod:`pcapkit.vendor` - package -- today always exactly one level down, e.g. - :mod:`pcapkit.vendor.reg.apptype`, but :issue:`732` needs deeper nesting such as - :mod:`pcapkit.vendor.reg.apptype.tcp`. The output file mirrors that same - position, whatever its depth, under :mod:`pcapkit.const` instead. - - This used to split the module's absolute path into exactly two levels - (``ROOT, STEM = os.path.split(temp)``) and assume the second was always - the module's one-and-only position under ``vendor/``. That holds for the - flat ``vendor//.py`` layout every crawler has today, but - breaks for any deeper nesting -- e.g. ``vendor/reg/apptype/tcp.py`` - resolved to ``vendor/const/apptype/tcp.py``, *inside* ``vendor/`` itself - rather than under ``pcapkit/const/`` at all. - - Anchoring on the ``vendor`` package's own ``__file__`` instead of a fixed - split count handles arbitrary nesting: whatever path a module sits at + package, at any depth -- e.g. :mod:`pcapkit.vendor.reg.linktype` one + level down, or :mod:`pcapkit.vendor.reg.apptype.tcp` (:issue:`732`) + two. The output file mirrors that same position under + :mod:`pcapkit.const`. + + The position is taken relative to the ``vendor`` package's own + ``__file__`` rather than by splitting the module's path a fixed number + of times, so any nesting works: whatever path a module sits at *relative to* ``vendor/``, the same relative path is mirrored under - ``const/``, which also happens to be what the flat case already did -- - so every crawler that exists today keeps writing to exactly the file it - writes to now. + ``const/``. A fixed two-level split would send + ``vendor/reg/apptype/tcp.py`` to ``vendor/const/apptype/tcp.py``, + *inside* ``vendor/`` itself rather than under ``pcapkit/const/``. ``pcapkit.vendor`` is imported here, inside the method, rather than at module scope: it imports *this* module while it is still initialising diff --git a/pcapkit/vendor/ftp/command.py b/pcapkit/vendor/ftp/command.py index 866eb65b1e..331af584d0 100644 --- a/pcapkit/vendor/ftp/command.py +++ b/pcapkit/vendor/ftp/command.py @@ -77,8 +77,8 @@ class FEATCode(EnumRegistry, StrEnum): minting the per-command ones at import time as an incidental side effect of building :class:`Command`'s own rows. GitHub issue :issue:`860`: that import-time mutation was the same defect shape :issue:`775` removed - from :class:`~pcapkit.const.pcapng.filter_type.FilterType`, just not - previously noticed here. ``_missing_`` still unmints for a keyword that + from :class:`~pcapkit.const.pcapng.filter_type.FilterType`. ``_missing_`` + still unmints for a keyword that turns up on the wire but names none of these -- the extendability the owner conditionally asked to keep on GitHub issue :issue:`860`: unsure what a FEAT value absent from the RFC's list means in the CSV table, the owner @@ -255,16 +255,16 @@ class defines neither ``get`` nor ``_missing_`` of its own to reconcile class {NAME}(EnumRegistry, StrEnum): """[{NAME}] {DOCS} - .. note:: + .. versionchanged:: 1.5.0 - Neither ``_missing_`` nor ``get()`` mints any more. The owner ruled on + Neither ``_missing_`` nor ``get()`` mints a member. The owner ruled on GitHub issue :issue:`860` that ``get`` should not mint: only IANA-registered values are legitimate members, a new one is properly created through :meth:`register`, and ``get`` is not given enough information to create one. Concretely true here -- a bare wire command word carries no - :attr:`feat`/:attr:`desc`/:attr:`type`/:attr:`conf`, so minting one used - to register a permanent member with all four hollowed out to their - defaults; :meth:`register` is the path that can actually supply them. + :attr:`feat`/:attr:`desc`/:attr:`type`/:attr:`conf`, so a minted member + had all four hollowed out to their defaults; :meth:`register` is the + path that can actually supply them. """ @@ -402,10 +402,10 @@ def get(key: 'str', default: 'Optional[str]' = None) -> '{NAME}': # unchanged. Two calls naming the same command in different # case, e.g. ``get('xyzw')`` and ``get('XYZW')``, therefore build # results that are *not* equal -- each is exactly what its own - # caller passed, which minting's ``_member_map_`` cache used to - # paper over by returning the *first* casing seen for every - # later call regardless of case. Losing that is the one - # observable behaviour change in GitHub issue #860's conversion. + # caller passed. Minting instead returned, through the + # ``_member_map_`` cache, the *first* casing seen for every later + # call regardless of case; that is the one observable behaviour + # change in GitHub issue #860's conversion. return {NAME}._unregistered_member(default if default is not None else key, name) return {NAME}[name] # type: ignore[misc] @@ -481,12 +481,12 @@ def process(self, data: 'list[str]') -> 'tuple[list[str], list[str]]': if isinstance(feat, str): # NOTE: GitHub issue #860. Every FEAT code the registry # names -- lower-case group marker or upper-case per-command - # keyword alike -- is now a declared member of FEATCode (see + # keyword alike -- is a declared member of FEATCode (see # feat_codes below and FEATCode's own template), so both - # cases resolve by plain attribute access; neither ever - # calls FEATCode(...) at import time any more, which used to - # mint the upper-case ones as a side effect of evaluating - # this very module. + # cases resolve by plain attribute access, and nothing calls + # FEATCode(...) at import time, which would mint the + # upper-case ones as a side effect of evaluating this very + # module. if feat.isupper() and feat not in feat_codes: feat_codes[feat] = f'#: {cmmt}\n {feat} = {feat!r}' feat = f'FEATCode.{feat}' diff --git a/pcapkit/vendor/http/method.py b/pcapkit/vendor/http/method.py index c2e02234a5..52fabe8598 100644 --- a/pcapkit/vendor/http/method.py +++ b/pcapkit/vendor/http/method.py @@ -55,7 +55,10 @@ class {NAME}(EnumRegistry, StrEnum): legitimate members, and a new one is created through :meth:`register`, which can supply :attr:`safe` and :attr:`idempotent` where a bare wire method verb carries neither. ``get()`` matches ``key`` case-sensitively, - as :rfc:`9110` makes the method token (:issue:`896`). + as :rfc:`9110` makes the method token (:issue:`896`), and checks it + against member values as well as names (:issue:`908`). A member's + :class:`str` payload is its value, so ``Method.GET == 'GET'`` + (:issue:`870`). """ @@ -104,21 +107,13 @@ def _unregistered_member(cls, value: 'str', name: 'str') -> '{NAME}': declared, never reformatted. Only :attr:`name` -- the identifier, not the value -- is canonicalised. - A *registered* member of this class used to carry something - different here: GitHub issue :issue:`870` found that - :meth:`__new__` called ``str.__new__(cls)`` with no - argument at all, so every one of the 40 declared members' - own :class:`str` payload was permanently empty regardless - of ``value`` (``str(Method.GET) == ''``, and - ``Method.GET == 'GET'`` was :obj:`False`) -- true on - ``main`` at ``60b85e3a4`` as well as when this docstring - was first written. :issue:`870` fixed :meth:`__new__` to - ``str.__new__(cls, value)``, mirroring + A *registered* member carries its value the same way: + :meth:`__new__` passes ``value`` to ``str.__new__``, as :class:`~pcapkit.const.ftp.command.Command`'s own - ``__new__``, so a registered member's payload now agrees - with an *unregistered* member built through this method: - both carry their own value as real :class:`str` content - (``str(Method('frob')) == 'frob'``). + ``__new__`` does (:issue:`870`), so both kinds of member + carry their own value as real :class:`str` content + (``str(Method.GET) == 'GET'`` and + ``str(Method('frob')) == 'frob'``). name: Bare label for the unregistered member -- here, the canonical upper-case form of ``value``, matching the name every *registered* member of this class is looked up by, @@ -136,32 +131,31 @@ def get(cls, key: 'str', default: 'Optional[str]' = None) -> '{NAME}': """Backport support for original codes. Delegates to :meth:`~pcapkit.corekit.enum.EnumLookup.get` for the - lookup itself, per GitHub issue :issue:`908`: the previous override checked - only ``_member_map_`` (names), never ``_value2member_map_`` - (values), so the two IANA methods whose member *name* differs from - their *value* -- ``BASELINE_CONTROL`` / ``'BASELINE-CONTROL'`` and + lookup itself, so ``key`` is checked against member values as well + as names (GitHub issue :issue:`908`). That is what resolves the two + IANA methods whose member *name* differs from their *value* -- + ``BASELINE_CONTROL`` / ``'BASELINE-CONTROL'`` and ``VERSION_CONTROL`` / ``'VERSION-CONTROL'``, a hyphen being unusable - in a Python identifier -- failed to resolve through ``get`` even - though :meth:`_missing_` (and so the constructor) already found - them via that same value-side lookup. - - The base is a :class:`classmethod` - (:meth:`~pcapkit.corekit.enum.EnumLookup.get`), and a zero-argument - ``super()`` needs a first argument to bind, so this override had to - move from :class:`staticmethod` to :class:`classmethod` to delegate - at all -- see GitHub issue :issue:`908`'s own correction of the fix it - originally proposed. Callers are unaffected by the switch itself: - ``{NAME}.get('X')`` binds identically either way. - - ``default`` keeps its existing meaning here rather than adopting - the base's ``NO_DEFAULT`` sentinel and its value-only fallback -- - widening it would be caller-visible, since the base's ``default`` - must already name a *registered* value (resolved through - ``_value2member_map_``), while this override's ``default`` instead - supplies the *value* of a freshly minted unregistered member. - Nothing in this tree calls ``get`` with a non-``None`` ``default`` - to notice today, but widening the signature is a separate change - from this defect and stays out of scope. + in a Python identifier -- which :meth:`_missing_` (and so the + constructor) also finds by value. + + It is a :class:`classmethod`, as the base is, because a + zero-argument ``super()`` needs a first argument to bind + (:issue:`908`). ``{NAME}.get('X')`` binds the same as it would on a + :class:`staticmethod`. + + ``default`` does not follow the base's ``NO_DEFAULT`` sentinel and + value-only fallback: the base's ``default`` must name a + *registered* value (resolved through ``_value2member_map_``), while + this override's ``default`` supplies the *value* of the + unregistered member it builds on a miss. + + .. versionchanged:: 1.5.0 + + A non-:class:`str` ``key`` raises :exc:`ValueError` + (:issue:`908`). ``get(42)`` and ``get(None)`` raised + :exc:`AttributeError` from ``key.upper()``, and ``get(b'GET')`` + raised :exc:`TypeError`. Args: key: Key to get enum item. Looked up case-**sensitively**, @@ -172,26 +166,20 @@ def get(cls, key: 'str', default: 'Optional[str]' = None) -> '{NAME}': command codes are not. Checked against both member names and values, via the base's own precedence -- name before value -- so a value-only match such as - ``'BASELINE-CONTROL'`` now resolves too, closing GitHub - issue :issue:`908`. + ``'BASELINE-CONTROL'`` resolves too (:issue:`908`). default: Value for the unregistered member built when ``key`` matches neither a name nor a value. ``None``, the - default, uses ``key`` itself -- unchanged from before - GitHub issue :issue:`908`. + default, uses ``key`` itself. Raises: ValueError: If ``key`` is not a :class:`str`. This reaches the caller from the base's non-``str`` branch, which calls ``cls(key)`` and so ``_missing_``, and is **not** caught by the ``except KeyError`` fallback below -- only a failed - *name* lookup is. The non-``str`` surface therefore moved - with GitHub issue :issue:`908`: ``get(42)`` and ``get(None)`` used - to raise :exc:`AttributeError` from ``key.upper()``, and - ``get(b'GET')`` used to *return* a member whose name and - value were both the :class:`bytes` object. Raising is the - intended behaviour -- a ``bytes``-valued ``Method`` is not a - thing this registry should hand back -- but it is a change, - so it is stated rather than left to be discovered. + *name* lookup is. Raising is the intended behaviour for + ``get(b'GET')`` as well: a member whose name and + value were both the :class:`bytes` object is not a thing + this registry should hand back. :meta private: """ diff --git a/pcapkit/vendor/ipx/packet.py b/pcapkit/vendor/ipx/packet.py index 059f9734cc..af777b0037 100644 --- a/pcapkit/vendor/ipx/packet.py +++ b/pcapkit/vendor/ipx/packet.py @@ -32,19 +32,19 @@ __all__ = ['Packet'] ############################################################################### -# NOTE: this crawler no longer crawls, and the registry below is maintained by -# hand; see #518. It is the same failure, on the same article and the same -# removal revision, that retired the sibling socket crawler in #507. +# NOTE: this crawler does not crawl: the registry below is maintained by hand; +# see #518. It is the same failure, on the same article and the same removal +# revision, that retired the sibling socket crawler in #507. # -# It used to scrape the packet-type table out of +# Its source was the packet-type table in # https://en.wikipedia.org/wiki/Internetwork_Packet_Exchange#IPX_packet_structure, -# and two separate things broke that. Wikipedia now answers ``requests``' +# and two separate things broke that. Wikipedia answers ``requests``' # default ``python-requests/`` User-Agent with HTTP 403 (measured # 2026-09-19: 403 and 126 bytes of robot-policy text for the default agent, 200 # and 114117 bytes for a descriptive one). That half is fixed in -# ``Vendor._request``, which now sends the descriptive agent +# ``Vendor._request``, which sends the descriptive agent # :func:`~pcapkit.vendor.default.get_user_agent` builds -- but it only gets the -# crawler as far as a page that no longer holds the data. The table was deleted +# crawler as far as a page that does not hold the data. The table was deleted # from the article on 2026-08-25, in revision 1371327031 ("deleted some # irrelevant technical tables"), leaving the live page (revision 1372814585) # with a single ``wikitable`` -- the IPX header format one, headers @@ -54,7 +54,8 @@ # Pointing ``LINK`` at the last revision that still carries the table # (``oldid=1368657333``, re-measured 2026-09-19: 200, 118834 bytes, 4 # ``wikitable``s, ``table[1]`` headers ``['Value', 'Meaning/Protocol']``, 8 body -# rows) would have worked, and running the old ``process()`` against it +# rows) would have worked, and running the crawler's original ``process()`` +# against it # reproduces the committed constant file byte for byte. It is still the wrong # fix: a network fetch pinned to a frozen snapshot is all of the fragility of a # crawler with none of the freshness, and it would go on selecting a table by @@ -62,8 +63,8 @@ # # What settles it is that the registry is closed. IPX packet types were Novell's # to assign -- :rfc:`1362`, :rfc:`1551` and :rfc:`1634` all say "Packet Types -# also need to be assigned by Novell" -- and there is no longer a Novell to -# assign them. IPX will not gain new packet types, so there is nothing for a +# also need to be assigned by Novell" -- and no Novell remains to assign +# them. IPX will not gain new packet types, so there is nothing for a # crawler to pick up on its next run. # # The table below is therefore transcribed from that last revision that carried @@ -79,7 +80,8 @@ #: above it. Names go through :meth:`~Vendor.rename`, so the generated member is #: the :meth:`~Vendor.safe_name` of the first element; comments are stored as the #: scrape rendered them, reStructuredText markup and all, since the prose-mangling -#: the old ``process()`` did to Wikipedia's cell text has no source left to mangle. +#: the crawler's original ``process()`` did to Wikipedia's cell text has no source +#: left to mangle. #: #: Every value below is independently corroborated by Wireshark's IPX dissector, #: ``epan/dissectors/packet-ipx.h`` (fetched 2026-09-19), which defines @@ -105,7 +107,7 @@ 1: ('RIP', '``RIP``, Routing Information Protocol ([:rfc:`1582`], [:rfc:`2091`])'), # XNS IDP inheritances. The archived revision is the only source found for - # these two names: no RFC and no Novell document reachable today lists + # these two names: no RFC and no reachable Novell document lists # either against a packet-type number. Wireshark corroborates the values as # ``ECHO 2`` and ``ERROR 3``. 2: ('Echo Packet', 'Echo Packet'), diff --git a/pcapkit/vendor/ipx/socket.py b/pcapkit/vendor/ipx/socket.py index f48fb03cf1..d03f819e7c 100644 --- a/pcapkit/vendor/ipx/socket.py +++ b/pcapkit/vendor/ipx/socket.py @@ -32,12 +32,12 @@ __all__ = ['Socket'] ############################################################################### -# NOTE: this crawler no longer crawls, and the registry below is maintained by -# hand; see #507. +# NOTE: this crawler does not crawl: the registry below is maintained by hand; +# see #507. # -# It used to scrape the well-known-socket table out of +# Its source was the well-known-socket table in # https://en.wikipedia.org/wiki/Internetwork_Packet_Exchange#Socket_number, -# and two separate things broke that. Wikipedia now answers ``requests``' +# and two separate things broke that. Wikipedia answers ``requests``' # default ``python-requests/`` User-Agent with HTTP 403 (measured # 2026-09-19: 403 and 126 bytes of robot-policy text for the default agent, # 200 and 114117 bytes for a descriptive one), so ``Vendor._request`` could not @@ -55,7 +55,7 @@ # registry itself is closed, which is what settles it -- IPX socket numbers # were Novell's to assign, and Novell's own documentation still ends the list # with "software developers writing NetWare applications can contact Novell to -# reserve well-known sockets". There is no longer a Novell to contact, and IPX +# reserve well-known sockets". No Novell remains to contact, and IPX # will not gain new socket numbers, so there is nothing for a crawler to pick # up on its next run. # @@ -257,7 +257,7 @@ #: a notation for readers; the ruling on :issue:`775` kept ``Registered by Xerox`` out #: of this set on that test, because a proprietary protocol may expose no name #: of its own, so the company name serves as its name. :issue:`775`'s final round -#: converts it too, so this set no longer decides *whether* a range registers -- +#: converts it too, so this set does not decide *whether* a range registers -- #: only *how its name is spelled*: these four keep the bare label with no hex #: suffix, since the label itself already says nothing was assigned, while #: ``Registered by Xerox`` keeps the hex-suffixed name it always rendered. diff --git a/pcapkit/vendor/pcapng/block_type.py b/pcapkit/vendor/pcapng/block_type.py index bb9009ab97..602329f184 100644 --- a/pcapkit/vendor/pcapng/block_type.py +++ b/pcapkit/vendor/pcapng/block_type.py @@ -58,8 +58,8 @@ # unrelated ```` elements from a navigation template, which can change # whenever the service is redeployed, is gratuitous risk for no gain. # -# NOTE: the draft has since moved to the OPSAWG working group as -# ``draft-ietf-opsawg-pcapng``, currently at ``-05``. Tracking it is a separate +# NOTE: the draft moved to the OPSAWG working group as +# ``draft-ietf-opsawg-pcapng``. Tracking it is a separate # change, not a URL swap: the newer revisions alter the registries, and from # ``draft-ietf-opsawg-pcapng-03`` onwards ``process()`` below dies with # ``ValueError: invalid literal for int() with base 16: '0x0A0D0AXX'`` on a diff --git a/pcapkit/vendor/pcapng/option_type.py b/pcapkit/vendor/pcapng/option_type.py index 7c68716732..3f36452921 100644 --- a/pcapkit/vendor/pcapng/option_type.py +++ b/pcapkit/vendor/pcapng/option_type.py @@ -254,15 +254,15 @@ def get(key: 'int | str', default: 'int' = -1, *, namespace: 'str' = 'opt') -> ' temp_ns.update({NAME}.__members_ns__.get(namespace, {{}})) if key in temp_ns: return temp_ns[key] - # NOTE: GitHub issue #860. This used to extend_enum(...) a + # NOTE: GitHub issue #860. An extend_enum(...) here would mint a # permanent member as a side effect of a mere lookup -- exactly # the ``get``/``_missing_`` minting the owner's ruling forbids, # here on the wire-facing path # (:meth:`pcapkit.protocols.misc.pcapng.PCAPNG._make_pcapng_options` # calls this with raw option-code bytes off the wire). # ``_unregistered_member`` never touches ``__members_ns__`` - # either, so a genuinely unknown option code no longer grows - # either lookup table. + # either, so a genuinely unknown option code grows neither + # lookup table. return {NAME}._unregistered_member(key, '%s_unknown' % namespace) if key in {NAME}.__members__: return getattr({NAME}, key) @@ -270,12 +270,12 @@ def get(key: 'int | str', default: 'int' = -1, *, namespace: 'str' = 'opt') -> ' # string, so ``get(member.value)`` resolves it as ``cls(value)`` would. if key in {NAME}._value2member_map_: # type: ignore[misc] return {NAME}._value2member_map_[key] # type: ignore[misc,return-value] - # NOTE: same ruling, the str-keyed path: this used to mint ``key`` - # itself as the member's name with ``default`` as its value. Neither + # NOTE: same ruling, the str-keyed path: rather than mint ``key`` + # itself as the member's name with ``default`` as its value -- neither # ``get`` nor ``_missing_`` has enough information to register one - # properly -- there is no namespace, description or anything else - # IANA would attach to it -- so this now returns an unregistered - # pseudo-member with the same attributes the old mint would have set. + # properly, as there is no namespace, description or anything else + # IANA would attach to it -- this returns an unregistered + # pseudo-member with the same attributes such a mint would set. return {NAME}._unregistered_member(default, key) @classmethod diff --git a/pcapkit/vendor/pcapng/tls_key_label.py b/pcapkit/vendor/pcapng/tls_key_label.py index 8d3cc7ebbe..da9841be99 100644 --- a/pcapkit/vendor/pcapng/tls_key_label.py +++ b/pcapkit/vendor/pcapng/tls_key_label.py @@ -121,8 +121,8 @@ def process(self, data: 'list[str]') -> 'tuple[list[str], list[str]]': # mis-cite a row citing a different RFC entirely -- section 4.2 is # specifically RFC 9850's own "TLS SSLKEYLOGFILE Labels" registry # section, not a fact about any other RFC's structure. All ten - # rows fetched as of 2026-09-28 are ``[RFC9850]``, so this always - # takes the ``rfc == '9850'`` branch today; a future registry + # rows fetched on 2026-09-28 are ``[RFC9850]``, so each takes the + # ``rfc == '9850'`` branch; a future registry # change either still fits it or raises here for a human to look # at, rather than rendering a citation nobody checked. IANA has # spelt it ``[RFC 9850]`` since 2026-10-13, which fits too (#1664). @@ -140,8 +140,8 @@ def process(self, data: 'list[str]') -> 'tuple[list[str], list[str]]': pref = f"{renm} = '{label}'" # NOTE: mirrors the ``# nosec B105`` bandit suppression every - # such member already carries in - # the hand-written ``pcapkit.protocols.misc.pcapng.TLSKeyLabel`` as of #882 -- + # such member carried in the hand-written + # ``pcapkit.protocols.misc.pcapng.TLSKeyLabel`` (#882) -- # bandit's B105 (hardcoded password string) flags a string # constant assigned to a name containing ``SECRET``, which eight # of these ten labels' names do. diff --git a/pcapkit/vendor/reg/apptype/apptype.py b/pcapkit/vendor/reg/apptype/apptype.py index 3a0a67f329..bc626b183c 100644 --- a/pcapkit/vendor/reg/apptype/apptype.py +++ b/pcapkit/vendor/reg/apptype/apptype.py @@ -120,7 +120,18 @@ class TransportProtocol(EnumLookup, IntEnum): - """Transport layer protocol.""" + """Transport layer protocol. + + .. versionchanged:: 1.5.0 + + An ``aenum.IntEnum`` rather than an ``aenum.IntFlag`` (:issue:`808`), + numbered sequentially: ``sctp`` is ``3`` and ``dccp`` is ``4``, where + they were ``4`` and ``8``, and no :class:`int` is decoded as a ``|`` + composite (:issue:`860`). :meth:`get` refuses a name no member + declares, with :exc:`~pcapkit.utilities.exceptions.EnumKeyError`, + rather than minting a member for it (:issue:`808`, :issue:`923`). + + """ # mypy has no aenum plugin, so this class is a plain class to it: every # member below is cast rather than left as a bare auto()-valued one -- @@ -315,6 +326,15 @@ class {NAME}(EnumRegistry, StrEnum): makes it subclassable at all, since :mod:`aenum` refuses to extend an enumeration that already has members. + .. versionchanged:: 1.5.0 + + Split by transport protocol (:issue:`732`): :mod:`pcapkit.const.reg.apptype` + is a package, and every assignment is a member of one of the four + registries above rather than of this class. ``get()`` raises + :exc:`ValueError` for a port outside ``0``-``65535`` (:issue:`758`), and + returns an unregistered member rather than minting one for a port the + registry does not assign (:issue:`860`). + Note: The rows below are the registry entries whose **transport protocol column is empty**. A blank transport is not an assignment to anything, so there @@ -483,9 +503,9 @@ def _dispatch(cls, key: 'int', proto: 'TransportProtocol | str | int') -> 'Type[ of the work for :issue:`808` -- so it is looked up as the one whole value it already is, exactly like any other bare int: refused when that whole value names no registry, resolved when it - happens to equal one instead. Since GitHub issue :issue:`860` moved - this class off power-of-two spacing, a hand-built composite - is no longer guaranteed to be the former -- see + happens to equal one instead. With the sequential values of + GitHub issue :issue:`860`, a hand-built composite + is not guaranteed to be the former -- see :meth:`_dispatch`'s own body for a value that resolves. Returns: @@ -710,10 +730,10 @@ def register(cls, port: 'int', name: 'str') -> '{NAME}': # pylint: disable=argu The counterpart of :meth:`register_alias`: that method requires ``port`` to already carry a member and adds a further name for it; this requires the opposite -- ``port`` unclaimed in this registry -- - and mints the first. :meth:`get` can no longer do this itself as of + and mints the first. :meth:`get` does not do this itself, per GitHub issue :issue:`860` (the owner's ruling there: only IANA-registered values are legitimate, a new entry is properly created through :meth:`register`, - and ``get`` lacks the information to create one), so this is now the only + and ``get`` lacks the information to create one), so this is the only way to add a service this registry does not already carry. Deliberately scoped like :meth:`register_alias` to **this** @@ -855,9 +875,9 @@ def _unregistered_member(cls, value: 'int', name: 'str', its :data:`__registry__` side table alike. Used by :meth:`_missing_` and :meth:`get` for a port neither resolves - to a real, IANA-registered service, so that such a lookup no longer - grows either table -- contrast :meth:`register`, the explicit, - caller-named path that still does. + to a real, IANA-registered service, so that such a lookup grows + neither table -- contrast :meth:`register`, the explicit, + caller-named path that does. Reconstructs :attr:`svc`, :attr:`port` and :attr:`proto` the same way :meth:`__new__` would: @@ -1198,15 +1218,13 @@ def records(self, data: 'list[str]') -> 'tuple[OrderedDict[str, Record], list[st flag = self.flag([proto]) claim = '' if proto == 'undefined' else f' and cls.__transport__ is {flag}' - # NOTE: GitHub issue #860. This used to extend_enum(...) a - # brand-new, permanent member under a generated Python - # identifier (``f'{self.safe_name(svc)}_{{value}}'``) every - # time a port in this span was looked up. It no longer takes a - # Python identifier at all -- an unregistered member is never - # set as a class attribute, so there is nothing for one to - # collide with -- only the bare label IANA (or this span) - # assigns the whole range, exactly what used to be the third - # positional argument to extend_enum. + # NOTE: GitHub issue #860. A port in this span resolves to an + # unregistered member rather than an extend_enum(...)-ed, + # permanent one, so the branch takes no Python identifier at + # all -- an unregistered member is never set as a class + # attribute, so there is nothing for one to collide with -- + # only the bare label IANA (or this span) assigns the whole + # range. miss.append(f'if {start} <= value <= {stop}{claim}:') miss.append(f' #: {cmmt}') miss.append(f' return cls._unregistered_member(value, {svc!r}, {flag})') diff --git a/pcapkit/vendor/reg/ethertype.py b/pcapkit/vendor/reg/ethertype.py index 832fc7ecfe..24fe684d3a 100644 --- a/pcapkit/vendor/reg/ethertype.py +++ b/pcapkit/vendor/reg/ethertype.py @@ -40,7 +40,7 @@ class EtherType(Vendor): #: convert to :meth:`~pcapkit.corekit.enum.EnumRegistry._unregistered_ #: member`, on the theory that a proprietary protocol's company name IS #: the final concrete name for every other row. :issue:`775`'s final round - #: converts every row alike, so this set no longer decides *whether* a + #: converts every row alike, so this set does not decide *whether* a #: row registers -- only *how its name is spelled*: these two keep the #: bare label with no hex suffix, since the label itself already says #: nothing was assigned, while every other row keeps the hex-suffixed diff --git a/pcapkit/vendor/reg/linktype.py b/pcapkit/vendor/reg/linktype.py index d0e8064066..e4b1ee1674 100644 --- a/pcapkit/vendor/reg/linktype.py +++ b/pcapkit/vendor/reg/linktype.py @@ -93,7 +93,7 @@ def process(self, data: 'list[Tag]') -> 'tuple[list[str], list[str]]': # a leading sign, PEP 515 underscores -- rather than the narrower # ``str.isdigit()``, so it recognises exactly the same values the # main loop's own ``int(temp)`` below does. A signed or underscored - # value is not reachable in tcpdump's live table today (every + # value is not present in tcpdump's live table (every # committed member matches a plain unsigned decimal), but a # narrower recognition set here than in the main loop would be the # same class of silent under-count this pre-scan exists to close