refactor(const,vendor): move the generated const-enum template onto EnumRegistry (#775) - #858
Conversation
|
One small note: forgot to mention previously, but I'd prefer the module name to be |
|
Recorded, verbatim: "One small note: forgot to mention previously, but I'd prefer the module name to be Doing it here, and the measurement is why: This PR already touches 105 of the 124, so the rename is nearly free inside it. Doing it as a separate PR means a No shadowing risk, checked rather than assumed: The worker is on it: One consequence worth stating: the cross-review currently running is against |
…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).
d91aaf3 to
86dcb37
Compare
|
GOOD TO GO on
Body corrected. It claimed the excluded ten are "all StrEnum-based". Measured: only Three of my own claims were refuted, and all three matter:
What was confirmed, by AST comparison of all 121 const modules on both trees rather than by grep: The Regeneration proven live on 3 of the author's 5-file sample against upstream: md5s differ only by two lines per No stdlib shadowing from the rename, verified in one process: stdlib One thing worth knowing that is out of scope here: 83 of the 105 still mint via
|
…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.
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).
…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.
…- 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.
…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.
…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.
…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.
…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.
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).
…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.
…- 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.
…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.
Please follow the guide below
make pylint,make mypy,make isort)make testpasses, and a test case covers the changedocs/source/changelog/and regeneratedCHANGELOG.md, if the change is user-visible — N/A, changelog centralised in docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620) #657What is the purpose of your pull request?
refactor— changes neither behaviour nor performanceDescription of your pull request and other information
Tier 3 of #775/#842: mixes
EnumRegistryinto the generated const-enum template (pcapkit/vendor/default.py'sLINE) and every const module it produces, soget/get_all/register/register_aliasexist on all 121 const registries instead of 7.Census, measured on
05468a06b(this batch's base): 121 const modules, 6 already carryingEnumRegistry(tier 1/2, #855), 111 carrying the literal generatedget()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 plusreg/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 generatedregister()used to silently alias an already-registered value rather than minting or raising.get()dispatch matrix (old:isinstance(key, int)first; new/base:isinstance(key, str)first) — measured onTransType, anIntEnumregistry:KeyErrorKeyError(unchanged)ValueErrorValueError(unchanged)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.
StrEnumdecision: excluded from this tier. None of the 105 in-scope files areStrEnum. The excluded ten are excluded because each carries a bespoke__new__, not because they are allStrEnum— measured, onlyftp/command.FEATCode,http/method.Method,pcapng/option_type.OptionTypeandreg/apptype.AppTypeareStrEnum, whileftp/return_code.ResponseKind,ftp/return_code.GroupingInformation,http/status_code.StatusCodeareIntEnumandftp/command.CommandTypeisIntFlag. Each already has its own bespoke, mint-on-lookup contract from a crawler that overridesprocess()/context()outright, so none of them sharevendor/default.py's template at all. Measured directly on a syntheticEnumRegistry+StrEnumregistry (present onmainsince tier 1) that the baseget()'sstr-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-unusedextend_enumimport),sctp/cause_code,hip/parameter(crawler-overriddenprocess(), manyextend_enumranges),pcapng/record_type(crawler-overridden, single legacy mint),reg/ethertype(57extend_enumcall sites) — all five came back byte-identical to the hand-edit (diffclean).Tests: extended
tests/const/test_const_registry_protocol.pywith the 105-file sweep (base declared + inherited + no local copy, full corpus accounted for at 121 = 6 + 10 + 105), theregister()regression pin (fails on05468a06b, passes here), a_missing_range-parity pin, theget()dispatch matrix above, and theStrEnumlimitation measurement.tests/const/test_const_enum_get.py's vendor-template regression test asserted on the now-removedget()block text; rewritten to assert the mix-in and the absence of a shadowing local copy instead — the previously-namedtest_const_enum_builtin_parity.py/test_const_enum_lookup.pywere checked too but don't actually depend on this template's shape (they cover the bespokemh/tcp.flags/ftp/httptemplates 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 passpython -m unittest discover -s tests/vendor— 86 tests, all passpython -m unittest tests.test_tier_guard— 107 tests, all passregister()-raises test: fails on05468a06b(base), passes on this headisort -l100 -ppcapkit,mypy,pylint(Makefile flags) clean on all 105 const files +vendor/default.py—pylintduplicate-codefindings on this batch drop from 108 to 4