Skip to content

fix(foundation): add the #718 identity guard to the five registrars it missed - #742

Merged
JarryShaw merged 1 commit into
mainfrom
fix/739-extractor-registry-identity-guard
Sep 24, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/739-extractor-registry-identity-guard

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

What is the purpose of your pull request?

  • fix — corrects a defect

Description of your pull request and other information

Five registrars warned on mere key presence, so re-registering the exact same class/descriptor read as "overwriting X" even though nothing was displaced. Issue #718 gave ten code-keyed registrars an identity guard (incumbent is not None and incumbent is not new, landed in PR #726); these five sit outside its wording ("names only the key") but share the defect.

Registrar File Guard before Guard after
register_engine extraction.py if name in cls.__engine__: incumbent = cls.__engine__.get(name); if incumbent is not None and incumbent is not engine:
register_reassembly extraction.py if protocol in cls.__reassembly__: same shape, cls.__reassembly__
register_traceflow extraction.py if protocol in cls.__traceflow__: same shape, cls.__traceflow__
register_dumper extraction.py if format in cls.__output__: compares the incumbent dumper, index 0 of the stored (dumper, ext) pair
register_dumper traceflow/traceflow.py same as above same fix, its own __output__

Matched against PR #726's converted ProtocolBase.register (the diff, not main, which doesn't have it yet). Message text unchanged throughout.

Measured, not assumed: at import time __engine__ (6), __reassembly__ (3), __traceflow__ (1), and both __output__s (8 each) hold only ModuleDescriptors — zero classes. So the first real registration still legitimately warns; only a repeat is now silent — narrower than #718's win, as the issue predicted. __engine__/__reassembly__/__traceflow__ are plain dicts. Both __output__s are collections.defaultdict (format -> (dumper, ext)): confirmed .get()/in on a miss leave len() unchanged, while d[missing] inserts the default-factory tuple (+1). .get() stays non-inserting; the identity check compares the incumbent dumper only, so a re-registration that changes just ext is identity-equal on the dumper and stays silent too — judged defensible rather than switching to a full-pair comparison.

Judged non-breaking: a correct caller sees strictly fewer warnings, never a different return value or exception; not ticking breaking.

5 new test methods (one per registrar) pin: same-object re-registration silent, different-object still warns with unchanged message, registry write happens both ways (plus a same-dumper/different-ext case for the two dumper sites). Reverted each guard hunk alone and confirmed all five fail with AssertionError: Expected 'warn' to not have been called. Called 1 times.

Ran tests/foundation/test_extraction.py + tests/foundation/traceflow/test_traceflow_base.py: 16 → 21 passed (28 subtests, unchanged). Coverage: extraction.py 367 → 372 stmts (miss unchanged at 7, 96%), traceflow.py 121 → 123 stmts (0 miss, 100%) — all new statements exercised.

Fixes #739.

@JarryShaw JarryShaw added fix Pull requests that fix a defect (fix: subject prefix) review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Sep 24, 2026
@JarryShaw
JarryShaw force-pushed the fix/739-extractor-registry-identity-guard branch from 6cbcb56 to f0a1f5b Compare September 24, 2026 15:30
@JarryShaw JarryShaw changed the title fix(foundation): add the #718 identity guard to the three registrars it missed fix(foundation): add the #718 identity guard to the five registrars it missed Sep 24, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Self-dispatched review (Opus), not the independent cross-review — I dispatched this myself, so treat it as weaker evidence than an independently-briefed review; posting per house rule that a sub-agent's verdict has to reach the PR if it's worth reporting at all.

Scope: covered only commit 6cbcb56ad (the original 3 sites — register_engine/register_reassembly/register_traceflow). Verdict then: GOOD TO GO — independently re-derived the dict-content counts (6/3/1, all ModuleDescriptor), the non-inserting .get()/in invariant, re-ran the guard-revert failure check itself (AssertionError: Expected 'warn' to not have been called), and confirmed test/coverage counts matched exactly. One non-blocking nuance raised: a caller using warnings.simplefilter('error', RegistryWarning) would see a control-flow change on same-object re-registration — agreed this doesn't warrant breaking, consistent with #726.

Does not cover the two register_dumper sites added in the current head (f0a1f5b15) — those are new since that review ran. An independently-dispatched cross-review covering the full current diff is pending separately and is the verdict that gates this PR.

@JarryShaw

Copy link
Copy Markdown
Owner Author

❌ NEEDS CHANGES @ f0a1f5b15 — docstring at extraction.py:383 and traceflow/traceflow.py:220 states the opposite of the behaviour its own test pins; three PR-body numbers wrong.

@JarryShaw

Copy link
Copy Markdown
Owner Author

❌ NEEDS CHANGES @ f0a1f5b15 — extraction.py:383 and traceflow/traceflow.py:220 assert the opposite of what their own test pins; three body numbers wrong.

Independent cross-review — distinct from the self-dispatched Opus review above, which covered only 6cbcb56ad (3 sites) and never saw the two register_dumper sites. Both findings below sit in that uncovered part.

Blocking. Both files carry the identical sentence: "…the identity check compares the incumbent dumper (index 0), not the pair -- a re-registration that only changes ext still counts as a different entry for that reason." Measured at both sites: an ext-only re-registration counts as the same entry and is silent (the ext is still updated). The test's own comment ("still identity-equal on the dumper, so still silent") and the PR body ("ext alone changing shouldn't count as 'different'") both contradict the docstring. Fix the sentence, or compare the pair so an ext-only change warns — one or the other, not both.

Body numbers. 19 → 21 passed → real 16 → 21; extraction.py 370 → 372 stmts → real 367 → 372 (367+5 matches the diff's own added statements); 8 new tests → 5 test methods. traceflow.py 121 → 123, miss unchanged at 7, 96%/100%, 28 subtests — all confirmed.

checked result
5 sites converted, each compares the object it then stores ✅
sixth site remaining ✅ none — re-derived 42 guards across 21 files (matches); 18 are #726's enum-code shape, 1 already guarded, 10 are #726's
insertion invariant ✅ both __output__ = defaultdict(8 entries): in Δ0, .get() Δ0, [] +1. __engine__/__reassembly__/__traceflow__ = plain dict(6/3/1): Δ0, [] KeyError. Swap is free at all five
does head ever warn more than base? ✅ never — 29-scenario base-vs-head differential: 12 flips 1→0, zero 0→1
each new test fails without its fix ✅ all five, not a sample: AssertionError: Expected 'warn' to not have been called. Called 1 times.
first real registration still warns ✅ all 26 seeded values are ModuleDescriptor, 0 classes — only repeats go silent; a defaultdict-polluted key still warns on first real registration
CI ✅ 15/15 required contexts green; 3 skips are conditional, non-required
merge gate ✅ merged into current origin/main (= 9b2d927c2, a no-op) → tests/foundation/: 257 passed, 11 skipped, 402 subtests, 0 failed

The factory returns a 2-tuple, so incumbent_entry[0] cannot raise; is not None is a sound "absent" sentinel because no registry holds a None value (checked all five).

breaking — agree it does not apply. These five carried no Warns: contract at base, nothing that worked stops working (only spurious warnings/errors disappear), genuine displacement still warns at all five, and #726 carries no breaking for the same change to 10 documented sites.

Raised and dismissed: registry/protocols.py:171's "presence-only is right for caller-supplied keys" principle does not contradict this PR — #726 deletes that paragraph and replaces it with "none of them does now", which #742 is precisely what makes true. Nit: #737/#735 keep all 8 commit-type lines; 7 unticked ones were deleted here. Fixes #739., one type ticked, fix label only, and both standing exceptions are correct.

❌ NEEDS CHANGES @ f0a1f5b15 — fix the ext sentence at extraction.py:383 and traceflow/traceflow.py:220; correct the three body numbers.

@JarryShaw JarryShaw added review: needs-changes Cross-review at the current head says changes are required; see the verdict comment and removed review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Sep 24, 2026
JarryShaw added a commit that referenced this pull request Sep 24, 2026
Library modules imported a *Base class aliased to its public name, so the
source read `class PCAP(Engine)` while actually inheriting `EngineBase`. Per
the ruling on #514 the split is permanent and library classes inherit the
base, so the base is now imported under its own name.

* rewrote 28 of the 82 alias imports -- EngineBase 8 of 8, ReassemblyBase 3
  of 3, DumperBase 2 of 2, TraceFlowBase 2 of 2, ProtocolBase 13 of 67
* renamed the code and annotation references that followed, including
  `cast()` targets, `TypeVar(bound=)` and `# type:` comments, but not the
  string values inside `Literal[...]`
* corrected four `docs/source` index pages that named the public class while
  their own class diagram roots the hierarchy at the base
* added `tests/test_base_class_contract.py`, pinning the contract per suite

The four non-protocols families are complete. The remaining 54 sites are all
`ProtocolBase` ones in paths #726 and #742 own, recorded as prefixes in
`PENDING_ALIAS_PATHS` so the entry fails once its pull request lands.

No behaviour change: name registry 38 keys before and after,
`descendants(Public)` 0 in all five suites, 45 dispatches identical and still
identical after clearing the name registry, mypy at its 112-error baseline
with none introduced.
…t missed

register_engine, register_reassembly, register_traceflow, and both
register_dumper copies (Extractor and TraceFlowBase) warned on mere key
presence, so re-registering the exact same class or descriptor read as
"overwriting X" even though nothing was displaced. #718 fixed this for ten
code-keyed registrars but excluded these five because their message wording
falls outside its criterion, even though they share the defect.

- Replace each `if key in cls.__dict__:` guard with `incumbent = cls.__dict__
  .get(key)` + `incumbent is not None and incumbent is not value`, matching
  the identity-guard shape #726 gives the ten sibling registrars. Message
  text is unchanged.
- register_engine/reassembly/traceflow: all three seed dicts hold only
  ModuleDescriptors, never a class, so the *first* real registration
  (descriptor -> class) still legitimately warns; only a *repeat*
  registration is now silent. Plain dicts, so `.get()`/`in` are both
  non-inserting.
- register_dumper (both copies): __output__ maps format -> (dumper, ext), so
  the identity check compares the incumbent dumper (index 0), not the pair --
  a re-registration that only changes ext is still identity-equal on the
  dumper and stays silent. __output__ is a `collections.defaultdict`;
  measured that `.get()` does not invoke the default factory the way a
  subscript access would (confirmed: len() unchanged on a `.get()`/`in`
  miss, +1 on a `d[missing]` subscript). `incumbent_entry[0]` is safe on a
  factory-produced miss too, since the factory itself returns a 2-tuple.
- Add tests pinning, per registrar: same-object re-registration is silent,
  different-object replacement still warns with the same message, and the
  registry write happens on both paths. Verified all five fail against the
  unfixed guard.

Ran tests/foundation/test_extraction.py + tests/foundation/traceflow/
test_traceflow_base.py: 16 -> 21 passed (28 subtests, unchanged). coverage:
extraction.py 367 -> 372 stmts (miss unchanged at 7, 96%), traceflow.py
121 -> 123 stmts (0 miss, 100%) -- all new statements fully exercised.

Fixes #739.
@JarryShaw
JarryShaw force-pushed the fix/739-extractor-registry-identity-guard branch from f0a1f5b to 0071801 Compare September 24, 2026 16:06
@JarryShaw JarryShaw added review: pending No verdict for the current head - never reviewed, or the head moved since the last one and removed review: needs-changes Cross-review at the current head says changes are required; see the verdict comment labels Sep 24, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Addressed both blockers from the independent review, pushed as 00718012f.

Docstring fix, not a code change. The ext-only sentence in both register_dumper docstrings said the opposite of what the code and its own test do. Chose to fix the prose rather than compare the full (dumper, ext) pair: the dumper is what "the same object" means for this guard's purpose (the message is about the dumper being overwritten, not the extension), and an ext-only re-registration staying silent is defensible — matches the reviewer's read. Both sites now read: "...a re-registration that only changes ext is still identity-equal on the dumper and stays silent, since the dumper is what 'the same object' means here, not the pair as a whole."

Corrected PR-body numbers, re-derived from a clean origin/main scratch worktree, not from memory: tests 16 → 21 passed (28 subtests unchanged), extraction.py 367 → 372 stmts (miss unchanged at 7, 96%), traceflow.py 121 → 123 stmts (0 miss, 100%), 5 new test methods (not 8/9).

Re-ran tests/foundation/test_extraction.py + tests/foundation/traceflow/test_traceflow_base.py (21 passed) and isort/mypy on both changed files after the docstring edit — all clean.

Label flipped review: needs-changes → review: pending for the new head.

@JarryShaw

Copy link
Copy Markdown
Owner Author

✅ GOOD TO MERGE @ 00718012f — docstring now matches the measured behaviour at both dumper sites and all four body numbers check out; supersedes my ❌ NEEDS CHANGES at f0a1f5b15.

@JarryShaw

Copy link
Copy Markdown
Owner Author

✅ GOOD TO MERGE @ 00718012f — both register_dumper docstrings now state what the code actually does, and all four body numbers are correct. Supersedes my ❌ at f0a1f5b15.

Independent cross-review, distinct from the self-dispatched one above. Narrow re-check: f0a1f5b15..00718012f is docstring-only (2 files, +10/−6, no executable line changed), so the first pass's measurements carry forward — re-confirmed by re-running the 29-scenario differential at this head and getting a byte-identical result.

Blocker resolved. New text, identical in both files: "a re-registration that only changes ext is still identity-equal on the dumper and stays silent, since the dumper is what 'the same object' means here, not the pair as a whole." Measured at 00718012f: ext-only repeat → warned=0 at both sites. True, not merely reworded. grep for still counts / different entry across both files returns nothing — no residual claim.

On pair-vs-dumper — my own judgement, not deference. Comparing incumbent_entry[0] is the right fix. The warning's subject is the dumper (dumper {format} already registered), and ext is metadata hanging off it, so correcting an extension for an unchanged dumper displaces no registered dumper. It also matters that registry/foundation.py:125-126 and dumpkit/common.py:158,163 each fan one call into two registries: a full-pair comparison would make a routine ext correction warn twice for nothing. A pair check would have been the wrong call.

re-checked at 00718012f result
diff vs f0a1f5b15 ✅ docstring-only; adds 0 statements, so 367→372 / 121→123 still hold
new wording true ✅ ext-only repeat silent at both sites; 29/29 scenarios identical to f0a1f5b15
residual contradiction in either file ✅ none
body numbers ✅ 16→21 passed, extraction.py 367→372 (miss 7, 96%), traceflow.py 121→123 (0 miss, 100%), 5 new test methods — all four match my independent measurements
CI ✅ 20 success + 3 conditional skips = 23; 15/15 required green, 0 failed, 0 cancelled
merge gate, re-run not carried forward ✅ 257 passed, 11 skipped, 402 subtests, 0 failed

Why the gate was re-run: main moved to 9813aa377, which lands in tests/foundation/engines/test_new_engine_parity_runtime.py — inside the directory the gate covers, so the f0a1f5b15 result no longer applied. Merged 9813aa377 into 00718012f → a7e2dd005, two parents, clean, no pcapkit/ file touched. BEHIND is that one test-only commit, not a conflict. (The repo sets merge.ff=only, so a bare git merge aborts rather than merging — worth knowing for anyone else running this gate.)

Non-blocking nits, neither worth a revision on its own:

  • The opener still says re-registering the exact same object "is a silent no-op", while the new sentence defines "the same object" as the dumper alone — and an ext-only repeat does still write. Nothing is false as written; "stays silent" without "no-op", or an explicit "the registry write happens either way", would close the gap.
  • unlike the other three is an orphaned 34-char line in an otherwise 68–76-char block, in both files — reflow leftover.

breaking still does not apply, for the reasons given at f0a1f5b15.

✅ GOOD TO MERGE @ 00718012f — supersedes my ❌ NEEDS CHANGES at f0a1f5b15.

@JarryShaw JarryShaw added 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 24, 2026
@JarryShaw
JarryShaw merged commit 0a3abff into main Sep 24, 2026
24 checks passed
@JarryShaw
JarryShaw deleted the fix/739-extractor-registry-identity-guard branch September 24, 2026 17:37
JarryShaw added a commit that referenced this pull request Sep 24, 2026
…one (#750)

Library modules imported a *Base class aliased to its public name, so the
source read `class PCAP(Engine)` while actually inheriting `EngineBase`. Per
the ruling on #514 the split is permanent and library classes inherit the
base, so the base is now imported under its own name.

* rewrote 28 of the 82 alias imports -- EngineBase 8 of 8, ReassemblyBase 3
  of 3, DumperBase 2 of 2, TraceFlowBase 2 of 2, ProtocolBase 13 of 67
* renamed the code and annotation references that followed, including
  `cast()` targets, `TypeVar(bound=)` and `# type:` comments, but not the
  string values inside `Literal[...]`
* corrected four `docs/source` index pages that named the public class while
  their own class diagram roots the hierarchy at the base
* added `tests/test_base_class_contract.py`, pinning the contract per suite

The four non-protocols families are complete. The remaining 54 sites are all
`ProtocolBase` ones in paths #726 and #742 own, recorded as prefixes in
`PENDING_ALIAS_PATHS` so the entry fails once its pull request lands.

No behaviour change: name registry 38 keys before and after,
`descendants(Public)` 0 in all five suites, 45 dispatches identical and still
identical after clearing the name registry, mypy at its 112-error baseline
with none introduced.
JarryShaw added a commit that referenced this pull request Sep 24, 2026
…arning count

Round 8: ran the merge-base/changed-files/cited-path intersection to
completion (0c7f2b7..origin/main: 43 commits/124 files; 54 cited paths,
34 of them touched by that range) instead of trusting a tense grep.

- :1540/:1542 -- "93 of the 95 sites"/"48 of the 49 record lengths" ->
  94 of 95 / 49 of 49; zero old-expression sites remain on main.
- :1544-1546, :1566 -- "LOCATOR_SET keeps the old expression ... is
  currently right" / "is unchanged in both respects" -> past tense;
  #679 fixed both LOCATOR_SET sites.
- :1566 -- "HIP_COPIES stays at two" -> past tense; #679 fixed
  LOCATOR_SET's Length unit, #689 then dropped HIP_COPIES to one.
- :1976-1977 -- "pcapng.txt ... wants a separate refresh" -> past
  tense; #685 removed it from the index instead of regenerating it.
- :2327-2328 -- "55 warnings on main before this change, 56 after"
  (-b html) -> 53/54; the raw `grep -c WARNING:` double-counts two
  Scapy import lines as Sphinx warnings, confirmed live on this head
  with both -b dummy and -b html (real 36/raw 38 with const/reg.rst
  excluded, same +2 gap either way).
- :834 -- stale ``protocol.py:1016`` -> ``:1413`` (the actual
  ``self._file.read()`` call inside ``_read_fileng``).
- :1969 -- the #646 entry's coverage renumbering was wrong twice over
  (first ``1153 to 1265``, then ``1153 to 1443``, the latter being
  main's ``def`` line, which always executes and can never be the
  single miss). Corrected to ``1248 to 1360``, the ``warn(...)``
  statement's line before/after #646's own diff.

Regenerated CHANGELOG.md from the edited entries.

changelog_md.py --check: exit 0. pytest tests/project/test_changelog_md.py -q:
47 passed, 37 subtests.

Follow-up: origin/main advanced through #726/#740/#741/#742 (to 0a3abff)
and then #747/#748 (to 074c53e) while this sat at good-to-go; #726 moved
three more claims anchored on files it touched.

- :834 -- ``protocol.py:1413`` -> ``:1411``; #726 shifted the
  ``self._file.read()`` call in ``_read_fileng`` by -2 lines.
- :2382-83 -- traceflow.py "Line 406" -> "Line 424"; #742 inserted 18
  lines above the ``#: Type[Dumper]: Dumper class.`` comment.
- :2099-2104, :2187-89 -- the "seven code-keyed parser registrars" and
  ``Option.register`` are no longer presence-only. #726, fixing #718,
  gave all seven -- and ``Option.register`` itself -- the same identity
  guard ``register_protocol`` already had; reworded both passages to
  say so, confirmed against the guards' own current docstrings.

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.
JarryShaw added a commit that referenced this pull request Sep 24, 2026
…arning count

Round 8: ran the merge-base/changed-files/cited-path intersection to
completion (0c7f2b7..origin/main: 43 commits/124 files; 54 cited paths,
34 of them touched by that range) instead of trusting a tense grep.

- :1540/:1542 -- "93 of the 95 sites"/"48 of the 49 record lengths" ->
  94 of 95 / 49 of 49; zero old-expression sites remain on main.
- :1544-1546, :1566 -- "LOCATOR_SET keeps the old expression ... is
  currently right" / "is unchanged in both respects" -> past tense;
  #679 fixed both LOCATOR_SET sites.
- :1566 -- "HIP_COPIES stays at two" -> past tense; #679 fixed
  LOCATOR_SET's Length unit, #689 then dropped HIP_COPIES to one.
- :1976-1977 -- "pcapng.txt ... wants a separate refresh" -> past
  tense; #685 removed it from the index instead of regenerating it.
- :2327-2328 -- "55 warnings on main before this change, 56 after"
  (-b html) -> 53/54; the raw `grep -c WARNING:` double-counts two
  Scapy import lines as Sphinx warnings, confirmed live on this head
  with both -b dummy and -b html (real 36/raw 38 with const/reg.rst
  excluded, same +2 gap either way).
- :834 -- stale ``protocol.py:1016`` -> ``:1413`` (the actual
  ``self._file.read()`` call inside ``_read_fileng``).
- :1969 -- the #646 entry's coverage renumbering was wrong twice over
  (first ``1153 to 1265``, then ``1153 to 1443``, the latter being
  main's ``def`` line, which always executes and can never be the
  single miss). Corrected to ``1248 to 1360``, the ``warn(...)``
  statement's line before/after #646's own diff.

Regenerated CHANGELOG.md from the edited entries.

changelog_md.py --check: exit 0. pytest tests/project/test_changelog_md.py -q:
47 passed, 37 subtests.

Follow-up: origin/main advanced through #726/#740/#741/#742 (to 0a3abff)
and then #747/#748 (to 074c53e) while this sat at good-to-go; #726 moved
three more claims anchored on files it touched.

- :834 -- ``protocol.py:1413`` -> ``:1411``; #726 shifted the
  ``self._file.read()`` call in ``_read_fileng`` by -2 lines.
- :2382-83 -- traceflow.py "Line 406" -> "Line 424"; #742 inserted 18
  lines above the ``#: Type[Dumper]: Dumper class.`` comment.
- :2099-2104, :2187-89 -- the "seven code-keyed parser registrars" and
  ``Option.register`` are no longer presence-only. #726, fixing #718,
  gave all seven -- and ``Option.register`` itself -- the same identity
  guard ``register_protocol`` already had; reworded both passages to
  say so, confirmed against the guards' own current docstrings.

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.

Cross-review at 948ac49 came back NEEDS CHANGES: the round-12 edit fixed two
sites of the harmonisation claim and left its twin, plus its own reasoning,
asserting the opposite; and four numbers anchored on files the merges touched
had drifted independently of #726.

- :1877-1878, :1883-1884 (#675) -- "carries the guarded ``if code in
  cls.__xxx__: warn(...)``" / "every sibling warns on mere presence" ->
  past tense, noting #726 later gave all seven the identity guard this
  entry's own comparison assumes they lack.
- :2100-2109 -- dropped the retained "yields two keys and never reaches
  one key twice" (false: ``Internet.register(TransType.TCP, TCP)`` warns
  once, incumbent.klass is TCP) and "leaves a different-class test
  undecidable" (contradicted by :2404-2406's own ``incumbent is not
  protocol`` definition); replaced with the actual false positive the
  guard has -- pre-seeded ``ModuleDescriptor`` incumbents never compare
  equal to the resolved class.
- :2192 -- reflowed the ``Option.register`` paragraph (orphan lines
  fixed alongside).
- :2387-2388 -- the ``Type[Dumper]`` quote now matches what is actually
  at line 424 (post-#709-fix), rather than the pre-fix bare form.
- :945 -- ``README.md`` (103) -> (102).
- :1136 -- "75 of the 117 modules" -> "77 ... after #647 below adds
  the same ending to three more" (drifted via #647, independent of the
  four merges).
- :2119 -- dropped the irreproducible pylint "364 messages" figure;
  kept mypy's 112, which does reproduce.
- :2119 -- "326 registry writes" -> 327 (``R1CounterParameter``'s
  second code, from #690).

Also fixed six false claims in the PR body (separate from the .rst):
hunk/line counts, six-commits -> 46, the 5-row table's implied total,
"not trimmed", main's red/green state, and the now-unreachable
cherry-pick target.

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.
JarryShaw added a commit that referenced this pull request Sep 24, 2026
…arning count

Round 8: ran the merge-base/changed-files/cited-path intersection to
completion (0c7f2b7..origin/main: 43 commits/124 files; 54 cited paths,
34 of them touched by that range) instead of trusting a tense grep.

- :1540/:1542 -- "93 of the 95 sites"/"48 of the 49 record lengths" ->
  94 of 95 / 49 of 49; zero old-expression sites remain on main.
- :1544-1546, :1566 -- "LOCATOR_SET keeps the old expression ... is
  currently right" / "is unchanged in both respects" -> past tense;
  #679 fixed both LOCATOR_SET sites.
- :1566 -- "HIP_COPIES stays at two" -> past tense; #679 fixed
  LOCATOR_SET's Length unit, #689 then dropped HIP_COPIES to one.
- :1976-1977 -- "pcapng.txt ... wants a separate refresh" -> past
  tense; #685 removed it from the index instead of regenerating it.
- :2327-2328 -- "55 warnings on main before this change, 56 after"
  (-b html) -> 53/54; the raw `grep -c WARNING:` double-counts two
  Scapy import lines as Sphinx warnings, confirmed live on this head
  with both -b dummy and -b html (real 36/raw 38 with const/reg.rst
  excluded, same +2 gap either way).
- :834 -- stale ``protocol.py:1016`` -> ``:1413`` (the actual
  ``self._file.read()`` call inside ``_read_fileng``).
- :1969 -- the #646 entry's coverage renumbering was wrong twice over
  (first ``1153 to 1265``, then ``1153 to 1443``, the latter being
  main's ``def`` line, which always executes and can never be the
  single miss). Corrected to ``1248 to 1360``, the ``warn(...)``
  statement's line before/after #646's own diff.

Regenerated CHANGELOG.md from the edited entries.

changelog_md.py --check: exit 0. pytest tests/project/test_changelog_md.py -q:
47 passed, 37 subtests.

Follow-up: origin/main advanced through #726/#740/#741/#742 (to 0a3abff)
and then #747/#748 (to 074c53e) while this sat at good-to-go; #726 moved
three more claims anchored on files it touched.

- :834 -- ``protocol.py:1413`` -> ``:1411``; #726 shifted the
  ``self._file.read()`` call in ``_read_fileng`` by -2 lines.
- :2382-83 -- traceflow.py "Line 406" -> "Line 424"; #742 inserted 18
  lines above the ``#: Type[Dumper]: Dumper class.`` comment.
- :2099-2104, :2187-89 -- the "seven code-keyed parser registrars" and
  ``Option.register`` are no longer presence-only. #726, fixing #718,
  gave all seven -- and ``Option.register`` itself -- the same identity
  guard ``register_protocol`` already had; reworded both passages to
  say so, confirmed against the guards' own current docstrings.

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.

Cross-review at 948ac49 came back NEEDS CHANGES: the round-12 edit fixed two
sites of the harmonisation claim and left its twin, plus its own reasoning,
asserting the opposite; and four numbers anchored on files the merges touched
had drifted independently of #726.

- :1877-1878, :1883-1884 (#675) -- "carries the guarded ``if code in
  cls.__xxx__: warn(...)``" / "every sibling warns on mere presence" ->
  past tense, noting #726 later gave all seven the identity guard this
  entry's own comparison assumes they lack.
- :2100-2109 -- dropped the retained "yields two keys and never reaches
  one key twice" (false: ``Internet.register(TransType.TCP, TCP)`` warns
  once, incumbent.klass is TCP) and "leaves a different-class test
  undecidable" (contradicted by :2404-2406's own ``incumbent is not
  protocol`` definition); replaced with the actual false positive the
  guard has -- pre-seeded ``ModuleDescriptor`` incumbents never compare
  equal to the resolved class.
- :2192 -- reflowed the ``Option.register`` paragraph (orphan lines
  fixed alongside).
- :2387-2388 -- the ``Type[Dumper]`` quote now matches what is actually
  at line 424 (post-#709-fix), rather than the pre-fix bare form.
- :945 -- ``README.md`` (103) -> (102).
- :1136 -- "75 of the 117 modules" -> "77 ... after #647 below adds
  the same ending to three more" (drifted via #647, independent of the
  four merges).
- :2119 -- dropped the irreproducible pylint "364 messages" figure;
  kept mypy's 112, which does reproduce.
- :2119 -- "326 registry writes" -> 327 (``R1CounterParameter``'s
  second code, from #690).

Also fixed six false claims in the PR body (separate from the .rst):
hunk/line counts, six-commits -> 46, the 5-row table's implied total,
"not trimmed", main's red/green state, and the now-unreachable
cherry-pick target.

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.

Cross-review at 1749cc0 came back NEEDS CHANGES: round 13 fixed five of
the nine sites and introduced four new false claims doing it, including
two inside the flagship rewrite -- swapping one inaccuracy for another
is this document's recurring failure mode.

- :1136-37 -- "75 ... 77 now, after #647 ... adds ... three more" was
  internally inconsistent (75+3=78, not 77). Traced #647's own diff
  (fc32d1b): it adds ``_missing_`` to three IntFlag classes across
  only two *new* files -- ``tcp/flags.py`` and ``ftp/command.py`` --
  since the third, ``TransportProtocol``, shares ``reg/apptype.py``
  with the already-counted ``AppType``. Module delta is +2, matching
  75+2=77; reworded to say so.
- :1879-88 -- dropped "the comparison below assumes they still lack"
  it, which was false about text 8 lines below in the same diff
  (already past-tensed). Also reflowed three orphan lines this
  introduced (`passes, whereas`, `it twice with nothing`, `none of
  the`).
- :2107-19 -- "These tables also ship pre-seeded" over-generalised:
  verified live (``ProtocolBase.__proto__`` is 0 entries,
  ``Transport.__proto__ is ProtocolBase.__proto__`` -- True) that 2 of
  7 have nothing pre-seeded. Scoped to the five that do (Link 7,
  Internet 16, Frame 3, PCAPNG 3, SCTP 2). Also fixed "the guard
  resolves only the incoming class", which contradicts the guard's own
  docstring ("the comparison itself resolves nothing") -- resolution is
  the earlier ``isinstance(protocol, ModuleDescriptor)`` step, three
  lines above the guard, not something the guard does.
- :2129-30 -- dropped the invented "327th" ordinal (327 total stays;
  traced-write instrumentation via ``sys`` hooks found the seeding is
  literal dict construction, not ``.register()`` calls, so I could not
  reproduce an ordinal with confidence -- said "one of them" instead
  of guessing).
- :7-8 -- "between #326 and #509" now says the programme continued
  past it (verified: 193 distinct #nnn refs, max #726, 103 above 509).
- PR body -- "7 hunks, 1168+/11-" was the previous head's figure, not
  this one's; replaced with the actual command
  (``git diff --shortstat da697fa -- docs/source/changelog/1.5.0.rst``)
  and today's figure (9 hunks, 1152+/16-), since a hardcoded count here
  has now gone stale twice.

Left alone per this round's scope: :1969/:1974 (before/after claim,
not falsified by #726's later +1), mypy "112" (correct, re-ran with
the project's own flags), ":2122" 13-to-14 (correct at its delta
scope), and the other 121 cited paths (unaffected by main's one new
commit, #745, confirmed test-only).

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.
JarryShaw added a commit that referenced this pull request Sep 24, 2026
…arning count

Round 8: ran the merge-base/changed-files/cited-path intersection to
completion (0c7f2b7..origin/main: 43 commits/124 files; 54 cited paths,
34 of them touched by that range) instead of trusting a tense grep.

- :1540/:1542 -- "93 of the 95 sites"/"48 of the 49 record lengths" ->
  94 of 95 / 49 of 49; zero old-expression sites remain on main.
- :1544-1546, :1566 -- "LOCATOR_SET keeps the old expression ... is
  currently right" / "is unchanged in both respects" -> past tense;
  #679 fixed both LOCATOR_SET sites.
- :1566 -- "HIP_COPIES stays at two" -> past tense; #679 fixed
  LOCATOR_SET's Length unit, #689 then dropped HIP_COPIES to one.
- :1976-1977 -- "pcapng.txt ... wants a separate refresh" -> past
  tense; #685 removed it from the index instead of regenerating it.
- :2327-2328 -- "55 warnings on main before this change, 56 after"
  (-b html) -> 53/54; the raw `grep -c WARNING:` double-counts two
  Scapy import lines as Sphinx warnings, confirmed live on this head
  with both -b dummy and -b html (real 36/raw 38 with const/reg.rst
  excluded, same +2 gap either way).
- :834 -- stale ``protocol.py:1016`` -> ``:1413`` (the actual
  ``self._file.read()`` call inside ``_read_fileng``).
- :1969 -- the #646 entry's coverage renumbering was wrong twice over
  (first ``1153 to 1265``, then ``1153 to 1443``, the latter being
  main's ``def`` line, which always executes and can never be the
  single miss). Corrected to ``1248 to 1360``, the ``warn(...)``
  statement's line before/after #646's own diff.

Regenerated CHANGELOG.md from the edited entries.

changelog_md.py --check: exit 0. pytest tests/project/test_changelog_md.py -q:
47 passed, 37 subtests.

Follow-up: origin/main advanced through #726/#740/#741/#742 (to 0a3abff)
and then #747/#748 (to 074c53e) while this sat at good-to-go; #726 moved
three more claims anchored on files it touched.

- :834 -- ``protocol.py:1413`` -> ``:1411``; #726 shifted the
  ``self._file.read()`` call in ``_read_fileng`` by -2 lines.
- :2382-83 -- traceflow.py "Line 406" -> "Line 424"; #742 inserted 18
  lines above the ``#: Type[Dumper]: Dumper class.`` comment.
- :2099-2104, :2187-89 -- the "seven code-keyed parser registrars" and
  ``Option.register`` are no longer presence-only. #726, fixing #718,
  gave all seven -- and ``Option.register`` itself -- the same identity
  guard ``register_protocol`` already had; reworded both passages to
  say so, confirmed against the guards' own current docstrings.

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.

Cross-review at 948ac49 came back NEEDS CHANGES: the round-12 edit fixed two
sites of the harmonisation claim and left its twin, plus its own reasoning,
asserting the opposite; and four numbers anchored on files the merges touched
had drifted independently of #726.

- :1877-1878, :1883-1884 (#675) -- "carries the guarded ``if code in
  cls.__xxx__: warn(...)``" / "every sibling warns on mere presence" ->
  past tense, noting #726 later gave all seven the identity guard this
  entry's own comparison assumes they lack.
- :2100-2109 -- dropped the retained "yields two keys and never reaches
  one key twice" (false: ``Internet.register(TransType.TCP, TCP)`` warns
  once, incumbent.klass is TCP) and "leaves a different-class test
  undecidable" (contradicted by :2404-2406's own ``incumbent is not
  protocol`` definition); replaced with the actual false positive the
  guard has -- pre-seeded ``ModuleDescriptor`` incumbents never compare
  equal to the resolved class.
- :2192 -- reflowed the ``Option.register`` paragraph (orphan lines
  fixed alongside).
- :2387-2388 -- the ``Type[Dumper]`` quote now matches what is actually
  at line 424 (post-#709-fix), rather than the pre-fix bare form.
- :945 -- ``README.md`` (103) -> (102).
- :1136 -- "75 of the 117 modules" -> "77 ... after #647 below adds
  the same ending to three more" (drifted via #647, independent of the
  four merges).
- :2119 -- dropped the irreproducible pylint "364 messages" figure;
  kept mypy's 112, which does reproduce.
- :2119 -- "326 registry writes" -> 327 (``R1CounterParameter``'s
  second code, from #690).

Also fixed six false claims in the PR body (separate from the .rst):
hunk/line counts, six-commits -> 46, the 5-row table's implied total,
"not trimmed", main's red/green state, and the now-unreachable
cherry-pick target.

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.

Cross-review at 1749cc0 came back NEEDS CHANGES: round 13 fixed five of
the nine sites and introduced four new false claims doing it, including
two inside the flagship rewrite -- swapping one inaccuracy for another
is this document's recurring failure mode.

- :1136-37 -- "75 ... 77 now, after #647 ... adds ... three more" was
  internally inconsistent (75+3=78, not 77). Traced #647's own diff
  (fc32d1b): it adds ``_missing_`` to three IntFlag classes across
  only two *new* files -- ``tcp/flags.py`` and ``ftp/command.py`` --
  since the third, ``TransportProtocol``, shares ``reg/apptype.py``
  with the already-counted ``AppType``. Module delta is +2, matching
  75+2=77; reworded to say so.
- :1879-88 -- dropped "the comparison below assumes they still lack"
  it, which was false about text 8 lines below in the same diff
  (already past-tensed). Also reflowed three orphan lines this
  introduced (`passes, whereas`, `it twice with nothing`, `none of
  the`).
- :2107-19 -- "These tables also ship pre-seeded" over-generalised:
  verified live (``ProtocolBase.__proto__`` is 0 entries,
  ``Transport.__proto__ is ProtocolBase.__proto__`` -- True) that 2 of
  7 have nothing pre-seeded. Scoped to the five that do (Link 7,
  Internet 16, Frame 3, PCAPNG 3, SCTP 2). Also fixed "the guard
  resolves only the incoming class", which contradicts the guard's own
  docstring ("the comparison itself resolves nothing") -- resolution is
  the earlier ``isinstance(protocol, ModuleDescriptor)`` step, three
  lines above the guard, not something the guard does.
- :2129-30 -- dropped the invented "327th" ordinal (327 total stays;
  traced-write instrumentation via ``sys`` hooks found the seeding is
  literal dict construction, not ``.register()`` calls, so I could not
  reproduce an ordinal with confidence -- said "one of them" instead
  of guessing).
- :7-8 -- "between #326 and #509" now says the programme continued
  past it (verified: 193 distinct #nnn refs, max #726, 103 above 509).
- PR body -- "7 hunks, 1168+/11-" was the previous head's figure, not
  this one's; replaced with the actual command
  (``git diff --shortstat da697fa -- docs/source/changelog/1.5.0.rst``)
  and today's figure (9 hunks, 1152+/16-), since a hardcoded count here
  has now gone stale twice.

Left alone per this round's scope: :1969/:1974 (before/after claim,
not falsified by #726's later +1), mypy "112" (correct, re-ran with
the project's own flags), ":2122" 13-to-14 (correct at its delta
scope), and the other 121 cited paths (unaffected by main's one new
commit, #745, confirmed test-only).

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.

Cross-review at b2ac58b came back NEEDS CHANGES: round 15 fixed four
sites clean but swapped in two new inaccuracies, and left one
round-fourteen defect (body :26) unfixed.

- :7 -- "past #726" was wrong direction: #726 is the max ref in the
  document (193 distinct, min #251, max #726), not one exceeded ->
  "reaching #726".
- :2110-19 -- "ProtocolBase and Transport share one dict, starting and
  staying empty until a subclass registers" was false three ways,
  verified live against origin/main (pcapkit.__file__ asserted):
  Transport.register() itself raises UnsupportedCall (abstract); TCP
  and UDP keep their own separate __proto__ (4 and 3 entries), not the
  shared one, so registering on them leaves the shared dict at 0; only
  a direct ProtocolBase.register() call fills it. Narrowing to "five
  of these seven" also hid that TCP/UDP are pre-seeded too, which is
  exactly where the false positive bites in the transport family --
  restored that.
- body :26 -- "26 entry commits" -> 27 (commits whose subject starts
  "docs(changelog): the 1.5.0 entry/entries for", verified by grep),
  28 bullets added and 0 removed (verified via the .rst diff against
  da697fa; one commit, 6a956c4, adds two bullets for #648/#649).
- body :41 -- dropped the hardcoded "9 hunks, 1152+/16-" figure
  entirely (it had already drifted to 1154+ by the time of this
  commit) and named the second command needed for the hunk count,
  since --shortstat cannot print one.

On the ordinal question raised last round: dropping it was still right
(the asserted "327th" was wrong), but "no ordinal is derivable" does
not hold either -- the writes are at pcapkit/protocols/schema/schema.py,
not the 8 dict-literal registrar sites my instrumentation covered, and
they are traceable. Left the text as "one of them being
R1CounterParameter's second code" (no ordinal asserted, no false
derivability claim either) rather than reopen a site outside this
round's scope.

Regenerated CHANGELOG.md again. changelog_md.py --check: exit 0. pytest
tests/project/test_changelog_md.py -q: 47 passed, 37 subtests.
JarryShaw added a commit that referenced this pull request Sep 24, 2026
…st 54 sites (#752)

Completes #514 part (c). #750 renamed 28 of 82 `ProtocolBase as Protocol`
alias imports and deferred the remaining 54 -- all `ProtocolBase` -- to paths
#726 and #742 owned. Both have merged, so the deferral is over.

* renamed the alias import at all 54 sites (51 under pcapkit/protocols/, 3
  under pcapkit/foundation/) and every in-file reference that used the local
  alias: class headers, annotations, cast(), isinstance/issubclass checks,
  # type: comments, and the bracketed part of a handful of Sphinx #: doc
  comments -- 191 lines changed, no statements added
* merged two now-unaliased same-module imports per isort in 4 files
  (application.py, internet.py, link.py, transport.py), removing 4 statements
* left descriptive prose, protocol-name string literals, error-message text,
  and fully-qualified :class:/:meth:/:rtype: cross-references to the real
  public Protocol class untouched, matching #750's own precedent
* emptied tests/test_base_class_contract.py's PENDING_ALIAS_PATHS, its
  documented end state, and updated the stale docstring narrative

No behaviour change: __mro__/__bases__/__module__ identical across 18 classes
spanning every family, __proto__ registry 38 keys before and after,
descendants(Protocol) 0 in both. mypy stays at the 112-error baseline.
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Pull requests that fix a defect (fix: subject prefix)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RegistryWarning: three Extractor registrars still warn on a same-object re-registration

1 participant