Repository navigation
fix(foundation): raise RegistryError, not a leaked TypeError, for a non-class - #1025
Conversation
|
Cross-review verdict: NEEDS CHANGES (ran on Opus; authored on Sonnet). It found a real gap and three errors in my own prose. All four reproduced by me before acting. The gap, and it is a public-API asymmetry. All three are exported from Three corrections to my own text:
One scope correction that matters more than the rest. Everything else held: the two-mechanism split, the non-breaking claim with no affected |
9f8b774 to
3427a28
Compare
3427a28 to
1f24b9a
Compare
|
Cross-review verdict: GOOD TO GO at Both round-2 findings are fixed, and the reviewer established something that makes the carry-forward sound rather than assumed: the round-2 → round-3 diff is prose-only. I verified it — It also re-derived the scope claim two ways rather than taking it on trust. One defect it found outside this PR, now fixed: #1026's body still listed six sites and still asserted the CI on this head: 64 success, 3 skipped, remainder in flight, nothing failed. |
|
resolve conflicts |
…on-class Six registrar guards promised `RegistryError` for a non-class argument and leaked `abc`'s `TypeError` instead. Closes #1021. * Each guard was a bare `if not issubclass(x, Base):`, so a non-class was rejected before the `raise` was reached. Measured, with the raise site read off the traceback rather than the message: Extractor.register_dumper -> TypeError | extraction.py:406 Extractor.register_engine -> TypeError | <frozen abc>:123 Extractor.register_reassembly -> TypeError | <frozen abc>:123 Extractor.register_traceflow -> TypeError | <frozen abc>:123 TraceFlow.register_dumper -> TypeError | traceflow.py:237 register_protocol -> TypeError | <frozen abc>:123 Two mechanisms: `Dumper`'s metaclass is plain `type`, so the builtin `issubclass` raises in place; the other targets carry `ABCMeta`-derived metaclasses, so it delegates and `abc` raises. Identical message either way, which is what hid this. * `TraceFlow.register_dumper` matters beyond symmetry. It backs the exported `register_traceflow_dumper`, whose sibling `register_extractor_dumper` has an identical signature and docstring thirty lines away -- so fixing only the `Extractor` side left two indistinguishable public functions raising different exceptions. * The check sits *after* the `ModuleDescriptor` unwrap in the five registrars that accept one, because that branch unwraps rather than short-circuits. `register_protocol` accepts no descriptor. Not breaking: `RegistryError` subclasses `TypeError` via `BaseError`, so `except TypeError` still catches it. Only a caller matching the exact type, or the old message text, sees a difference. No guard's target class changes -- that is #1016's subject -- and a wrong class still raises `RegistryError` as before. No docstring changed: the four `Raises:` clauses already read "is not a class, or not a `X` subclass" and are simply true now. Six of thirteen `issubclass` guards in `pcapkit/`. The `register` classmethods on `ProtocolBase`, `Frame`, `PCAPNG`, `SCTP`, `Link`, `Internet` and `Transport` all still leak. `Transport.register` is **not** exempt as an earlier draft of this message claimed: its `UnsupportedCall` is gated on `cls is Transport`, so it fires only for the abstract class, and the guard below leaks through `TCP.register` and `UDP.register` -- the only way it is ever reached. #1026 tracks all seven. New tests cover all six registrars across non-class inputs including a descriptor resolving to a non-class, plus the unchanged wrong-class path. On main they produce 28 subfailures, every one the leaked `TypeError`. tests/foundation + tests/project: 536 passed, 13 skipped, 1319 subtests passed.
1f24b9a to
39a4c4d
Compare
|
Conflicts resolved and rebased onto The conflict was semantic rather than textual, so the resolution is worth stating: #1020 merged in the meantime and deliberately narrowed these three guards from the So in each of the three registrars I kept
The Two corrections made while resolving:
Verified on Resetting |
|
Cross-review verdict on It went after the resolution specifically — a semantic conflict resolved by hand is exactly where someone else's merged work gets silently reverted — and could not break it. What it derived independently rather than taking from me:
It also found no seam in the changelog bullet I rewrote and rewrapped in three passes, which was the thing I most expected to be wrong. One correction to my own briefing, not to this change: I have been telling agents the venv is Python 3.14.7. It is 3.14.8 ( Its one UNVERIFIED gap — the rest of |
`process.rst:104` pinned 159 entries while the file holds 161, so `tests/project` was failing on `main` again. * #1025, #1028 and #1030 each measured 159 against a 158-entry base, which was correct for each branch in isolation. Merging all three added three entries and left the pin two behind. * This is the second time the same collision has landed, after `51100da7e` fixed a two-way version of it. Measuring per branch is not sufficient -- the pin is only correct at the moment it is measured against the tree it will merge into, so it needs re-measuring at merge time or the check will keep going red whenever two changelog-touching branches land together. Measured rather than incremented: `grep -cE '^\* ' docs/source/changelog/1.5.0.rst` gives 161. 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
Closes #1021, implementing the maintainer's choice of "code follows prose".
Six registrar guards promised
RegistryErrorfor a non-class and leakedabc'sTypeErrorinstead. Each was a bareif not issubclass(x, Base):, so a non-class was rejected before theraisewas reached. Measured with the raise site read off the traceback rather than the message, because the message is identical in both cases:Two mechanisms.
Dumper's metaclass is plaintype, so the builtinissubclassraises in place at the guard line; the other targets carryABCMeta-derived metaclasses, so the builtin delegates and the raise happens insideabc.TraceFlow.register_dumpermatters beyond symmetry, and was missed in the first revision. It backs the exportedregister_traceflow_dumper, whose siblingregister_extractor_dumperhas an identical signature and docstring thirty lines away in the same file — so fixing only theExtractorside left two indistinguishable public functions raising different exceptions.The check goes after the
ModuleDescriptorunwrap in the five registrars that accept one, because that branch unwraps rather than short-circuits, so a descriptor naming a non-class attribute reaches it.register_protocolaccepts no descriptor and has no unwrap.Not breaking.
RegistryErrorsubclassesTypeErrorviaBaseError:So
except TypeErrorkeeps working, and only a caller matching the exact type or the old message text sees a difference. No guard's target class changes — that is #1016's subject and PR #1020's work. A wrong class still raisesRegistryError, and the tests pin that it is unchanged.No docstring changed. The four
Raises:clauses already read "is not a class, or not aXsubclass"; with code following prose they are simply true.Scope, stated honestly: six of thirteen bare
issubclassguards inpcapkit/. Theregisterclassmethods onProtocolBase,Frame,PCAPNG,SCTP,Link,InternetandTransportall still leak, and #1026 tracks them. An earlier draft calledTransport.registerexempt — that was wrong. ItsUnsupportedCallis gated oncls is Transport, so it fires only for the abstract class; the guard below it is reachable and leaks throughTCP.registerandUDP.register, which is the only way it is ever called.New tests cover all six registrars across non-class inputs — instance, string,
None, and a descriptor resolving to a non-class — plus the wrong-class case, asserting the message so a strayTypeErrorcannot pass silently. Onmainthey produce 28 subfailures, every one the leakedTypeError.tests/foundation+tests/project: 536 passed, 13 skipped, 1319 subtests passed.util/changelog_md.py --checkexits 0, and theprocess.rstentry-count pin is 157, measured rather than incremented.