Skip to content

refactor(const,vendor): move the generated const-enum template onto EnumRegistry (#775) - #858

Merged
JarryShaw merged 1 commit into
mainfrom
refactor/775-tier3-generated-template
Sep 27, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
refactor/775-tier3-generated-template

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Please follow the guide below

What is the purpose of your pull request?

  • refactor — changes neither behaviour nor performance

Description of your pull request and other information

Tier 3 of #775/#842: mixes EnumRegistry into the generated const-enum template (pcapkit/vendor/default.py's LINE) and every const module it produces, so get/get_all/register/register_alias exist on all 121 const registries instead of 7.

Census, measured on 05468a06b (this batch's base): 121 const modules, 6 already carrying EnumRegistry (tier 1/2, #855), 111 carrying the literal generated get() docstring. Of those 111, 105 share the shared template byte-for-byte (verified against the template's own literal text) and are converted here; the other 6 plus reg/apptype's 4 transport subclasses (10 files total) are bespoke registries that don't share this template and are left alone — each carries its own __new__, see below.

This also closes the register() two-contract residue #855 disclosed: the generated register() used to silently alias an already-registered value rather than minting or raising.

Before: TransType.register(6, 'TOTALLY_NEW_NAME') -> <TransType.TCP: 6>, mints nothing, raises nothing
After:  TransType.register(6, 'TOTALLY_NEW_NAME') -> ValueError: 6 is already registered on TransType as 'TCP'; use TransType.register_alias() ...

get() dispatch matrix (old: isinstance(key, int) first; new/base: isinstance(key, str) first) — measured on TransType, an IntEnum registry:

key before after
valid int value resolves resolves (unchanged)
valid name resolves resolves (unchanged)
missing name KeyError KeyError (unchanged)
missing int ValueError ValueError (unchanged)
neither int nor str (3.5, None, b'x') KeyError (treated as name) ValueError (treated as value, via _missing_'s own int check)

Only the last row changes, and only for a key type nothing in the library ever passes.

StrEnum decision: excluded from this tier. None of the 105 in-scope files are StrEnum. The excluded ten are excluded because each carries a bespoke
__new__, not because they are all StrEnum — measured, only ftp/command.FEATCode, http/method.Method,
pcapng/option_type.OptionType and reg/apptype.AppType are StrEnum, while ftp/return_code.ResponseKind,
ftp/return_code.GroupingInformation, http/status_code.StatusCode are IntEnum and ftp/command.CommandType is
IntFlag. Each already has its own bespoke, mint-on-lookup contract from a crawler that overrides process()/context() outright, so none of them share vendor/default.py's template at all. Measured directly on a synthetic EnumRegistry+StrEnum registry (present on main since tier 1) that the base get()'s str-key path never falls back to a value lookup — a real, pre-existing limitation, not something this tier introduces — so converting any of those five/six would silently regress a value lookup rather than just relocate behaviour.

Regeneration: hand-edited the 105 files mechanically (script-derived from the template's own get/register/_unregistered_member text), then regenerated 5 live from IANA/Wikipedia — arp/hardware (IntEnum, drops the now-unused extend_enum import), sctp/cause_code, hip/parameter (crawler-overridden process(), many extend_enum ranges), pcapng/record_type (crawler-overridden, single legacy mint), reg/ethertype (57 extend_enum call sites) — all five came back byte-identical to the hand-edit (diff clean).

Tests: extended tests/const/test_const_registry_protocol.py with the 105-file sweep (base declared + inherited + no local copy, full corpus accounted for at 121 = 6 + 10 + 105), the register() regression pin (fails on 05468a06b, passes here), a _missing_ range-parity pin, the get() dispatch matrix above, and the StrEnum limitation measurement. tests/const/test_const_enum_get.py's vendor-template regression test asserted on the now-removed get() block text; rewritten to assert the mix-in and the absence of a shadowing local copy instead — the previously-named test_const_enum_builtin_parity.py/test_const_enum_lookup.py were checked too but don't actually depend on this template's shape (they cover the bespoke mh/tcp.flags/ftp/http templates instead), so they're unchanged.

_missing_ is untouched everywhere, per its own per-registry bounded ranges.

Test plan

  • python -m unittest discover -s tests/const — 159 tests, all pass
  • python -m unittest discover -s tests/vendor — 86 tests, all pass
  • python -m unittest tests.test_tier_guard — 107 tests, all pass
  • New register()-raises test: fails on 05468a06b (base), passes on this head
  • isort -l100 -ppcapkit, mypy, pylint (Makefile flags) clean on all 105 const files + vendor/default.py — pylint duplicate-code findings on this batch drop from 108 to 4
  • 5-file live regeneration sample byte-identical to the hand-edit

@JarryShaw JarryShaw added refactor Restructuring for its own sake — neither a fix nor a new capability (refactor: prefix) const Regenerated IANA or vendor constant tables; members keep their numeric values review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Sep 27, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

One small note: forgot to mention previously, but I'd prefer the module name to be pcapkit.corekit.enum (no -s)

@JarryShaw

Copy link
Copy Markdown
Owner Author

Recorded, verbatim: "One small note: forgot to mention previously, but I'd prefer the module name to be
pcapkit.corekit.enum (no -s)"

Doing it here, and the measurement is why:

importers of pcapkit.corekit.enums on main : 17
importers on this PR's head                : 124   <- this PR's own 105 converted registries

This PR already touches 105 of the 124, so the rename is nearly free inside it. Doing it as a separate PR means a
second pass over the same 105 files and a guaranteed conflict with this one.

No shadowing risk, checked rather than assumed: enums.py's only third-party import is
from aenum import extend_enum — it never imports stdlib enum — and Python 3's absolute-import default means
pcapkit/corekit/enum.py cannot shadow stdlib enum for any other module either. No pcapkit/corekit/enum.py
exists today.

The worker is on it: git mv, then the 124 import sites, pcapkit/corekit/__init__.py's re-export, the tests, and
any docs/ cross-reference. I will verify the rename is pure — that the only change is the module path, with
no content drift — before this goes back to review.

One consequence worth stating: the cross-review currently running is against d91aaf30f, and its findings are
about get/register semantics, which a rename does not touch. So that verdict still applies; I will re-verify
only the rename on top rather than paying for a second full review of the same 100-file diff.

…numRegistry (#775)

Tier 3 of #775/#842: mixes pcapkit.corekit.enum.EnumRegistry into the
generated const-enum template and every registry it produces, so get,
get_all, register and register_alias exist on all 121 const registries
rather than 7. Closes the register() two-contract residue #855 disclosed:
an already-registered value now raises instead of silently aliasing.

- pcapkit/vendor/default.py: LINE now emits
  class {NAME}(EnumRegistry, IntEnum) and drops the hand-written get,
  register and _unregistered_member the base now provides; the aenum
  import line only keeps extend_enum when a registry's own _missing_
  still calls it directly. _missing_ itself is untouched.
- 105 generated pcapkit/const/*.py files edited to match, mechanically
  (script-derived from the template's own literal text). Verified
  byte-identical against a live regeneration of 5 files spanning both
  import shapes (arp/hardware, sctp/cause_code, hip/parameter,
  pcapng/record_type, reg/ethertype).
- Excluded: the 6 bespoke registries #855 already converted, and 10
  files with bespoke, mint-on-lookup get()/_missing_ contracts that
  don't share this template (ftp/command, ftp/return_code, http/method,
  http/status_code, pcapng/option_type, reg/apptype/* - AppType is
  tier 2 of #842, not tier 3). All are StrEnum-based; measured that the
  base get()'s str-key path never tries the value path, so converting
  them would silently regress value lookups.
- Measured the one real dispatch difference: a key that is neither int
  nor str now raises ValueError (treated as a value) instead of the old
  KeyError (treated as a name). Int and str keys are unaffected.
- Per owner ruling on #858: renamed pcapkit/corekit/enums.py to
  pcapkit/corekit/enum.py (git mv, no content change beyond the two
  self-referencing docstring lines naming the module's own path) and
  updated all 124 importers, corekit/__init__.py's re-export, and the
  four test modules that name the path in prose or assertions. No
  docs/ reference existed to update.
- tests/const/test_const_registry_protocol.py: new coverage for the
  105-file batch - base declared/inherited, full-corpus sweep, the
  register() regression pin (fails on prior head, passes here),
  _missing_ range parity, the get() dispatch matrix, and the StrEnum
  exclusion's own measurement.
- tests/const/test_const_enum_get.py: its vendor-template regression
  test asserted on the now-removed get() block; rewritten to assert the
  mix-in and the absence of a local copy instead.

Build: mypy/isort/pylint clean on all touched files (pylint duplicate-code
findings dropped 108->4 on this batch). tests/const, tests/vendor and
tests/test_tier_guard.py pass in full (352 tests, plain unittest).
@JarryShaw
JarryShaw force-pushed the refactor/775-tier3-generated-template branch from d91aaf3 to 86dcb37 Compare September 27, 2026 17:42
@JarryShaw JarryShaw added breaking Breaks public-facing behaviour or API (apply alongside the type label) review: good-to-go Cross-review at the current head says ready; CI state is separate and removed review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Sep 27, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

GOOD TO GO on 86dcb375c — cross-review (opus, a different model from the sonnet author). No code defect
found.
Two non-code items it raised are now done: breaking applied, and the body's StrEnum rationale
corrected.

breaking applied, and backfilled onto #855. The get exception-type change alone would not earn it; the
register() change does. Measured on two of the 105, and the old behaviour was worse than "returns silently" —
it actually installed the alias:

main  TransType.register(6, 'TOTALLY_NEW_NAME') -> <TransType.TCP: 6>   'TOTALLY_NEW_NAME' in __members__: True
head  TransType.register(6, 'TOTALLY_NEW_NAME') -> ValueError: 6 is already registered on TransType as 'TCP';
                                                   use TransType.register_alias() ...           in __members__: False

register is a documented public classmethod on 105 public pcapkit.const classes, so a downstream consumer using
it as an aliasing shortcut breaks on upgrade. The in-repo caller audit came back clean — 3 call sites, all in
tests/const/, all written for this change — but that protects this repository, not its users, and is not the
test.
#855 shipped the identical semantic for 6 classes unlabelled, so I have backfilled breaking there too
rather than leave the two inconsistent.

Body corrected. It claimed the excluded ten are "all StrEnum-based". Measured: only ftp/command.FEATCode,
http/method.Method, pcapng/option_type.OptionType and reg/apptype.AppType are StrEnum;
ftp/return_code.ResponseKind, ftp/return_code.GroupingInformation and http/status_code.StatusCode are
IntEnum, and ftp/command.CommandType is IntFlag. The exclusion decision is right — each carries a
bespoke __new__ — but the stated reason was wrong for four classes, and that paragraph is what a future
maintainer reads to know why they were left out.

Three of my own claims were refuted, and all three matter:

  • The diff is 124 files, not 100. gh pr view --json files silently caps at 100; gh api .../files --paginate
    and git diff --numstat both give 124, +779/−7170. My earlier "100 files, +321/−6721" was an API artefact.
  • Three methods were removed per module, not two — _unregistered_member as well as get and register. Safe,
    because the base supplies it, but my brief undercounted.
  • No IntFlag exists among the 105. All five EnumRegistry, IntFlag classes are the mh/*_flag and
    tcp/flags ones feat(corekit): add EnumRegistry base for const registries, convert 5 (#775) #855 already converted; the one bare IntFlag is ftp/command.CommandType, excluded. So the
    IntFlag matrix I asked for was not constructible.

What was confirmed, by AST comparison of all 121 const modules on both trees rather than by grep:
105 ['IntEnum'] -> ['EnumRegistry', 'IntEnum'], 0 methods added, 0 member-count deltas, 0 structural surprises,
and — the check that matters most — 0 source differences across every surviving method in all 105 files, md5'd
per method. _missing_ is byte-identical everywhere. The template's new conditional extend_enum import is correct
in all 105: every module whose _missing_ uses it still imports it, none that doesn't imports it unused.

The get matrix: 11 of 13 cells identical, 3 changed and only in exception class (KeyError → ValueError for
a key that is neither int nor str). No cell went from raising to returning or back, none returned a different
member, and supplying a default makes even those three identical.

Regeneration proven live on 3 of the author's 5-file sample against upstream: md5s differ only by two lines per
file that are an artefact of the reviewer's own python -m invocation (.. module:: __main__); filtering those
leaves zero changed lines — including the two interesting cases, arp/hardware (no extend_enum, import
correctly dropped) and reg/ethertype (57 sites, import correctly kept).

No stdlib shadowing from the rename, verified in one process: stdlib enum and pcapkit.corekit.enum are
distinct objects with distinct __file__s, sys.modules['enum'] is stdlib, and test_const_enum_builtin_parity.py
— which uses stdlib enum.IntEnum as its reference — passes 32/32. Shadowing only occurs if pcapkit/corekit/
itself is put on sys.path, which would already break every absolute pcapkit. import, and the repo already ships
11 other stdlib-name collisions (corekit/io.py, utilities/logging.py, corekit/fields/numbers.py, …).

One thing worth knowing that is out of scope here: 83 of the 105 still mint via extend_enum inside
_missing_, so get(148) grows the registry on both trees. #775's "no mint on lookup" goal is met only for the
21 modules using _unregistered_member. Unchanged by this PR, but it means the goal is not yet achieved
tree-wide — that is the next tier, not a defect here.

tests/const 159, tests/vendor 86, tests/test_tier_guard.py 107 — all OK under plain unittest, matching the
author's counts.

@JarryShaw
JarryShaw merged commit 4ec9e95 into main Sep 27, 2026
63 checks passed
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Sep 27, 2026
JarryShaw added a commit that referenced this pull request Sep 27, 2026
…ss-review on #859

#859 (open, breaking, about to land) changes EnumRegistry.get's no-default
marker from -1 to an identity sentinel object, so -1 becomes an ordinary
default value for 111 of the const registries -- making #584's "-1 ...
is what separates 'no default was supplied' from 'a default was supplied'"
false for what 1.5.0 actually ships, since both entries land in the same
release.

Reworded to past tense -- -1 "was what separated" the two cases "at the
time" -- and added a forward sentence noting the convention was later
replaced by a sentinel, once the shared base class #858 (added below) put
get() behind one call site for most of these registries instead of many.
Bare-cites #859 as the vehicle without giving it its own entry yet, per
instruction -- it is still open and its head may move.

The #584 entry itself is not deleted: it records a real integer-path
default-handling fix that still shipped, and only the one now-superseded
promise about -1 specifically needed correcting.
JarryShaw added a commit that referenced this pull request Sep 27, 2026
EnumRegistry.register() now raises ValueError naming the existing member
and pointing at register_alias(), where it used to silently alias an
already-registered value under the caller's new name -- aenum treats a
taken value as an alias request, so the old bare extend_enum() minted
nothing and raised nothing, contradicting the method's own docstring.

pcapkit.corekit.enums.EnumRegistry centralises get/get_all/register/
register_alias/register_aliases/_unregistered_member in one mixin and
converts the first six registries with no bespoke __new__. The new guard
reaches only those six; the other 105 const registries still carry the
old generator's ungated extend_enum, so pcapkit.const ships two
contradictory register contracts until #858 migrates the rest -- residue
the PR names rather than papers over.

Verified independently rather than taken from the PR table: the base's
get() dispatch divergence on a non-int/non-str key raises KeyError on the
still-unconverted generated template (empirically confirmed against
05468a0, not the TypeError the PR body itself claims for that path).
JarryShaw added a commit that referenced this pull request Sep 27, 2026
…rated template

pcapkit/vendor/default.py's generated template now emits
class {NAME}(EnumRegistry, IntEnum) and drops its own get/register/
_unregistered_member entirely, so every const-enum class it produces
inherits #855's guard instead -- e.g. TransType.register(6,
'TOTALLY_NEW_NAME') now raises ValueError naming the existing member and
pointing at register_alias(), where it used to mint nothing and raise
nothing.

Census re-derived independently by AST over the merge commit rather than
taken from the PR table: 121 const modules hold 127 enum classes (three
modules define more than one class each). 6 classes already used
EnumRegistry from #855; of the other 115 modules, 105 share the generated
template byte-for-byte and are converted here, and the remaining 10 keep
their own bespoke __new__ and are left alone -- ftp/command (4 classes),
ftp/return_code (3), http/method, http/status_code, pcapng/option_type,
reg/apptype/apptype.py (2) and its four transport subclasses. Total now
inheriting EnumRegistry: 111 of 127 classes, up from 6. Also renames
pcapkit/corekit/enums.py to enum.py (no -s), the maintainer's ruling.

util/changelog_md.py regenerated CHANGELOG.md for all three entries added
across this and the two preceding commits (#855, #856, #858); --check
exit 0.
JarryShaw added a commit that referenced this pull request Sep 27, 2026
…- it was wrong

14c8930's commit message states: "the base's get() dispatch divergence
on a non-int/non-str key raises KeyError on the still-unconverted
generated template (empirically confirmed against 05468a0, not the
TypeError the PR body itself claims for that path)." That claim is false.
The test behind it called TransType.get(3.5) -- TransType is a generated
registry #855 never touched, so it only ever tells you about #858's
population, not #855's.

Re-tested properly this round: detached worktrees at 895cde6 (856,
immediately before 855) and 05468a0 (855), editable finder evicted from
sys.meta_path, pcapkit.__file__ asserted per process, one throwaway
process per cell since these registries mint on a fresh value.

  Flags.get(3.5) at 895cde6 (855's own six, pre-conversion):
    TypeError: argument of type 'float' is not a container or iterable
  Flags.get(3.5) at 05468a0 (855's own six, post-conversion):
    ValueError: 3.5 is not a valid Flags
  TransType.get(3.5) at both refs (generated, #855 never touches it):
    KeyError: 3.5 -- unchanged by #855, this is #858's population

So #855's PR body was right: TypeError is what the six bespoke registries
raised before this PR. KeyError belongs to the still-unconverted 105 and
is correctly attributed to #858's entry, not #855's. The #855 entry is
corrected back to TypeError, scoped to its own six registries' own
before/after rather than compared against "the generated template ...
used by the other 105" -- that comparison belongs to #858, which already
states it correctly.

14c8930 is left as-is rather than amended: it has already been pushed,
and rewriting it would need a force-push, which this branch does not do.
This commit is the correction of record instead.

util/changelog_md.py regenerated CHANGELOG.md; --check exit 0.
JarryShaw added a commit that referenced this pull request Sep 27, 2026
…s tense

Two fixes, both cross-review findings on #657.

1. The #855 entry's TypeError claim was generalised from one registry
(Flags) to all six #855 converts. The split is on member type, not on
converted-vs-not: the pre-855 hand-copied get() ends `return cls[key]`,
which aenum treats as a containment test on a Flag subclass (TypeError)
but a name lookup on a plain Enum (KeyError). Re-derived per-registry on
895cde6 (immediately before #855), MRO printed per cell:

  ExtensionHeader  [IntEnum, int]   get(3.5) -> KeyError: 3.5
  BindingACKFlag   [IntFlag, int]   get(3.5) -> TypeError: argument of
                                     type 'float' is not a container or
                                     iterable
  Flags            [IntFlag, int]   get(3.5) -> TypeError: ... (as above)

Confirmed post-855 (05468a0) that ExtensionHeader.get(3.5) now raises
ValueError, the same KeyError->ValueError shift #858's entry already
attributes to the other 105 -- so for ExtensionHeader specifically there
is no new divergence at all, only the same shift arriving six registries
early. Scoped the TypeError claim to the five IntFlag registries and
named ExtensionHeader's own KeyError->ValueError shift separately.

2. The #584 entry said -1 "was later replaced" by a sentinel object --
completed past tense for something that has not landed. gh pr view 859
-> OPEN, mergedAt null; origin/main:pcapkit/corekit/enum.py:64 still
reads NO_DEFAULT = -1. Reworded to present-continuous with an explicit
"not yet landed", so the entry does not tell a reader 1.5.0 ships
something it does not yet ship.

util/changelog_md.py regenerated CHANGELOG.md for both fixes.
JarryShaw added a commit that referenced this pull request Sep 27, 2026
…registries (#775)

- classify every one of the 89 `_missing_` bodies still calling `extend_enum`
  against the owner's ruling on #775/#847: a final concrete assigned name
  mints, a notation for the reader (Unassigned/Reserved/Deprecated/etc.)
  unmints
- convert 82 registries wholly and two more (`EtherType`, `Socket`) partially,
  172 branches total, from `extend_enum` to `_unregistered_member`, in both
  the vendor crawler and the generated const file, following #838's/#858's
  precedent; proved a 5-file sample (Form-A and Form-B crawlers, both mixed
  registries) regenerates byte-identically
- leave 89-82=7 untouched: `CGAType`'s mint is not an IANA-style range at all,
  and 6 files sit on classes without `EnumRegistry` yet (`AppType` and
  friends), tracked separately by #860 pending #859
- extend `tests/const/test_const_enum_no_mint.py` with the ruling-derived
  registry lists and behavioural/source coverage for all 82+2, correct its
  stale "~92 still mint" docstring to the measured 89, and fix collateral
  breakage in three sibling test files and `tests/vendor/test_ipx_socket_
  unit.py` that pinned the pre-ruling mint behaviour

- fix the last two collateral pins the ruling invalidates, in files the first
  pass missed and CI caught: `tests/corekit/test_fields_numbers_unassigned_
  enum.py` expected `BlockType(0x0bad0bad).name == 'Reserved_0bad0bad'`, now
  `Reserved` with the value asserted explicitly since the name no longer
  carries it; and `tests/protocols/misc/test_pcapng_unit.py` read
  `FilterType.Unassigned_0` by attribute, a member that existed only because
  of the import-time mint at `pcapkit/protocols/misc/pcapng.py:4593` which
  this change removes

Build: plain `unittest` on tests/const (179), tests/vendor (86),
tests/corekit/test_fields_numbers_unassigned_enum.py (11) and
tests/protocols/misc/test_pcapng_unit.py (92 + 1 skipped) all green; both
newly-fixed files fail with their const file reverted to main.
JarryShaw added a commit that referenced this pull request Sep 27, 2026
…registries (#775)

- classify every one of the 89 `_missing_` bodies still calling `extend_enum`
  against the owner's ruling on #775/#847: a final concrete assigned name
  mints, a notation for the reader (Unassigned/Reserved/Deprecated/etc.)
  unmints
- convert 82 registries wholly and two more (`EtherType`, `Socket`) partially,
  172 branches total, from `extend_enum` to `_unregistered_member`, in both
  the vendor crawler and the generated const file, following #838's/#858's
  precedent; proved a 5-file sample (Form-A and Form-B crawlers, both mixed
  registries) regenerates byte-identically
- leave 89-82=7 untouched: `CGAType`'s mint is not an IANA-style range at all,
  and 6 files sit on classes without `EnumRegistry` yet (`AppType` and
  friends), tracked separately by #860 pending #859
- extend `tests/const/test_const_enum_no_mint.py` with the ruling-derived
  registry lists and behavioural/source coverage for all 82+2, correct its
  stale "~92 still mint" docstring to the measured 89, and fix collateral
  breakage in three sibling test files and `tests/vendor/test_ipx_socket_
  unit.py` that pinned the pre-ruling mint behaviour

- fix the last two collateral pins the ruling invalidates, in files the first
  pass missed and CI caught: `tests/corekit/test_fields_numbers_unassigned_
  enum.py` expected `BlockType(0x0bad0bad).name == 'Reserved_0bad0bad'`, now
  `Reserved` with the value asserted explicitly since the name no longer
  carries it; and `tests/protocols/misc/test_pcapng_unit.py` read
  `FilterType.Unassigned_0` by attribute, a member that existed only because
  of the import-time mint at `pcapkit/protocols/misc/pcapng.py:4593` which
  this change removes

Build: plain `unittest` on tests/const (179), tests/vendor (86),
tests/corekit/test_fields_numbers_unassigned_enum.py (11) and
tests/protocols/misc/test_pcapng_unit.py (92 + 1 skipped) all green; both
newly-fixed files fail with their const file reverted to main.
JarryShaw added a commit that referenced this pull request Sep 28, 2026
…ss-review on #859

#859 (open, breaking, about to land) changes EnumRegistry.get's no-default
marker from -1 to an identity sentinel object, so -1 becomes an ordinary
default value for 111 of the const registries -- making #584's "-1 ...
is what separates 'no default was supplied' from 'a default was supplied'"
false for what 1.5.0 actually ships, since both entries land in the same
release.

Reworded to past tense -- -1 "was what separated" the two cases "at the
time" -- and added a forward sentence noting the convention was later
replaced by a sentinel, once the shared base class #858 (added below) put
get() behind one call site for most of these registries instead of many.
Bare-cites #859 as the vehicle without giving it its own entry yet, per
instruction -- it is still open and its head may move.

The #584 entry itself is not deleted: it records a real integer-path
default-handling fix that still shipped, and only the one now-superseded
promise about -1 specifically needed correcting.
JarryShaw added a commit that referenced this pull request Sep 28, 2026
EnumRegistry.register() now raises ValueError naming the existing member
and pointing at register_alias(), where it used to silently alias an
already-registered value under the caller's new name -- aenum treats a
taken value as an alias request, so the old bare extend_enum() minted
nothing and raised nothing, contradicting the method's own docstring.

pcapkit.corekit.enums.EnumRegistry centralises get/get_all/register/
register_alias/register_aliases/_unregistered_member in one mixin and
converts the first six registries with no bespoke __new__. The new guard
reaches only those six; the other 105 const registries still carry the
old generator's ungated extend_enum, so pcapkit.const ships two
contradictory register contracts until #858 migrates the rest -- residue
the PR names rather than papers over.

Verified independently rather than taken from the PR table: the base's
get() dispatch divergence on a non-int/non-str key raises KeyError on the
still-unconverted generated template (empirically confirmed against
05468a0, not the TypeError the PR body itself claims for that path).
JarryShaw added a commit that referenced this pull request Sep 28, 2026
…rated template

pcapkit/vendor/default.py's generated template now emits
class {NAME}(EnumRegistry, IntEnum) and drops its own get/register/
_unregistered_member entirely, so every const-enum class it produces
inherits #855's guard instead -- e.g. TransType.register(6,
'TOTALLY_NEW_NAME') now raises ValueError naming the existing member and
pointing at register_alias(), where it used to mint nothing and raise
nothing.

Census re-derived independently by AST over the merge commit rather than
taken from the PR table: 121 const modules hold 127 enum classes (three
modules define more than one class each). 6 classes already used
EnumRegistry from #855; of the other 115 modules, 105 share the generated
template byte-for-byte and are converted here, and the remaining 10 keep
their own bespoke __new__ and are left alone -- ftp/command (4 classes),
ftp/return_code (3), http/method, http/status_code, pcapng/option_type,
reg/apptype/apptype.py (2) and its four transport subclasses. Total now
inheriting EnumRegistry: 111 of 127 classes, up from 6. Also renames
pcapkit/corekit/enums.py to enum.py (no -s), the maintainer's ruling.

util/changelog_md.py regenerated CHANGELOG.md for all three entries added
across this and the two preceding commits (#855, #856, #858); --check
exit 0.
JarryShaw added a commit that referenced this pull request Sep 28, 2026
…- it was wrong

14c8930's commit message states: "the base's get() dispatch divergence
on a non-int/non-str key raises KeyError on the still-unconverted
generated template (empirically confirmed against 05468a0, not the
TypeError the PR body itself claims for that path)." That claim is false.
The test behind it called TransType.get(3.5) -- TransType is a generated
registry #855 never touched, so it only ever tells you about #858's
population, not #855's.

Re-tested properly this round: detached worktrees at 895cde6 (856,
immediately before 855) and 05468a0 (855), editable finder evicted from
sys.meta_path, pcapkit.__file__ asserted per process, one throwaway
process per cell since these registries mint on a fresh value.

  Flags.get(3.5) at 895cde6 (855's own six, pre-conversion):
    TypeError: argument of type 'float' is not a container or iterable
  Flags.get(3.5) at 05468a0 (855's own six, post-conversion):
    ValueError: 3.5 is not a valid Flags
  TransType.get(3.5) at both refs (generated, #855 never touches it):
    KeyError: 3.5 -- unchanged by #855, this is #858's population

So #855's PR body was right: TypeError is what the six bespoke registries
raised before this PR. KeyError belongs to the still-unconverted 105 and
is correctly attributed to #858's entry, not #855's. The #855 entry is
corrected back to TypeError, scoped to its own six registries' own
before/after rather than compared against "the generated template ...
used by the other 105" -- that comparison belongs to #858, which already
states it correctly.

14c8930 is left as-is rather than amended: it has already been pushed,
and rewriting it would need a force-push, which this branch does not do.
This commit is the correction of record instead.

util/changelog_md.py regenerated CHANGELOG.md; --check exit 0.
JarryShaw added a commit that referenced this pull request Sep 28, 2026
…s tense

Two fixes, both cross-review findings on #657.

1. The #855 entry's TypeError claim was generalised from one registry
(Flags) to all six #855 converts. The split is on member type, not on
converted-vs-not: the pre-855 hand-copied get() ends `return cls[key]`,
which aenum treats as a containment test on a Flag subclass (TypeError)
but a name lookup on a plain Enum (KeyError). Re-derived per-registry on
895cde6 (immediately before #855), MRO printed per cell:

  ExtensionHeader  [IntEnum, int]   get(3.5) -> KeyError: 3.5
  BindingACKFlag   [IntFlag, int]   get(3.5) -> TypeError: argument of
                                     type 'float' is not a container or
                                     iterable
  Flags            [IntFlag, int]   get(3.5) -> TypeError: ... (as above)

Confirmed post-855 (05468a0) that ExtensionHeader.get(3.5) now raises
ValueError, the same KeyError->ValueError shift #858's entry already
attributes to the other 105 -- so for ExtensionHeader specifically there
is no new divergence at all, only the same shift arriving six registries
early. Scoped the TypeError claim to the five IntFlag registries and
named ExtensionHeader's own KeyError->ValueError shift separately.

2. The #584 entry said -1 "was later replaced" by a sentinel object --
completed past tense for something that has not landed. gh pr view 859
-> OPEN, mergedAt null; origin/main:pcapkit/corekit/enum.py:64 still
reads NO_DEFAULT = -1. Reworded to present-continuous with an explicit
"not yet landed", so the entry does not tell a reader 1.5.0 ships
something it does not yet ship.

util/changelog_md.py regenerated CHANGELOG.md for both fixes.
@JarryShaw JarryShaw added this to the 1.5 milestone Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaks public-facing behaviour or API (apply alongside the type label) const Regenerated IANA or vendor constant tables; members keep their numeric values refactor Restructuring for its own sake — neither a fix nor a new capability (refactor: prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant