From f26540778ee887338abfc271a7f1841f8bc3acc0 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Fri, 2 Oct 2026 12:22:29 -0400 Subject: [PATCH 1/5] docs(corekit): state the enum.py rulings as statements, not quotations Part of #987 (group A), following the direction set out in #719. - Replace the 15 quoted maintainer passages in pcapkit/corekit/enum.py with statements of the rule, the reason, and the issue where it was settled (#877, #842, #775, #923). - Two sites that cited nothing now inherit their sibling's issue (#877 for the range-validation hook, #842 for "all four methods exist on every const enum"); the two register_alias passages are sourced to #842. - Docstrings only: the token stream with string literals masked is identical to origin/main. tests/corekit under plain unittest: 400 tests, 5 failures (the documented purge_modules limitation), 16 skipped. --- pcapkit/corekit/enum.py | 99 ++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/pcapkit/corekit/enum.py b/pcapkit/corekit/enum.py index 276e87137..f877604a7 100644 --- a/pcapkit/corekit/enum.py +++ b/pcapkit/corekit/enum.py @@ -18,20 +18,21 @@ :meth:`~EnumRegistry._unregistered_member`. Every generated enumeration under :mod:`pcapkit.const` inherits from here. -That split is the owner's ruling on GitHub issue #877, verbatim: *"My initial -thought was to make them immutable - unless RFC/IANA says otherwise. Therefore -they may subclass a bare base enum from pcapkit.corekit.enum - where -EnumRegistry subclasses it for using in the other mutable ones."* - -Which methods land on which tier was settled in the same thread. The owner's own -second thought is what drew the line: *"if it carries ``register``, then why not -``register_alias``. We might be creating a bad ruling."* Following that through, +That split is the rule set on GitHub issue #877: a helper enumeration is +immutable by default, unless RFC or IANA documents its value space as open. +Such closed sets subclass a bare base enumeration in this module, and +:class:`EnumRegistry` subclasses that base for the mutable ones. + +Which methods land on which tier was settled in the same thread. The deciding +consideration was that a base carrying ``register`` has no principled reason to +withhold ``register_alias``, so drawing the line between them risked setting a +bad precedent. Following that through, a base holding both would leave :class:`EnumRegistry` with only ``register_aliases``, ``_extend`` and ``_unregistered_member`` -- too thin to justify a second class, collapsing the two tiers into one. So all five mutating -methods stay put, and what the base carries instead is the owner's other requirement, -verbatim: *"there must be some sort of range validation logic for the inherited -classes to hook in"* -- which is :meth:`EnumLookup._validate_value`. Legality is +methods stay put, and what the base carries instead is the other requirement +from that thread: some range-validation logic that inheriting classes can hook +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 @@ -52,12 +53,12 @@ `__, for the remaining seven once the files holding them freed up. -The registry tier's own shape is the earlier ruling on GitHub issue #842, -verbatim: *"to finalise the abstraction idea, get/get_all/register/register_alias -should always exist on the const enums - so they're to be moved to the base -class. And AppType's sub-base class will do its necessary overrides and -dispatching logic; AppType subclasses will have their necessary overrides again -pertaining their different contracts."* +The registry tier's own shape is the earlier rule set on GitHub issue #842: +``get``, ``get_all``, ``register`` and ``register_alias`` exist on every const +enumeration, so the abstraction is finished by moving them to the base class. +``AppType``'s sub-base class carries the overrides and dispatching logic it +needs, and ``AppType``'s subclasses override again where their contracts +differ. That is a three-tier hierarchy, of which this module is **tier one**: @@ -84,11 +85,12 @@ ``register``, ``register_alias`` or ``get_all`` at all. Adding one method meant editing every bespoke template by hand, which is the cost #775 asks to remove. -The contracts are the maintainer's, verbatim: *"get is a shortcut for ``[]`` -operation and returns the canonical enum. get_all returns all matching enums. -register mints new enum to the class at runtime with specified names - so we -don't have to guess blindly. register_alias(es) adds additional alias(es) to a -given enum's mapping."* +The contracts are those set out on GitHub issue #842: ``get`` is a shortcut for +the ``[]`` operation and returns the canonical enumeration member; ``get_all`` +returns every matching member; ``register`` mints a new member on the class at +runtime under caller-specified names, so nothing has to be guessed; +``register_alias`` (and ``register_aliases``) adds further alias names to a +given member's mapping. """ from typing import TYPE_CHECKING @@ -160,9 +162,8 @@ class EnumLookup: def _validate_value(cls, value: 'Any') -> 'None': """Hook: reject ``value`` if this enumeration's contract does not allow it. - The owner's requirement on GitHub issue #877, verbatim: *"there must be - some sort of range validation logic for the inherited classes to hook - in."* This is that hook, and it is what the bare tier carries **instead** + GitHub issue #877 requires some range-validation logic for the inheriting + classes to hook into. This is that hook, and it is what the bare tier carries **instead** of ``register``: what values are *legal* is something every enumeration has an opinion on, whereas who may *add* one is only an open registry's concern. @@ -181,9 +182,9 @@ def _validate_value(cls, value: 'Any') -> 'None': type is :obj:`None` deliberately rather than the validated value, so that this hook cannot become a converter: a subclass that returned a changed value here would silently alter what a lookup resolves to, which is - exactly the case-folding the owner's ruling on GitHub issue #877 rules - out -- *"enum should honour and keep their original writings as in the - registrars."* Case handling belongs in a deliberate ``get`` override with + exactly the case-folding the ruling on GitHub issue #877 rules out: + an enumeration keeps the original spellings its registrars use. + Case handling belongs in a deliberate ``get`` override with an RFC behind it, not in a validation hook. Raise from :mod:`pcapkit.utilities.exceptions`, per the same issue's @@ -254,9 +255,8 @@ def get(cls, key: 'Any', default: 'Any' = NO_DEFAULT) -> 'Self': ``EtherType`` and ``Socket``, so they no longer mint on any path either. Registering a member any other way is :meth:`register`'s job and nobody else's, which is the ruling #775 exists to carry - out: *"so that we dont create registered enums out of - unrecognised/unregistered values, unless user/caller explicitly - created them"*. A value inside a registry's declared-but-unassigned + out: an unrecognised or unregistered value does not become a registered + member unless a user or caller explicitly creates one. A value inside a registry's declared-but-unassigned range still resolves, through that registry's own ``_missing_`` and :meth:`_unregistered_member`, to a member that is deliberately absent from the lookup tables -- true outside the one registry @@ -342,11 +342,10 @@ def get(cls, key: 'Any', default: 'Any' = NO_DEFAULT) -> 'Self': given on :meth:`_validate_value` itself. Both failure paths raise from :mod:`pcapkit.utilities.exceptions` - rather than a builtin, per a ruling recorded on GitHub issue #923, - verbatim: *"Either ``ValueError`` or ``KeyError``, that's depending on - how stdlib's ``Enum`` would raise on these circumstances. And we - should raise one from ``pcapkit.utilities.exceptions`` rather builtin - exceptions."* The *shape* is unchanged by that ruling and + rather than a builtin, per the ruling recorded on GitHub issue #923: + in-library code raises from ``pcapkit.utilities.exceptions`` rather + than a builtin, and whether ``ValueError`` or ``KeyError`` applies + follows what stdlib's ``Enum`` raises in the same circumstance. The *shape* is unchanged by that ruling and deliberately so -- a name miss stays :exc:`KeyError`-derived and a value miss :exc:`ValueError`-derived, matching ``E['nosuch']`` and ``E(999)`` on a stdlib @@ -435,8 +434,8 @@ def get_all(cls, key: 'Any') -> 'tuple[Self, ...]': registry inheriting this base unmodified -- that tuple holds exactly one entry, since an alias registered by :meth:`register_alias` is a second *name* for the canonical member rather than a second member. The method - still exists here, per the ruling that all four *"should always exist on - the const enums"*, and it is where a registry with genuinely several + still exists here, because all four methods must exist on every const + enumeration (GitHub issue #842), and it is where a registry with genuinely several matches puts them: ``AppType`` overrides it to return every service IANA assigns to a port. @@ -471,8 +470,8 @@ class EnumRegistry(EnumLookup): An enumeration inherits from *here* when it may grow at runtime, and from :class:`EnumLookup` directly when it may not. The owner's ruling on GitHub - issue #877 is what draws that line, verbatim: *"My initial thought was to - make them immutable - unless RFC/IANA says otherwise."* + issue #877 is what draws that line: an enumeration is immutable unless + RFC or IANA says otherwise. Mixed in ahead of the enum base exactly as before -- ``class Foo(EnumRegistry, IntFlag)`` -- and gaining :class:`EnumLookup` as a parent @@ -486,8 +485,8 @@ def register(cls, value: 'Any', name: 'str') -> 'Self': """Mint a new member on this registry at runtime, under ``name``. The caller-named path, and the only one that grows the registry: - *"register mints new enum to the class at runtime with specified names - - so we don't have to guess blindly"*. Contrast :meth:`get` and + it mints a new member on the class at runtime under names the caller + specifies, so nothing has to be guessed (GitHub issue #842). Contrast :meth:`get` and ``_missing_``, which resolve without naming anything. Refuses a ``value`` that already has a member. Without this guard, @@ -577,14 +576,14 @@ def _extend(cls, value: 'Any', name: 'str') -> 'Self': def register_alias(cls, value: 'Any', name: 'str') -> 'Self': """Add ``name`` as a further name for the member already at ``value``. - Per the ruling, an alias *"adds additional alias(es) to a given enum's - mapping"* -- so it needs an enum to be given, and this refuses a value - no member carries rather than falling through to :meth:`register`. - Asked whether that should hold generally, the maintainer's answer was - *"actually i think it should always be for an existing member"*, and on - what an alias means away from ``AppType``: *"For non-AppType registries, - 'Alias' is custom/caller-opt-in names, which are not recorded in IANA - registrars"*. Minting under the name of an aliasing call would + Per GitHub issue #842, an alias adds a further name to a given member's + mapping -- so it needs an existing member to attach to, and this refuses + a value no member carries rather than falling through to + :meth:`register`. That holds for every registry; the one exception is + ``AppType`` and the concrete enumerations, which must call it for + members that do not exist yet. What an alias means also differs away + from ``AppType``: on every other registry it is a custom name the + caller opts into, not one recorded by the IANA registrars. Minting under the name of an aliasing call would manufacture exactly the unrecorded member #775 removes. Membership is tested against ``_value2member_map_`` rather than by From 3524f52ab7550002a3eef5bd70e1f61ea79ffe23 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Fri, 2 Oct 2026 12:50:04 -0400 Subject: [PATCH 2/5] docs(corekit): drop an alias exception that no registry implements The cross-review on #990 found the alias passage asserting that AppType and the concrete enumerations are an exception which must call register_alias for members that do not exist yet. No registry does that, and AppType is stricter than the base rather than exempt: - base, enum.py:615-618, raises when the value is in no member map; - AppType.register_alias, const/reg/apptype/apptype.py:2860-2871, raises when __registry__ is None, when getlist(port) is empty, and when the name is already on that port. Its own docstring requires port to carry a member. Only three register_alias definitions exist, the third being the vendor template emitter. The passage also resolved an open conditional on #842 into a settled fact plus a "must"; it now states what the code does. - Re-wrap six passages: maximum line length 115 -> 96, restoring main's. - "the rule set on" read as a compound noun at two sites; reworded. - register's prose said "names" against its single name parameter. Prose only: tokenising with every string masked gives identical sequences before and after. tests/corekit: 400 passed, 16 skipped, 658 subtests. --- pcapkit/corekit/enum.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/pcapkit/corekit/enum.py b/pcapkit/corekit/enum.py index f877604a7..e22451767 100644 --- a/pcapkit/corekit/enum.py +++ b/pcapkit/corekit/enum.py @@ -18,7 +18,7 @@ :meth:`~EnumRegistry._unregistered_member`. Every generated enumeration under :mod:`pcapkit.const` inherits from here. -That split is the rule set on GitHub issue #877: a helper enumeration is +That split follows the rules laid down in GitHub issue #877: a helper enumeration is immutable by default, unless RFC or IANA documents its value space as open. Such closed sets subclass a bare base enumeration in this module, and :class:`EnumRegistry` subclasses that base for the mutable ones. @@ -53,7 +53,7 @@ `__, for the remaining seven once the files holding them freed up. -The registry tier's own shape is the earlier rule set on GitHub issue #842: +The registry tier's own shape is the earlier design settled in GitHub issue #842: ``get``, ``get_all``, ``register`` and ``register_alias`` exist on every const enumeration, so the abstraction is finished by moving them to the base class. ``AppType``'s sub-base class carries the overrides and dispatching logic it @@ -163,8 +163,8 @@ def _validate_value(cls, value: 'Any') -> 'None': """Hook: reject ``value`` if this enumeration's contract does not allow it. GitHub issue #877 requires some range-validation logic for the inheriting - classes to hook into. This is that hook, and it is what the bare tier carries **instead** - of ``register``: what values are *legal* is something every enumeration + classes to hook into. This is that hook, and it is what the bare tier carries + **instead** of ``register``: what values are *legal* is something every enumeration has an opinion on, whereas who may *add* one is only an open registry's concern. @@ -256,10 +256,10 @@ def get(cls, key: 'Any', default: 'Any' = NO_DEFAULT) -> 'Self': either. Registering a member any other way is :meth:`register`'s job and nobody else's, which is the ruling #775 exists to carry out: an unrecognised or unregistered value does not become a registered - member unless a user or caller explicitly creates one. A value inside a registry's declared-but-unassigned - range still resolves, through that registry's own ``_missing_`` and - :meth:`_unregistered_member`, to a member that is deliberately - absent from the lookup tables -- true outside the one registry + member unless a user or caller explicitly creates one. A value inside a + registry's declared-but-unassigned range still resolves, through that + registry's own ``_missing_`` and :meth:`_unregistered_member`, to a member + that is deliberately absent from the lookup tables -- true outside the one registry named above, where such a value instead lands in *both* tables, exactly as :meth:`register` would leave it -- for a non-``str`` key; the ``str`` case is qualified below. Both describe ``key`` resolution @@ -345,8 +345,8 @@ def get(cls, key: 'Any', default: 'Any' = NO_DEFAULT) -> 'Self': rather than a builtin, per the ruling recorded on GitHub issue #923: in-library code raises from ``pcapkit.utilities.exceptions`` rather than a builtin, and whether ``ValueError`` or ``KeyError`` applies - follows what stdlib's ``Enum`` raises in the same circumstance. The *shape* is unchanged by that ruling and - deliberately so -- a name miss + follows what stdlib's ``Enum`` raises in the same circumstance. The *shape* + is unchanged by that ruling and deliberately so -- a name miss stays :exc:`KeyError`-derived and a value miss :exc:`ValueError`-derived, matching ``E['nosuch']`` and ``E(999)`` on a stdlib :class:`~enum.Enum`, and matching the 119 of this tree's 127 concrete @@ -485,9 +485,9 @@ def register(cls, value: 'Any', name: 'str') -> 'Self': """Mint a new member on this registry at runtime, under ``name``. The caller-named path, and the only one that grows the registry: - it mints a new member on the class at runtime under names the caller - specifies, so nothing has to be guessed (GitHub issue #842). Contrast :meth:`get` and - ``_missing_``, which resolve without naming anything. + it mints a new member on the class at runtime under the ``name`` the + caller specifies, so nothing has to be guessed (GitHub issue #842). + Contrast :meth:`get` and ``_missing_``, which resolve without naming anything. Refuses a ``value`` that already has a member. Without this guard, :func:`~aenum.extend_enum` does not mint anything for an already-taken @@ -579,12 +579,13 @@ def register_alias(cls, value: 'Any', name: 'str') -> 'Self': Per GitHub issue #842, an alias adds a further name to a given member's mapping -- so it needs an existing member to attach to, and this refuses a value no member carries rather than falling through to - :meth:`register`. That holds for every registry; the one exception is - ``AppType`` and the concrete enumerations, which must call it for - members that do not exist yet. What an alias means also differs away + :meth:`register`. That holds for every registry, ``AppType`` included, + whose own override is stricter still: the port must already carry a + member of that very registry. What an alias means also differs away from ``AppType``: on every other registry it is a custom name the - caller opts into, not one recorded by the IANA registrars. Minting under the name of an aliasing call would - manufacture exactly the unrecorded member #775 removes. + caller opts into, not one recorded by the IANA registrars. Minting + under the name of an aliasing call would manufacture exactly the + unrecorded member #775 removes. Membership is tested against ``_value2member_map_`` rather than by calling ``cls(value)``: a declared-but-unassigned value resolves through From d750f7370cb74fdd9f202245eca1ff5157c55f8f Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Fri, 2 Oct 2026 13:23:59 -0400 Subject: [PATCH 3/5] docs(corekit): scope the four-method contract to the registry tier The cross-review on #990 found the de-quoted #842 clause asserting a universal that this tree falsifies. Measured by import: of 130 const enum classes, 127 carry all four methods and 3 carry neither register nor register_alias -- CommandType and ConformanceRequirement in const/ftp/command.py, and TransportProtocol in const/reg/apptype/apptype.py. All three subclass EnumLookup, the bare tier #877 split off precisely so a closed set is not handed register, which this same module docstring explains thirty lines above the sentence that contradicted it. The source clause was prescriptive, and #842 closed with the remaining classes tracked rather than with the spec met everywhere, so the prose now reads as a contract the registry tier is held to: - Both sites say the four methods are to exist on every const *registry*, not every const enumeration, and the module docstring now states that #877's closed sets sit outside that contract. - register's prose said "names" against its single name parameter. - The claim that an alias adds a name rather than a member is scoped to this base, because AppType's override mints a real one through extend_enum: measured, TCP.register_alias takes len(TCP) from 6147 to 6148 with __members__ and _member_names_ growing alike. - One clause distinguishes #842's AppType exception, which is about where an alias is routed, from whether the member being aliased must already exist. Prose only: token sequences identical with strings masked, all three differing string tokens are docstrings, the AST with docstrings blanked compares equal, and maximum line length stays 96. The four enum test files: 130 passed, 112 subtests. --- pcapkit/corekit/enum.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/pcapkit/corekit/enum.py b/pcapkit/corekit/enum.py index e22451767..068aa107b 100644 --- a/pcapkit/corekit/enum.py +++ b/pcapkit/corekit/enum.py @@ -54,8 +54,9 @@ remaining seven once the files holding them freed up. The registry tier's own shape is the earlier design settled in GitHub issue #842: -``get``, ``get_all``, ``register`` and ``register_alias`` exist on every const -enumeration, so the abstraction is finished by moving them to the base class. +``get``, ``get_all``, ``register`` and ``register_alias`` are to exist on every +const registry, so the abstraction is finished by moving them to the base class. +The closed sets split off by #877 are outside that contract. ``AppType``'s sub-base class carries the overrides and dispatching logic it needs, and ``AppType``'s subclasses override again where their contracts differ. @@ -88,7 +89,7 @@ The contracts are those set out on GitHub issue #842: ``get`` is a shortcut for the ``[]`` operation and returns the canonical enumeration member; ``get_all`` returns every matching member; ``register`` mints a new member on the class at -runtime under caller-specified names, so nothing has to be guessed; +runtime under the name the caller specifies, so nothing has to be guessed; ``register_alias`` (and ``register_aliases``) adds further alias names to a given member's mapping. @@ -434,9 +435,9 @@ def get_all(cls, key: 'Any') -> 'tuple[Self, ...]': registry inheriting this base unmodified -- that tuple holds exactly one entry, since an alias registered by :meth:`register_alias` is a second *name* for the canonical member rather than a second member. The method - still exists here, because all four methods must exist on every const - enumeration (GitHub issue #842), and it is where a registry with genuinely several - matches puts them: ``AppType`` overrides it to return every service IANA + still exists here, because all four methods are to exist on every const + registry (GitHub issue #842), and it is where a registry with genuinely + several matches puts them: ``AppType`` overrides it to return every service IANA assigns to a port. Args: @@ -579,8 +580,10 @@ def register_alias(cls, value: 'Any', name: 'str') -> 'Self': Per GitHub issue #842, an alias adds a further name to a given member's mapping -- so it needs an existing member to attach to, and this refuses a value no member carries rather than falling through to - :meth:`register`. That holds for every registry, ``AppType`` included, - whose own override is stricter still: the port must already carry a + :meth:`register`. That holds for every registry, ``AppType`` included + (the issue's ``AppType`` exception concerns where an alias is routed, not + whether the member being aliased must already exist), whose own override + is stricter still: the port must already carry a member of that very registry. What an alias means also differs away from ``AppType``: on every other registry it is a custom name the caller opts into, not one recorded by the IANA registrars. Minting @@ -593,12 +596,14 @@ def register_alias(cls, value: 'Any', name: 'str') -> 'Self': absent from that table, so a successful call proves nothing about whether a member exists. - An alias adds a *name*, not a member: ``__members__`` grows by one while - ``_member_names_``, iteration and ``_value2member_map_`` are untouched. - Calls :meth:`_extend` directly rather than :meth:`register`, which + On this base, an alias adds a *name*, not a member: ``__members__`` grows + by one while ``_member_names_``, iteration and ``_value2member_map_`` are + untouched. Calls :meth:`_extend` directly rather than :meth:`register`, which would now refuse this call outright -- :meth:`register` and :meth:`register_alias` test ``value``'s membership for opposite outcomes, so neither can be the other's implementation any more. + ``AppType``'s override differs: it mints a real member through + :func:`~aenum.extend_enum`, so its iteration grows too. Args: value: Value of the existing member to alias. From 4d51b4bb0447036505c0c3c34b6773032962d100 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Fri, 2 Oct 2026 14:02:16 -0400 Subject: [PATCH 4/5] docs(corekit): render #842's AppType hedge instead of denying it Round 3's review found the parenthetical this pull request added asserting the opposite of what #842 says, and that instruction was mine. #842 carries two AppType exceptions, not one: the issue body has the routing exception, and comment 5852772329 answers whether the generic register_alias requires an existing member with "always, unless AppType and the concrete enumerations need to call it on non-existing members". That unless is a carve-out on exactly the question the parenthetical said the issue was not about. main's text was lossy by omission -- it carried the first half of that sentence and dropped the unless. The parenthetical turned the omission into a positive false claim, which is worse, and is the third way this pull request has lost the same qualifier: round 1 invented an exception no registry implements, round 2 flattened a modal into a universal, round 3 denied the hedge existed. - The passage now states what #842 settled and what it left open, then answers the open part from behaviour: AppType's override requires the port to carry a member of that registry already. It names the concrete enumerations too, which the comment does and my instruction had dropped. - "stricter still" is gone rather than qualified. The base tests membership in _value2member_map_ and AppType tests __registry__.getlist(port), so it was never a strengthening of one predicate. - Two untouched lines said every generated enumeration under pcapkit.const inherits from EnumRegistry, which the sentence added last round contradicts. Both now say registry: the three closed sets are generated and inherit EnumLookup, so "generated" was no escape hatch. - The paragraph is re-flowed to 75-82 columns. The edit had left a 52-column line whose successor fit, the same defect #992 was sent back for. Prose only, at the strictest setting: 753 tokens identical with no exclusions at all and only strings masked, both differing string tokens are docstrings, the AST with docstrings blanked compares equal, maximum line length stays 96. The four enum test files: 130 passed, 112 subtests. --- pcapkit/corekit/enum.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/pcapkit/corekit/enum.py b/pcapkit/corekit/enum.py index 068aa107b..d7ad759bc 100644 --- a/pcapkit/corekit/enum.py +++ b/pcapkit/corekit/enum.py @@ -15,7 +15,7 @@ * :class:`EnumRegistry`, a subclass of the above -- adds the **mutating** half: :meth:`~EnumRegistry.register`, :meth:`~EnumRegistry.register_alias`, :meth:`~EnumRegistry.register_aliases`, :meth:`~EnumRegistry._extend` and - :meth:`~EnumRegistry._unregistered_member`. Every generated enumeration under + :meth:`~EnumRegistry._unregistered_member`. Every generated registry under :mod:`pcapkit.const` inherits from here. That split follows the rules laid down in GitHub issue #877: a helper enumeration is @@ -64,7 +64,7 @@ That is a three-tier hierarchy, of which this module is **tier one**: 1. :class:`EnumRegistry` -- the four methods, in the form that suits a registry - mapping one key to one member. Every generated enumeration under + mapping one key to one member. Every generated registry under :mod:`pcapkit.const` inherits them from here. 2. ``AppType``'s sub-base -- overrides all four to route through its ``_dispatch``, because a port lookup needs a transport protocol to be @@ -578,17 +578,16 @@ def register_alias(cls, value: 'Any', name: 'str') -> 'Self': """Add ``name`` as a further name for the member already at ``value``. Per GitHub issue #842, an alias adds a further name to a given member's - mapping -- so it needs an existing member to attach to, and this refuses - a value no member carries rather than falling through to - :meth:`register`. That holds for every registry, ``AppType`` included - (the issue's ``AppType`` exception concerns where an alias is routed, not - whether the member being aliased must already exist), whose own override - is stricter still: the port must already carry a - member of that very registry. What an alias means also differs away - from ``AppType``: on every other registry it is a custom name the - caller opts into, not one recorded by the IANA registrars. Minting - under the name of an aliasing call would manufacture exactly the - unrecorded member #775 removes. + mapping -- so it needs an existing member to attach to, and this refuses a + value no member carries rather than falling through to :meth:`register`. + #842 settled on an alias always attaching to an existing member, leaving + open only whether ``AppType`` or a concrete enumeration might need to + alias a value no member carries. ``AppType``'s override does not: it + requires the port to already carry a member of that very registry. What an + alias means also differs away from ``AppType``: on every other registry it + is a custom name the caller opts into, not one recorded by the IANA + registrars. Minting under the name of an aliasing call would manufacture + exactly the unrecorded member #775 removes. Membership is tested against ``_value2member_map_`` rather than by calling ``cls(value)``: a declared-but-unassigned value resolves through From e5836e8dce502c28de07a46cedcc44952dc93c06 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Fri, 2 Oct 2026 14:32:14 -0400 Subject: [PATCH 5/5] docs(corekit): name the real exception set, and stop narrowing #842's residual Round 4 passed with no blocker, but two of its notes were the same qualifier-firming this pull request had already done three times, so they are fixed rather than carried. The tier-1 bullet said every generated registry under pcapkit.const inherits the four methods from EnumRegistry. Measured across all 127: 118 take every method from the base tiers, 5 are the AppType family overriding all four, and 4 carry a hand-written get -- Command and FEATCode in const/ftp/command.py, Method in const/http/method.py, OptionType in const/pcapng/option_type.py. So the exception set is nine, not five; carving out only AppType would have left the sentence false for the other four. - The bullet now excepts the overrides listed below it and the hand-written get overrides, rather than asserting a universal with nine counter-examples. - "leaving open only whether" loses the "only". Against the pre-existence ruling that word is accurate, since the unless clause is its sole residual, but against #842 as a whole it is false: the body carries five open design questions. - "settled on" stays, checked rather than softened. The first statement of the rule is tentative, but 5852780815 records the contract outright, 5855971633 and 5856019103 restate it flatly, and the issue closed as completed. Adding a hedge where the record is firm would be the same error pointing the other way. Left for a change of its own, both pre-existing: get and get_all are defined on EnumLookup rather than EnumRegistry, so "inherits them from here" is true only transitively; and the tier-2 bullet says the sub-base routes all four through _dispatch where only get and get_all do. Prose only: 753 tokens identical with no exclusions at all and only strings masked, both differing string tokens are docstrings, the AST with docstrings blanked compares equal while the raw dump differs, maximum line length stays 96. Both edited paragraphs re-wrapped so no line has an absorbable successor. The four enum test files: 130 passed, 112 subtests. --- pcapkit/corekit/enum.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pcapkit/corekit/enum.py b/pcapkit/corekit/enum.py index d7ad759bc..33a5dabb7 100644 --- a/pcapkit/corekit/enum.py +++ b/pcapkit/corekit/enum.py @@ -64,8 +64,9 @@ That is a three-tier hierarchy, of which this module is **tier one**: 1. :class:`EnumRegistry` -- the four methods, in the form that suits a registry - mapping one key to one member. Every generated registry under - :mod:`pcapkit.const` inherits them from here. + mapping one key to one member. The registries under :mod:`pcapkit.const` + inherit them from here, apart from the overrides below and a few hand-written + ``get`` overrides. 2. ``AppType``'s sub-base -- overrides all four to route through its ``_dispatch``, because a port lookup needs a transport protocol to be answerable at all. Landed as of GitHub issue #860: not in this module, but @@ -581,13 +582,13 @@ def register_alias(cls, value: 'Any', name: 'str') -> 'Self': mapping -- so it needs an existing member to attach to, and this refuses a value no member carries rather than falling through to :meth:`register`. #842 settled on an alias always attaching to an existing member, leaving - open only whether ``AppType`` or a concrete enumeration might need to - alias a value no member carries. ``AppType``'s override does not: it - requires the port to already carry a member of that very registry. What an - alias means also differs away from ``AppType``: on every other registry it - is a custom name the caller opts into, not one recorded by the IANA - registrars. Minting under the name of an aliasing call would manufacture - exactly the unrecorded member #775 removes. + open whether ``AppType`` or a concrete enumeration might need to alias a + value no member carries. ``AppType``'s override does not: it requires the + port to already carry a member of that very registry. What an alias means + also differs away from ``AppType``: on every other registry it is a custom + name the caller opts into, not one recorded by the IANA registrars. + Minting under the name of an aliasing call would manufacture exactly the + unrecorded member #775 removes. Membership is tested against ``_value2member_map_`` rather than by calling ``cls(value)``: a declared-but-unassigned value resolves through