fix(const,vendor): Old Xerox ethertype range shadowed by IEEE802.3 range - #865
Conversation
|
The 6 red checks here are not this PR's defect — they are #866's, inherited from CI builds the PR merge ref, not the head. Run whereas this branch's own tree has the correct Every failure is that recursion, not anything in the ethertype change: Five #865 is therefore gated on #867 merging, after which this needs an update to pick up the fix before its CI can be read. Keeping Worth stating as a general trap, since it cuts both ways: because CI tests the merge ref, a PR can go red for a defect it does not contain, and can go green while carrying one that only appears once |
|
NEEDS CHANGES on
The third is a testing gap, not just stale prose: Everything else confirmed independently, and two claims came back stronger than the PR made them. Two latent warts, neither blocking and neither present today: identical ranges silently reverse precedence ( One nit in a file the PR owns:
Also now verified rather than UNVERIFIED: pylint 10.00/10 on both changed source files, mypy clean on the vendor one, isort clean on all three — changed files only, package-wide still unverified. #866's defect class is not present here: 57 |
d1626d3 to
8901e28
Compare
|
Round 2 pushed — head All three stale sites addressed, and the author went further than asked on the first one rather than just deleting a sentence. It read the other reason EtherType was excluded from The probe table gained the behavioural coverage, which was the valuable half: ETHERTYPE_OLD_XEROX_LABEL = 'Old_Xerox_Experimental_values_Invalid_as_an_Ethertype_since_1983'
ETHERTYPE_UNASSIGNED_PROBES = {
0x8039: 'DEC_Unassigned',
0x0101: ETHERTYPE_OLD_XEROX_LABEL,
}so The non-discriminating test is fixed: arrival order is now INNER, OUTER, MIDDLE, the only one of the three forcing a genuine mid-list insert (MIDDLE lands at index 1). Counts: the new file (9) plus Re-review dispatched on the same head. The six red CI checks remain #866's inherited defect, not this PR's — #867 clears them. One pre-existing inconsistency surfaced and deliberately not touched: |
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).
|
NEEDS CHANGES on First, a correction to my own previous comment. I wrote that "arrival order INNER, OUTER, MIDDLE is the only shape requiring a mid-list insert". That is false, and I relayed it without deriving it. Worse, "forces a mid-list insert" is not even the criterion that matters. Re-derived myself against the real The swap traded blind spots rather than removing one. The old Required: correct the comment's two false claims, and exercise both arrival orders — The implementation is unaffected: round 1's exhaustive permutation and 4000-trial fuzz already proved Site 1 confirmed with the historical premise checked, which is the part I most wanted verified. Site 2's retirement confirmed rather than retracted: the behavioural probe dominates both of the retired source assertions, and adds order-sensitivity the string search never had — the pre-fix run is the proof. Nothing about EtherType was lost in the 39 deleted lines; no dangling reference to the old The pre-existing |
… IEEE802.3 one it is inside EtherType._missing_ tested 0x0000-0x05DC (IEEE802.3 Length Field) before 0x0101-0x01FF (Old Xerox Experimental), and the second range is wholly contained in the first. The generated method returns on the first matching `if`, so the Old Xerox branch was unreachable for every value it covers: EtherType(0x0101).name resolved to 'IEEE802_3_Length_Field_0x0101' instead of the Old Xerox name. - pcapkit/vendor/reg/ethertype.py: the range bounds come from the live IANA CSV, not a literal table, so the fix is general rather than a special case for these two constants. process() now collects each range row's (start, stop, body) and a new EtherType._insert_range places each ahead of the first already-placed range that fully contains it, leaving every non-overlapping pair in the CSV's own row order. - pcapkit/const/reg/ethertype.py: regenerated via `python -m pcapkit.vendor reg.ethertype`; the only diff is the two branches swapping places. - tests/const/test_const_enum_no_mint.py: EtherType joins RULING_CONVERTED_WITH_REACHABLE_GAP now that its masked branch is reachable; ETHERTYPE_UNASSIGNED_PROBES gains 0x0101 for a behavioural no-mint proof; the now-stale source-only masking test is retired (subsumed, explained in place) rather than left asserting a defect that no longer exists. - tests/const/test_const_ethertype_862_unit.py: the nested-range ordering regression pin now drives two arrival orders, since no single order discriminates both of the two specific wrong "insert ahead of a container" implementations considered during review; the docstring states what it does and does not prove. - Swept all 56 range tests in the generated _missing_: this pair is the only containment among them. tests/const, tests/vendor pass under plain unittest and under coverage+pytest; new/changed tests pin both the symptom and the root cause, and are proven to fail without the fix. Fixes #862
8901e28 to
7f7b24e
Compare
|
Round 3 pushed — head The author independently re-derived the truth table before changing anything, implementing both naive rules against the real It then took the two-subTest form rather than gambling on an unproven four-range chain: Counts, methods not subTest records: 29 for the two files together, Label back to A measurement trap worth recording, since it nearly produced a false accusation from me. |
|
GOOD TO GO on The two-subTest form is sufficient and neither subTest is redundant — each fails under exactly the naive rule it claims and passes under the other: Dropping either reopens a blind spot, so the pair is minimal as well as sufficient. On my "is there a third wrong implementation" question — yes, one, and it is the obvious alternative design. The retraction reads correctly and, if anything, under-claims — pinning the correct output on two arrival orders excludes an infinite family of misordering implementations, not just the two named. The review would not change it: a test docstring that understates its reach errs in the safe direction, and "regression pin, not a general correctness proof" is the honest characterisation. The provenance note is accurate — the exhaustive proof (six three-level orders, 24 four-level, 4000-trial fuzz) was review evidence and genuinely does not live in the repo. Counts as methods, independently walked as well as read from
|
|
Unblocked — #867 merged at 00:10:35Z as Verified on merged So the |
|
Same four files, same content: Two notes for the merge itself: The branch now carries a merge commit, so it is two commits rather than the house one-per-PR. Harmless if this lands by squash — which is how #861, #863 and #867 landed ( CI is re-running from scratch on the new base: 10 ok / 0 fail / 48 in flight at the time of writing. The previous six red marks were #866's recursion, which #867 fixed at |
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.
make pylint,make mypy,make isort) — not run locally in this session; diff follows the style of the surrounding, unchanged codemake testpasses, and a test case covers the change — ran viacoverage run -m pytestontests/const(188 passed, 40027 subtests) andtests/vendor(86 passed, 189 subtests); did not run the full suite (documented OOM risk on this tree)What is the purpose of your pull request?
fix— corrects a defectDescription of your pull request and other information
Fixes #862.
EtherType._missing_tested0x0000 <= value <= 0x05DC(IEEE802.3Length Field) before
0x0101 <= value <= 0x01FF(Old Xerox Experimental). Thesecond range is wholly contained in the first, and the generated method
returns on the first matching
if, so the Old Xerox branch was unreachablefor every value it covers —
EtherType(0x0101).nameresolved to'IEEE802_3_Length_Field_0x0101'instead of the Old Xerox name.The range bounds come from the live IANA CSV rather than a literal table
(unlike #841's IPX socket fix), so the fix in
pcapkit/vendor/reg/ethertype.pyis general:process()now collects eachrange row and a new
EtherType._insert_rangeplaces each range ahead of thefirst already-placed range that fully contains it, leaving every
non-overlapping pair in the CSV's own row order.
pcapkit/const/reg/ethertype.pyis regenerated (
python -m pcapkit.vendor reg.ethertype); the diff is exactlythe two branches swapping places.
Swept all 56 range tests in the generated
_missing_: this pair is the onlycontainment among them, so no other subsumed range exists today.
New tests in
tests/const/test_const_ethertype_862_unit.pypin both thesymptom (against the committed const file) and the root cause (against
process()fed a CSV fixture reproducing IANA's own rows), plus the generalordering rule against synthetic nested ranges. Verified each fails against
the pre-fix generator/const file.