fix(const,vendor): return the unregistered member instead of re-entering _missing_ (#866) - #867
Conversation
c6d6d70 to
847451d
Compare
|
NEEDS CHANGES on
One finding refutes the guard's own premise and is worth keeping: a "does every Confirmed alongside: scope is exactly 2 of 103 registries (re-derived by AST, not grep); regeneration byte-identical for both files, verified twice; A worker is applying all six now. No code changes, so this verdict carries to the amended head. |
…ing _missing_ (#866) - `pcapkit/vendor/pcapng/{record_type,secrets_type}.py` rendered a two-line `_missing_` body whose first line called `_unregistered_member` without returning it and whose second was `return cls(value)`. That was harmless while the first line was `extend_enum`, which registers the member so the following lookup found it; #861 replaced it with `_unregistered_member`, which deliberately does not register, so the lookup missed again and re-entered `_missing_` until `RecursionError` - collapse both to the single returning line the other 101 registries already use, and regenerate `pcapkit/const/pcapng/{record_type,secrets_type}.py` from the fixed crawlers - add `tests/vendor/test_vendor_missing_body_unit.py`, a crawler-layer guard: every constant line a crawler emits into `miss` must `return`, none may be `return cls(value)`, and the sweep size is pinned at 35 so a crawler joining or leaving is deliberate. #861 fixed the generated files and not the generators, so the defect was latent until `e58618bdf` regenerated Build: 6 new tests pass. With the four source files reverted, 4 of the 6 fail (counted as methods, not subTest records): the self-recursive-lookup guard, the named-pair guard, and both behavioural lookups. `test_the_sweep_size_is_pinned` and `test_the_hard_coded_body_ends_in_a_return` pass either way -- the latter by design, since #866's body ended on `return cls(value)`, which returns. `tests/const/test_const_enum_lookup.py` also fails on the reverted tree, which is what caught this on main. tests/vendor 92 and tests/project 156 green.
847451d to
d22b71f
Compare
Both `get`'s `cls(default)` sites -- the `str` branch's and the value branch's -- reached `_missing_` for a `default` that fell inside a still-minting registry's own range, growing the registry as a side effect of resolving `default` rather than `key`. - Replace both `cls(default)` calls with a plain `_value2member_map_` lookup (owner's ruling, option 1): `default` can no longer mint by construction, and a `default` naming no registered member now falls through to the same lookup error `key` itself would have raised. - `key` resolution is unchanged: `EtherType.get(0x0888)` still mints `Xyplex_0x0888` through `_missing_`, per the ruling's own exception. - Update the docstring's "It never mints" caveat, the now-false claim that an unregistered default still reaches `cls(default)`, and the Args/Raises text to match. - Add `GetDefaultNoMintTests` pinning the issue's own repro, the preserved key-path mint, default resolving to a registered value (int and str), and the exception type per path. Update two `NoDefaultSentinelTests` assertions that asserted the old `cls(default)` failure mode for an unregistered `-1`/`-1.0` default. - Update four assertions in `test_const_enum_get.py` and `test_const_enum_builtin_parity.py` that encoded the superseded contract (an unresolvable/unregistered default failing with its own named error, or resolving into a declared-but-unassigned range) -- each retargeted to the new contract rather than weakened, with a registered-default check added back where the old one only proved the "genuinely consulted" half via a value that no longer resolves. Owner's rulings, verbatim: - "Take (b). Only register can mint. get should not mint unless it falls through the _missing_'s minted ranges." - Choosing option 1 of three proposed: "I think 1 is correct mechanism we'd like." Build/tests, under plain unittest: test_const_registry_protocol.py 74/74, test_const_enum_get.py 8/8, test_const_enum_builtin_parity.py 33/33, tests/vendor/ 86/86 -- no regressions. tests/const/ as a whole carries 5 unrelated pre-existing failures (SecretsType/RecordType recursion, filed as #866, fixed in #867) plus one further out-of-scope failure in test_const_enum_no_mint.py (contended with #865, reported separately rather than edited here).
|
GOOD TO GO on All six applied, prose only:
Why the verdict carries rather than needing a fourth round: Verified on the amended head: 6 tests pass; with the four source files reverted 4 of 6 fail as methods (the self-recursive-lookup guard, the named-pair guard, both behavioural lookups), exactly as predicted when the last-line check was narrowed. Ready for you to merge — and it is the one that unblocks |
Both `get`'s `cls(default)` sites -- the `str` branch's and the value branch's -- reached `_missing_` for a `default` that fell inside a still-minting registry's own range, growing the registry as a side effect of resolving `default` rather than `key`. - Replace both `cls(default)` calls with a plain `_value2member_map_` lookup (owner's ruling, option 1): `default` can no longer mint by construction, and a `default` naming no registered member now falls through to the same lookup error `key` itself would have raised. - `key` resolution is unchanged: `EtherType.get(0x0888)` still mints `Xyplex_0x0888` through `_missing_`, per the ruling's own exception -- landing in both lookup tables, exactly as `register` would leave it, since that is #775's own deliberately-kept-minting exception, not something `get` itself does. - Update the docstring's "It never mints" caveat (now qualified: true for `default`, not for `key` on the three still-minting registries), the now-false claim that an unregistered default still reaches `cls(default)`, and the Args/Raises text to match. - Add `GetDefaultNoMintTests` pinning the issue's own repro, the preserved key-path mint, default resolving to a registered value (int and str), and the exception type per path. Update two `NoDefaultSentinelTests` assertions that asserted the old `cls(default)` failure mode for an unregistered `-1`/`-1.0` default -- their docstrings now say plainly that they coincidentally pass on the pre-#857 tree too, and point at the test that still discriminates it. - Update four assertions in `test_const_enum_get.py` and `test_const_enum_builtin_parity.py` that encoded the superseded contract (an unresolvable/unregistered default failing with its own named error, or resolving into a declared-but-unassigned range) -- each retargeted to the new contract rather than weakened. The FilterType test's three dropped resolve/identity assertions are restored through its unaffected `key` path and the method renamed to match; it and its comment cross-reference are updated together. Owner's rulings, verbatim: - "Take (b). Only register can mint. get should not mint unless it falls through the _missing_'s minted ranges." - Choosing option 1 of three proposed: "I think 1 is correct mechanism we'd like." Build/tests, under plain unittest, methods not subTest records: test_const_registry_protocol.py 74/74, test_const_enum_get.py 8/8, test_const_enum_builtin_parity.py 33/33, tests/vendor/ 86/86 -- no regressions. tests/const/ as a whole carries 6 records: 3 methods inherited from #866 (fixed in #867) plus 1 further out-of-scope failure in test_const_enum_no_mint.py (contended with #865, handed over rather than edited here).
…ertion tests/const/test_const_enum_no_mint.py's GetNoLongerMintsTests.test_unresolvable_string_key_with_default_in_unassigned_range was contended with #865 (both touched pcapkit/corekit/enum.py-adjacent const/vendor territory) and deferred to this follow-up commit; #865 has now merged, so it lands here rather than being applied separately. Hardware.get('Definitely-Not-A-Member', 40) used to resolve 40 -- a value in Hardware's declared-but-unassigned 39-255 range, not a registered member -- via cls(default) -> _missing_ to an unregistered pseudo-member. #864 restricts default to a plain _value2member_map_ lookup, so it no longer resolves: the original KeyError for the key propagates instead, exactly the accepted cost the owner's ruling names. Re-measured fresh on the current tree (after #866/#867 and #862/#865 both landed): Hardware.get('Definitely-Not-A-Member', 40) raises KeyError: 'Definitely-Not-A-Member', members unchanged at 42 before and after. Checked for overlap with #865's own additions to this file (ETHERTYPE_UNASSIGNED_PROBES, EtherTypeMixedMintTests, the retired test_masked_old_xerox_row_converts_by_source) -- none: those are all EtherType key-path minting-order probes, unrelated to Hardware's default-path resolution this test covers. GetNoLongerMintsTests remains the right class.
* fix(corekit): stop EnumRegistry.get's default from minting (#864) Both `get`'s `cls(default)` sites -- the `str` branch's and the value branch's -- reached `_missing_` for a `default` that fell inside a still-minting registry's own range, growing the registry as a side effect of resolving `default` rather than `key`. - Replace both `cls(default)` calls with a plain `_value2member_map_` lookup (owner's ruling, option 1): `default` can no longer mint by construction, and a `default` naming no registered member now falls through to the same lookup error `key` itself would have raised. - `key` resolution is unchanged: `EtherType.get(0x0888)` still mints `Xyplex_0x0888` through `_missing_`, per the ruling's own exception -- landing in both lookup tables, exactly as `register` would leave it, since that is #775's own deliberately-kept-minting exception, not something `get` itself does. - Update the docstring's "It never mints" caveat (now qualified: true for `default`, not for `key` on the three still-minting registries), the now-false claim that an unregistered default still reaches `cls(default)`, and the Args/Raises text to match. - Add `GetDefaultNoMintTests` pinning the issue's own repro, the preserved key-path mint, default resolving to a registered value (int and str), and the exception type per path. Update two `NoDefaultSentinelTests` assertions that asserted the old `cls(default)` failure mode for an unregistered `-1`/`-1.0` default -- their docstrings now say plainly that they coincidentally pass on the pre-#857 tree too, and point at the test that still discriminates it. - Update four assertions in `test_const_enum_get.py` and `test_const_enum_builtin_parity.py` that encoded the superseded contract (an unresolvable/unregistered default failing with its own named error, or resolving into a declared-but-unassigned range) -- each retargeted to the new contract rather than weakened. The FilterType test's three dropped resolve/identity assertions are restored through its unaffected `key` path and the method renamed to match; it and its comment cross-reference are updated together. Owner's rulings, verbatim: - "Take (b). Only register can mint. get should not mint unless it falls through the _missing_'s minted ranges." - Choosing option 1 of three proposed: "I think 1 is correct mechanism we'd like." Build/tests, under plain unittest, methods not subTest records: test_const_registry_protocol.py 74/74, test_const_enum_get.py 8/8, test_const_enum_builtin_parity.py 33/33, tests/vendor/ 86/86 -- no regressions. tests/const/ as a whole carries 6 records: 3 methods inherited from #866 (fixed in #867) plus 1 further out-of-scope failure in test_const_enum_no_mint.py (contended with #865, handed over rather than edited here). * test(corekit): retarget the last #864 default-in-unassigned-range assertion tests/const/test_const_enum_no_mint.py's GetNoLongerMintsTests.test_unresolvable_string_key_with_default_in_unassigned_range was contended with #865 (both touched pcapkit/corekit/enum.py-adjacent const/vendor territory) and deferred to this follow-up commit; #865 has now merged, so it lands here rather than being applied separately. Hardware.get('Definitely-Not-A-Member', 40) used to resolve 40 -- a value in Hardware's declared-but-unassigned 39-255 range, not a registered member -- via cls(default) -> _missing_ to an unregistered pseudo-member. #864 restricts default to a plain _value2member_map_ lookup, so it no longer resolves: the original KeyError for the key propagates instead, exactly the accepted cost the owner's ruling names. Re-measured fresh on the current tree (after #866/#867 and #862/#865 both landed): Hardware.get('Definitely-Not-A-Member', 40) raises KeyError: 'Definitely-Not-A-Member', members unchanged at 42 before and after. Checked for overlap with #865's own additions to this file (ETHERTYPE_UNASSIGNED_PROBES, EtherTypeMixedMintTests, the retired test_masked_old_xerox_row_converts_by_source) -- none: those are all EtherType key-path minting-order probes, unrelated to Hardware's default-path resolution this test covers. GetNoLongerMintsTests remains the right class.
Please follow the guide below
[override]onvendor/pcapng/record_type.py:55, byte-identical onmainmake testpasses, and a test case covers the changeWhat is the purpose of your pull request?
fix— corrects a defectDescription of your pull request and other information
Fixes #866 —
mainis red right now: 12 tests across three files, allRecursionError.tests/const/test_const_enum_lookup.py,tests/const/test_const_enum_no_mint.py(RulingConversionDoesNotMintTests.test_converted_value_does_not_mintandtest_repeated_lookup_does_not_grow_members, two registries each), andtests/protocols/misc/test_pcapng_unit.py(7 of the 12). FivePython 3.xlegs plusGateandRequired checks passed, one defect.pcapkit/vendor/pcapng/{record_type,secrets_type}.pyrendered a two-line_missing_body whose first line did not return:Harmless while line one was
extend_enum(...), which registers the member so the followingcls(value)found it. #861 replaced it with_unregistered_member, which deliberately does not register — socls(value)missed again and re-entered_missing_.#861 only half-corrected it. It fixed the two generated files and never touched the two crawlers — and in the generated files it left
return cls(value)in place as unreachable dead code after the new return. That short-circuit is why #861's own CI and70fa92010were green (59 ok / 0 fail);e58618bdfregenerated from the stale crawlers and reintroduced it live. #861's cross-review verified byte-identical regeneration for 5 sampled crawlers of 82, and these two were not sampled.Both crawlers collapse to the single returning line the other 101 registries use, and the two const files are regenerated from them. Note
record_type.pyhasLINK = 'https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-03.html'and fetches it — onlysecrets_type.pyis offline — so that regeneration is not reproducible without connectivity, though it came back byte-identical.The guard is at the crawler layer deliberately:
test_const_enum_lookup.pyalready sweeps every committed registry and is what caught this, so the uncovered layer was the source a crawler emits. The invariant it enforces is narrow and true — a hard-coded body must end in a return, and no emitted line may re-enter_missing_for the same value. It is not "every emitted line must return": 53 crawlers emit 109 legitimately non-returning lines, the shapeVendor.processitself uses atdefault.py:341-343. And the guard sees only 35 of 96 crawlers; 61 are invisible (53append-built, 8 returning no(enum, miss)pair) and are covered behaviourally instead.Verified: 6 new tests pass; with the four source files reverted, 4 of 6 fail counted as methods — the self-recursive-lookup guard, the named-pair guard, and both behavioural lookups. The other two pass either way, the last-line check by design since #866's body ended on
return cls(value).tests/vendor92,tests/project156,tests/const190,test_pcapng_unit.py93, all green on the fixed tree.