fix(foundation): name the base class the registrar guard actually checks - #1020
Conversation
|
Unblocked — the maintainer ruled on #1016 and chose the option that resolves the #513 conflict rather than trading one defect for another. The narrowing goes ahead, and the built-ins get an internal registration path that bypasses the public guard. That is what makes #513's regression test survivable: its point was that pcapkit's own classes must be registrable, and they still are, through the internal path. The test is retargeted at that path rather than deleted or loosened. So this PR's rework changes shape once more. The guards narrow to |
|
The ruled design is built and verified, with one ordering constraint: #1023 should merge first. What is in the rework, off #513's regression test is retargeted, not weakened. It keeps its real subject classes, its unmocked gate and its registry read-back, and now calls the internal path, with a comment citing #1016 so a reader arriving from #513 sees why the assertion moved. Two new tests pin the other half: the public door rejects a base-only class both as a class and as a One consequence worth stating plainly. The internal path writes base-only classes into stores declared Measured on a scratch tree with #1023 cherry-picked onto Also corrected while in there: the |
8d5133c to
23506be
Compare
Implements the maintainer's ruling on #1016: the public `register_*` door names the public class, and the base check moves to an internal path, so neither that ruling nor #513 has to concede. * `register_engine`, `register_reassembly` and `register_traceflow` now guard `issubclass(x, Engine / Reassembly / TraceFlow)` instead of the `*Base` classes. Their messages are unchanged -- `main` already named the public class, which is what made the mismatch #1016 reported. * Three private classmethods take the base check: `Extractor._register_internal_engine` and its two siblings. They unwrap a `ModuleDescriptor`, check the base, keep the overwrite warning, and write the registry. None is in any `__all__`, none is autodocumented, and no `register_extractor_*` wrapper reaches them. * The three registry stores and the three metaclass `registry` properties widen from `Type[Engine]` to `Type[EngineBase]` and friends. The internal path writes base-only classes by design, so the narrow declaration was false by construction rather than by accident; `dict` is invariant in its value type, so the stores could not widen without the properties that return them. * The `#:` prose above each store said the values were "a tuple representing the module name and class name" and an `Engine` subclass. They are `ModuleDescriptor`s and base-only classes -- wrong on both counts before this change, not because of it. The built-ins are not affected by the narrowing and do not use the internal path at runtime: they are `ModuleDescriptor` literals in `Extractor`'s class body and never pass through a registrar. The internal path exists for programmatic internal registration, and is what #513's regression test now exercises. `register_protocol` and `register_dumper` are untouched. The former cannot narrow: `ProtocolBase` has 44 descendants including `Protocol` itself, while the public `Protocol` has none, so narrowing would reject every in-house protocol class. The latter already guards the public `Dumper`. Breaking: a third party subclassing `EngineBase` directly and calling `register_engine` is now rejected where it was accepted. #513's regression test is retargeted, not weakened. It keeps its real subject classes, its unmocked gate, its registry read-back and its anti-no-op comment, and now exercises the internal path -- with a comment citing #1016 so a reader arriving from #513 sees why the assertion moved, plus `mock.patch.dict` rollback it did not have before. What it guaranteed still holds: pcapkit's own base-only classes are registrable through a real gate. Two new tests pin the other half, that the public door rejects a base-only class both as a class and as a `ModuleDescriptor`. mypy, single-file entry point `mypy pcapkit/foundation/extraction.py`: 305 errors, 0 attributed to the file, sorted error set identical to main. The whole-package `mypy pcapkit` form reads 321 on both trees. The store widening draws a `_exeng` mismatch on its own, which #1023 resolved. tests/foundation + tests/interface + tests/project: 557 passed, 13 skipped, 1300 subtests passed.
23506be to
dae2505
Compare
|
Cross-review verdict: NEEDS CHANGES on prose, code good to go (Opus; author on Sonnet). Three findings, two real and one not — all three re-derived by me. A. The changelog asserted a mechanism that never runs, and this is the real one. It said the built-ins "are registered through a new internal path". They are not. B. "Three casts are deleted" — none were. C. Not a defect — an invocation difference. The reviewer measures 321 errors where the message says 305. Both are right: the message says "mypy on extraction.py", the single-file entry point, and 321 is the whole-package Head is now
One honest gap it flagged: the #513 test no longer exercises |
|
Verdict carried to Granting on that basis rather than paying a fourth review, because the amendment is prose-only and I measured it: Both substantive findings are fixed:
The third was not a defect: the reviewer measured 321 mypy errors against the message's 305. Both are right for their invocation — 305 is Re-ran after the amendment: Awaiting CI's last legs, then yours to merge. One thing worth carrying into review: the reviewer flagged that #513's test no longer exercises |
|
Withdrawing the good-to-go: this branch roughly doubles the I reported the first red
Every other run of that job sits at 18–27m. Only this branch reaches the The slowdown is not uniform, which is what makes it look like a real cause rather than a slow runner. Timing the same two log landmarks from job start:
So the segment between those two landmarks takes 3.0m on I have not identified the mechanism yet and am not asserting one. Investigating now; |
|
Correcting my own withdrawal: the slowdown is not attributable to this diff, and I should not have pointed at it. A profiling pass on a different model came back MECHANISM NOT FOUND, and I verified its load-bearing claims myself rather than taking the report. What is refuted — that this code slows
The cumulative profile tops match line for line; the only differences are Why there is no mechanism available, which I checked directly. The three added methods are called from exactly one place each — the tail of their own public Corroboration from the same CI run I drew the original claim from: every sibling What my earlier evidence was worth. The CI durations are real — 44.9 m and 42.2 m here against 18-27 m elsewhere — and so is the widening landmark gap. What was wrong was the attribution. One detail undercuts the picture further: the contaminated local driver run that preceded this showed Still unexplained, and the reason I am not simply declaring this closed: two attempts on this branch were both slow. I have launched a third run of that leg as the direct test. If it lands in the 18-27 min band the cause was runner-side; if it comes back at ~42 min again, something real survives local profiling and I will say so. Restoring One measurement gap named rather than buried: the full |
`process.rst:104` pinned 157 entries while the file holds 158, so `tests/project` was failing on `main`. * #1020 and #1027 each measured 157 against a 156-entry base, which was correct for each branch alone. Merging both added two entries and left the pin behind -- the parallel-branch collision the page's own "measure, never increment" rule exists to prevent, landing for the first time. Measured rather than incremented: `grep -cE '^\* ' docs/source/changelog/1.5.0.rst` gives 158. tests/project: 268 passed, 1 skipped, 864 subtests passed. The failing test was test_the_page_pins_its_own_measured_numbers.
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-visibleWhat is the purpose of your pull request?
fix— corrects a defectfeat— adds a featureperf— changes performance, not behaviourrefactor— changes neither behaviour nor performancetest— tests onlydocs— documentation onlyci— workflows or build toolingchore— anything elseDescription of your pull request and other information
Implements the ruling on #1016. This description replaces two superseded ones — the original widened the guards to the
*Baseclasses, the second narrowed them without an escape hatch, and both were wrong for reasons recorded in the thread.What the ruling settles. The public door names the public class, and pcapkit's own base-only built-ins get an internal path — so neither that ruling nor #513 concedes. Without the second half, narrowing alone fails #513's own regression test, which deliberately pushes the real built-ins through
register_extractor_*unmocked.The change:
register_engine,register_reassemblyandregister_traceflowguardissubclass(x, Engine / Reassembly / TraceFlow)instead of the*Baseclasses. Their messages are unchanged —mainalready named the public class, which is exactly the mismatch fix(foundation): registrar error messages and type hints name the public class, not the Base the guard checks #1016 reported, and an earlier revision of this PR wrongly "fixed" them to name the base.Extractor._register_internal_engineand siblings. They unwrap aModuleDescriptor, check the base, keep the overwrite warning, and write the registry. None is in any__all__and noregister_extractor_*wrapper reaches them.registryproperties widen fromType[Engine]toType[EngineBase], and three casts are deleted. The internal path writes base-only classes by design, so the narrow declaration became false by construction rather than by accident.dictis invariant in its value type, so the stores could not widen without the properties returning them —engines/engine.py:56,reassembly/reassembly.py:84,traceflow/traceflow.py:85.#:prose above each store claimed the values were "a tuple representing the module name and class name" and anEnginesubclass. They areModuleDescriptors and base-only classes — wrong on both counts before this change.Untouched, deliberately.
register_protocolcannot narrow:ProtocolBasehas 43 descendants and the publicProtocolhas 0, so narrowing would reject every in-house protocol class. That asymmetry is #514's subject.register_dumperalready guards the publicDumper— the pattern this PR restores to its three siblings.Breaking: a third party subclassing
EngineBasedirectly and callingregister_engineis now rejected where it was accepted.#513's regression test is retargeted, not weakened. It keeps its real subject classes, its unmocked gate and its registry read-back, and now exercises the internal path, with a comment citing #1016 so a reader arriving from #513 sees why the assertion moved. What it guaranteed still holds: pcapkit's own base-only classes are registrable. Two new tests pin the other half — the public door rejects a base-only class both as a class and as a
ModuleDescriptor— failing 6 of 6 subtests onmain.On the dependency that is now resolved. The store widening draws one mypy error on its own, on
self._exeng = eng(self), because_exengwas declaredEngine[_P]. That was #1023's declaration, and I deliberately did not silence it with a cast or an ignore in a file another PR owned. #1023 has merged, and rebased onto it this branch measures 305 errors with 0 attributed toextraction.py— identical tomain.tests/foundation+tests/interface+tests/project: 557 passed, 13 skipped, 1300 subtests passed.util/changelog_md.py --checkexits 0, and theprocess.rstentry-count pin is set from measurement.