Repository navigation
fix(registry): report the protocol-name collision register_protocol hid (#675) - #681
Conversation
`register_protocol` keyed `pcapkit.protocols.__proto__` on `cls.__name__.upper()` with a bare assignment, and three dispatchable protocol classes are all named `HTTP`, so registering one silently displaced whichever was there. - Warn with `RegistryWarning` when the key is held by a *different* class, naming both the displaced and the replacing class -- the module is the only thing that distinguishes the three `HTTP` classes, so a message that omitted it would not say which one was lost. - Re-registering the same class stays silent. Every wrapper registrar funnels into this function, so one class under two codes reaches it twice with nothing displaced; warning there would be noise on a supported path, and the filter it invites is what would hide the real collision. - Document the collision, why this guard differs from the presence-only siblings, and why re-keying the registry belongs to #514. Tests register two different classes named `HTTP` and assert the warning in both directions, cover the `Protocol.__init_subclass__` path that reaches the collision with no registry call in user code, pin the quiet cases, and assert the code-keyed sibling registries still warn on an identical re-registration. Coverage on the touched module holds at 88% with misses flat at 27. Fixes #675
`register_protocol` keyed the protocol registry on `cls.__name__.upper()` and three dispatchable classes are named `HTTP`, so registering one silently displaced another. The entry records the measurement, the correction to the issue's `RegistryWarning` claim, why the guard departs from the presence-only siblings, and why re-keying belongs to #514. Regenerated CHANGELOG.md with util/changelog_md.py; --check exits 0.
|
Filed #682 for the residue this PR deliberately leaves: the key space is still not unique, so the three Two corrections to my own evidence above, for the record:
|
|
GOOD TO GO Independent cross-review on a different model (Claude Sonnet) from the one that wrote the All eight load-bearing claims came back CONFIRMED, each re-derived independently rather than
It also confirmed the What it disputed, and my responseOne fair criticism, which I am recording rather than folding away: my "nothing else perturbed" All five failures are the same environmental cause already disclosed for What the review could not verifyStated plainly rather than implied: it did not execute the pre-fix "before" run (it argued that No disagreement was raised that changes the merge decision. Still unpublished and awaiting your |
… code-keyed registrars displaced (#692) Generalises #681, per the ask to "apply what #681 added to other registry as well". - `EnumSchema.register` assigned bare, so the schema half of 14 public registrars silently displaced a built-in while the parser half of the very same call warned. Guard it on presence, naming both schemas. - `EnumSchema.__init_subclass__` reaches the same registry without calling `register`, so `class MyOption(Option, code=...)` stayed silent too. Guard it as well, folding its two branches into one loop so the guard is written once. - The seven code-keyed registrars now name the displaced entry and its replacement. Their presence-only condition is deliberately unchanged: their key is caller-supplied and independent of the value, so #681's "present and a different class" has nothing to fix here, and the `ModuleDescriptor` incumbents these tables ship with would make it undecidable without resolving the descriptor -- forcing the import it exists to defer, just to decide whether to warn. - `ContextRegistry.register` already raises on a duplicate, and the reassembly and ESP registrars are unkeyed lists, so none of those three takes a guard. `import pcapkit` holds at 1 warning and 0 RegistryWarning; mypy 112 errors and pylint 364 messages both unchanged; schema.py coverage 99% with its 5 new statements covered and misses flat at 1. Fixes #692
… code-keyed registrars displaced (#692) Generalises #681, per the ask to "apply what #681 added to other registry as well". - `EnumSchema.register` assigned bare, so the schema half of 14 public registrars silently displaced a built-in while the parser half of the very same call warned. Guard it on presence, naming both schemas. - `EnumSchema.__init_subclass__` reaches the same registry without calling `register`, so `class MyOption(Option, code=...)` stayed silent too. Guard it as well, folding its two branches into one loop so the guard is written once. - The seven code-keyed registrars now name the displaced entry and its replacement. Their presence-only condition is deliberately unchanged: their key is caller-supplied and independent of the value, so #681's "present and a different class" has nothing to fix here, and the `ModuleDescriptor` incumbents these tables ship with would make it undecidable without resolving the descriptor -- forcing the import it exists to defer, just to decide whether to warn. - `ContextRegistry.register` already raises on a duplicate, and the reassembly and ESP registrars are unkeyed lists, so none of those three takes a guard. `import pcapkit` holds at 1 warning and 0 RegistryWarning; mypy 112 errors and pylint 364 messages both unchanged; schema.py coverage 99% with its 5 new statements covered and misses flat at 1. Fixes #692
…ders the same - register_protocol's overwrite warning showed both operands via bare repr(), which is only <class 'module.qualname'>. A factory that builds a fresh closure-local class of the same name on every call (the shape tests/protocols/test_construction_keyword_check_unit.py's _protocol_class hits) gives two distinct objects with an identical repr(), so the warning read as an overwrite of a class with itself. - The guard's identity check (incumbent is not protocol, from #681) is unchanged and still correct; only the message was unactionable. Now, only when the two repr()s coincide, each operand gets an id() suffix so a reader can tell which object won -- module+qualname would not help, since that is exactly what the coinciding repr() already carries. The common case of two differently-named classes is untouched and stays free of the extra noise. - Added tests/foundation/registry/test_protocols.py:: test_register_protocol_disambiguates_classes_sharing_a_repr, and confirmed it fails against the unfixed guard with the exact 'overwriting X with X' text from #710. Fixes #710. Build: targeted pytest run (test_protocols.py, test_construction_keyword_check_unit.py, test_protocol_code_registration_unit.py) green, 50 passed.
… code-keyed registrars displaced (#692) Generalises #681, per the ask to "apply what #681 added to other registry as well". - `EnumSchema.register` assigned bare, so the schema half of 14 public registrars silently displaced a built-in while the parser half of the very same call warned. Guard it on presence, naming both schemas. - `EnumSchema.__init_subclass__` reaches the same registry without calling `register`, so `class MyOption(Option, code=...)` stayed silent too. Guard it as well, folding its two branches into one loop so the guard is written once. - The seven code-keyed registrars now name the displaced entry and its replacement. Their presence-only condition is deliberately unchanged: their key is caller-supplied and independent of the value, so #681's "present and a different class" has nothing to fix here, and the `ModuleDescriptor` incumbents these tables ship with would make it undecidable without resolving the descriptor -- forcing the import it exists to defer, just to decide whether to warn. - `ContextRegistry.register` already raises on a duplicate, and the reassembly and ESP registrars are unkeyed lists, so none of those three takes a guard. `import pcapkit` holds at 1 warning and 0 RegistryWarning; mypy 112 errors and pylint 364 messages both unchanged; schema.py coverage 99% with its 5 new statements covered and misses flat at 1. Fixes #692
…ders the same - register_protocol's overwrite warning showed both operands via bare repr(), which is only <class 'module.qualname'>. A factory that builds a fresh closure-local class of the same name on every call (the shape tests/protocols/test_construction_keyword_check_unit.py's _protocol_class hits) gives two distinct objects with an identical repr(), so the warning read as an overwrite of a class with itself. - The guard's identity check (incumbent is not protocol, from #681) is unchanged and still correct; only the message was unactionable. Now, only when the two repr()s coincide, each operand gets an id() suffix so a reader can tell which object won -- module+qualname would not help, since that is exactly what the coinciding repr() already carries. The common case of two differently-named classes is untouched and stays free of the extra noise. - Added tests/foundation/registry/test_protocols.py:: test_register_protocol_disambiguates_classes_sharing_a_repr, and confirmed it fails against the unfixed guard with the exact 'overwriting X with X' text from #710. Fixes #710. Build: targeted pytest run (test_protocols.py, test_construction_keyword_check_unit.py, test_protocol_code_registration_unit.py) green, 50 passed.
… code-keyed registrars displaced (#695) Generalises #681, per the ask to "apply what #681 added to other registry as well". - `EnumSchema.register` assigned bare, so the schema half of 14 public registrars silently displaced a built-in while the parser half of the very same call warned. Guard it on presence, naming both schemas. - `EnumSchema.__init_subclass__` reaches the same registry without calling `register`, so `class MyOption(Option, code=...)` stayed silent too. Guard it as well, folding its two branches into one loop so the guard is written once. - The seven code-keyed registrars now name the displaced entry and its replacement. Their presence-only condition is deliberately unchanged: their key is caller-supplied and independent of the value, so #681's "present and a different class" has nothing to fix here, and the `ModuleDescriptor` incumbents these tables ship with would make it undecidable without resolving the descriptor -- forcing the import it exists to defer, just to decide whether to warn. - `ContextRegistry.register` already raises on a duplicate, and the reassembly and ESP registrars are unkeyed lists, so none of those three takes a guard. `import pcapkit` holds at 1 warning and 0 RegistryWarning; mypy 112 errors and pylint 364 messages both unchanged; schema.py coverage 99% with its 5 new statements covered and misses flat at 1. Fixes #692
…ders the same - register_protocol's overwrite warning showed both operands via bare repr(), which is only <class 'module.qualname'>. A factory that builds a fresh closure-local class of the same name on every call (the shape tests/protocols/test_construction_keyword_check_unit.py's _protocol_class hits) gives two distinct objects with an identical repr(), so the warning read as an overwrite of a class with itself. - The guard's identity check (incumbent is not protocol, from #681) is unchanged and still correct; only the message was unactionable. Now, only when the two repr()s coincide, each operand gets an id() suffix so a reader can tell which object won -- module+qualname would not help, since that is exactly what the coinciding repr() already carries. The common case of two differently-named classes is untouched and stays free of the extra noise. - Added tests/foundation/registry/test_protocols.py:: test_register_protocol_disambiguates_classes_sharing_a_repr, and confirmed it fails against the unfixed guard with the exact 'overwriting X with X' text from #710. Fixes #710. Build: targeted pytest run (test_protocols.py, test_construction_keyword_check_unit.py, test_protocol_code_registration_unit.py) green, 50 passed.
…n two classes share a repr (#711) - register_protocol's overwrite warning showed both operands via bare repr(), which is only <class 'module.qualname'>. A factory that builds a fresh closure-local class of the same name on every call (the shape tests/protocols/test_construction_keyword_check_unit.py's _protocol_class hits) gives two distinct objects with an identical repr(), so the warning read as an overwrite of a class with itself. - The guard's identity check (incumbent is not protocol, from #681) is unchanged and still correct; only the message was unactionable. Now, only when the two repr()s coincide, each operand gets an id() suffix so a reader can tell which object won -- module+qualname would not help, since that is exactly what the coinciding repr() already carries. The common case of two differently-named classes is untouched and stays free of the extra noise. - Added tests/foundation/registry/test_protocols.py:: test_register_protocol_disambiguates_classes_sharing_a_repr, and confirmed it fails against the unfixed guard with the exact 'overwriting X with X' text from #710. Fixes #710. Build: targeted pytest run (test_protocols.py, test_construction_keyword_check_unit.py, test_protocol_code_registration_unit.py) green, 50 passed.
… registrars - Nine code-keyed registrars (ProtocolBase, Internet, Link, Transport, SCTP, Frame, PCAPNG, and EnumSchema's register + __init_subclass__) warned on mere presence, so re-registering the exact same class under the same code emitted a misleading "overwriting X with X". Guard each on presence AND identity, matching register_protocol's guard from #681/#711. - Updated each site's docstring: the "fires on presence alone, deliberate" rationale (added by #695) no longer holds now the guard changed. - Repurposed test_sibling_registries_still_warn_on_an_identical_re_registration (tests/foundation/registry/test_protocols.py), which pinned the old behaviour by name, and fixed five other pre-existing tests that relied on it: test_register_analyze_and_next_layer_paths, the internet/link/frame/ pcapng "warns_on_overwrite" tests, and SCTP's, all of which re-registered a literal same object as their "overwrite" case. - Added one same-object no-op test per site (nine total). Did not apply #711's id() disambiguation to the siblings -- see PR body. Build: targeted pytest run, 249 passed, 1 skipped, 1930 subtests, exit 0. Fixes #718.
… registrars - Nine code-keyed registrars (ProtocolBase, Internet, Link, Transport, SCTP, Frame, PCAPNG, and EnumSchema's register + __init_subclass__) warned on mere presence, so re-registering the exact same class under the same code emitted a misleading "overwriting X with X". Guard each on presence AND identity, matching register_protocol's guard from #681/#711. - Updated each site's docstring: the "fires on presence alone, deliberate" rationale (added by #695) no longer holds now the guard changed. - Repurposed test_sibling_registries_still_warn_on_an_identical_re_registration (tests/foundation/registry/test_protocols.py) and fixed five other pre-existing tests that re-registered a literal same object as their "overwrite" case. - Added one same-object no-op test per site (nine total), plus a tenth covering EnumSchema.__init_subclass__'s guard through ordinary class-declaration syntax (repeated/aliased code=[...] member), not just a direct __init_subclass__() call. Corrected the comment, that test's docstring, and the PR table, which had all three asserted this path unreachable -- it isn't. Did not apply #711's id() disambiguation to the siblings -- see PR body. Build: targeted pytest run (9 files), 204 passed, 1930 subtests, exit 0. Fixes #718.
… registrars - Nine code-keyed registrars (ProtocolBase, Internet, Link, Transport, SCTP, Frame, PCAPNG, and EnumSchema's register + __init_subclass__) warned on mere presence, so re-registering the exact same class under the same code emitted a misleading "overwriting X with X". Guard each on presence AND identity, matching register_protocol's guard from #681/#711. - Updated each site's docstring: the "fires on presence alone, deliberate" rationale (added by #695) no longer holds now the guard changed. - Repurposed test_sibling_registries_still_warn_on_an_identical_re_registration (tests/foundation/registry/test_protocols.py) and fixed five other pre-existing tests that re-registered a literal same object as their "overwrite" case. - Added one same-object no-op test per site (nine total), plus a tenth covering EnumSchema.__init_subclass__'s guard through ordinary class-declaration syntax (repeated/aliased code=[...] member), not just a direct __init_subclass__() call. Corrected the comment, that test's docstring, and the PR table, which had all three asserted this path unreachable -- it isn't. Did not apply #711's id() disambiguation to the siblings -- see PR body. Build: targeted pytest run (9 files), 204 passed, 1930 subtests, exit 0. Fixes #718.
…strars - Ten code-keyed registrars (ProtocolBase, Internet, Link, Transport, SCTP, Frame, PCAPNG, EnumSchema's register + __init_subclass__, and pcapng.py's Option.register) warned on mere presence, so re-registering the exact same class under the same code emitted a misleading "overwriting X with X". Guard each on presence AND identity, matching register_protocol's guard from #681/#711. - Option.register needed its own fix: __init_subclass__ loops over a code list with no deduplication, so code=[b, b] reached it twice with the same class and warned about a self-overwrite. Rewrote its docstring, dropping the now-false admission that this could not happen. - Updated each site's docstring: the "fires on presence alone, deliberate" rationale (added by #695) no longer holds now the guard changed. - Repurposed test_sibling_registries_still_warn_on_an_identical_re_registration (tests/foundation/registry/test_protocols.py) and fixed five other pre-existing tests that re-registered a literal same object as their "overwrite" case. - Added one same-object no-op test per site (nine total), plus a tenth covering EnumSchema.__init_subclass__'s class-declaration path, and two more for Option.register's own code=[b, b] shape (silent on the same class, still warns once on a genuine displacement). Did not apply #711's id() disambiguation to the siblings -- see PR body. Build: targeted pytest run (9 files), 204 passed, 1930 subtests, exit 0. Option.register: test_pcapng_unit.py, 81 passed, 1753 subtests, pcapng.py at 100% line/branch coverage, exit 0. Fixes #718.
…strars - Ten code-keyed registrars (ProtocolBase, Internet, Link, Transport, SCTP, Frame, PCAPNG, EnumSchema's register + __init_subclass__, and pcapng.py's Option.register) warned on mere presence, so re-registering the exact same class under the same code emitted a misleading "overwriting X with X". Guard each on presence AND identity, matching register_protocol's guard from #681/#711. - Option.register needed its own fix: __init_subclass__ loops over a code list with no deduplication, so code=[b, b] reached it twice with the same class and warned about a self-overwrite. Rewrote its docstring, dropping the now-false admission that this could not happen. - Updated each site's docstring: the "fires on presence alone, deliberate" rationale (added by #695) no longer holds now the guard changed. - Repurposed test_sibling_registries_still_warn_on_an_identical_re_registration (tests/foundation/registry/test_protocols.py) and fixed five other pre-existing tests that re-registered a literal same object as their "overwrite" case. - Added one same-object no-op test per site (nine total), plus a tenth covering EnumSchema.__init_subclass__'s class-declaration path, and two more for Option.register's own code=[b, b] shape (silent on the same class, still warns once on a genuine displacement). - Cross-review found three prose sites that still argued the rejected reasoning: register_protocol's own docstring (foundation/registry/ protocols.py) claiming every sibling warns on mere presence, a test_pcapng_unit.py test docstring claiming __init_subclass__ passes each code exactly once, and a one-line summary in test_enum_schema_registry_unit.py calling the guard presence-only. Fixed all three; grepped every test file this PR touches for the same phrasing, no further instances. Did not apply #711's id() disambiguation to the siblings -- see PR body. Build: targeted pytest run (9 files), 204 passed, 1930 subtests, exit 0. Option.register: test_pcapng_unit.py, 81 passed, 1753 subtests, pcapng.py at 100% line/branch coverage, exit 0. Re-verified with the two other touched test files: 104 passed, 1838 subtests, exit 0. Fixes #718.
…strars - Ten code-keyed registrars (ProtocolBase, Internet, Link, Transport, SCTP, Frame, PCAPNG, EnumSchema's register + __init_subclass__, and pcapng.py's Option.register) warned on mere presence, so re-registering the exact same class under the same code emitted a misleading "overwriting X with X". Guard each on presence AND identity, matching register_protocol's guard from #681/#711. - Option.register needed its own fix: __init_subclass__ loops over a code list with no deduplication, so code=[b, b] reached it twice with the same class and warned about a self-overwrite. Rewrote its docstring, dropping the now-false admission that this could not happen. - Updated each site's docstring: the "fires on presence alone, deliberate" rationale (added by #695) no longer holds now the guard changed. - Repurposed test_sibling_registries_still_warn_on_an_identical_re_registration (tests/foundation/registry/test_protocols.py) and fixed five other pre-existing tests that re-registered a literal same object as their "overwrite" case. - Added one same-object no-op test per site (nine total), plus a tenth covering EnumSchema.__init_subclass__'s class-declaration path, and two more for Option.register's own code=[b, b] shape (silent on the same class, still warns once on a genuine displacement). - Cross-review found three prose sites that still argued the rejected reasoning: register_protocol's own docstring (foundation/registry/ protocols.py) claiming every sibling warns on mere presence, a test_pcapng_unit.py test docstring claiming __init_subclass__ passes each code exactly once, and a one-line summary in test_enum_schema_registry_unit.py calling the guard presence-only. Fixed all three; grepped every test file this PR touches for the same phrasing, no further instances. Did not apply #711's id() disambiguation to the siblings -- see PR body. Build: targeted pytest run (9 files), 204 passed, 1930 subtests, exit 0. Option.register: test_pcapng_unit.py, 81 passed, 1753 subtests, pcapng.py at 100% line/branch coverage, exit 0. Re-verified with the two other touched test files: 104 passed, 1838 subtests, exit 0. Fixes #718.
…strars (#726) - Ten code-keyed registrars (ProtocolBase, Internet, Link, Transport, SCTP, Frame, PCAPNG, EnumSchema's register + __init_subclass__, and pcapng.py's Option.register) warned on mere presence, so re-registering the exact same class under the same code emitted a misleading "overwriting X with X". Guard each on presence AND identity, matching register_protocol's guard from #681/#711. - Option.register needed its own fix: __init_subclass__ loops over a code list with no deduplication, so code=[b, b] reached it twice with the same class and warned about a self-overwrite. Rewrote its docstring, dropping the now-false admission that this could not happen. - Updated each site's docstring: the "fires on presence alone, deliberate" rationale (added by #695) no longer holds now the guard changed. - Repurposed test_sibling_registries_still_warn_on_an_identical_re_registration (tests/foundation/registry/test_protocols.py) and fixed five other pre-existing tests that re-registered a literal same object as their "overwrite" case. - Added one same-object no-op test per site (nine total), plus a tenth covering EnumSchema.__init_subclass__'s class-declaration path, and two more for Option.register's own code=[b, b] shape (silent on the same class, still warns once on a genuine displacement). - Cross-review found three prose sites that still argued the rejected reasoning: register_protocol's own docstring (foundation/registry/ protocols.py) claiming every sibling warns on mere presence, a test_pcapng_unit.py test docstring claiming __init_subclass__ passes each code exactly once, and a one-line summary in test_enum_schema_registry_unit.py calling the guard presence-only. Fixed all three; grepped every test file this PR touches for the same phrasing, no further instances. Did not apply #711's id() disambiguation to the siblings -- see PR body. Build: targeted pytest run (9 files), 204 passed, 1930 subtests, exit 0. Option.register: test_pcapng_unit.py, 81 passed, 1753 subtests, pcapng.py at 100% line/branch coverage, exit 0. Re-verified with the two other touched test files: 104 passed, 1838 subtests, exit 0. Fixes #718.
`register_protocol` keyed the protocol registry on `cls.__name__.upper()` and three dispatchable classes are named `HTTP`, so registering one silently displaced another. The entry records the measurement, the correction to the issue's `RegistryWarning` claim, why the guard departs from the presence-only siblings, and why re-keying belongs to #514. Regenerated CHANGELOG.md with util/changelog_md.py; --check exits 0.
`register_protocol` keyed the protocol registry on `cls.__name__.upper()` and three dispatchable classes are named `HTTP`, so registering one silently displaced another. The entry records the measurement, the correction to the issue's `RegistryWarning` claim, why the guard departs from the presence-only siblings, and why re-keying belongs to #514. Regenerated CHANGELOG.md with util/changelog_md.py; --check exits 0.
`register_protocol` keyed the protocol registry on `cls.__name__.upper()` and three dispatchable classes are named `HTTP`, so registering one silently displaced another. The entry records the measurement, the correction to the issue's `RegistryWarning` claim, why the guard departs from the presence-only siblings, and why re-keying belongs to #514. Regenerated CHANGELOG.md with util/changelog_md.py; --check exits 0.
Fixes #675.
The defect, re-verified on
b34f132f6The issue was measured on
0c7f2b7c9;mainhas moved four merges since, so I re-ran it onthis PR's base. The assignment has drifted from line 150 to line 160, but the defect is
unchanged. Measured with the worktree at
sys.path[0], the editable finder stripped fromsys.meta_path, andpcapkit.__file__asserted before anything else imported:The consequence is reachable straight through public API in the same run:
ProtocolBase.expand_comp('HTTP')resolved topcapkit.protocols.application.httpv1.HTTPafterwards — a bare-name lookup silently returning a different class than it did before.
Two facts the issue did not state, both of which matter for the fix:
HTTPclasses areProtocolBasesubclasses but not subclasses of thepublic
Protocol.Protocol.__init_subclass__is what callsregister_protocol(cls)unconditionally, so it never fires for them — which is why a plain
import pcapkitis silent.Measured with the fix applied: 0
RegistryWarnings duringimport pcapkit.__init_subclass__makes the collision reachable with no registry call in theuser's code at all. Defining
class HTTP(Protocol)displaces the built-in entry. That is thesharpest form of the bug; it now warns, and there is a test for it.
The
RegistryWarningclaim — confirmed on the number, corrected on the reasonThe issue says
register_protocolis "the one registrar inpcapkit/foundation/registry/withzero
RegistryWarninguses". The count is right, the explanation is not, and the correctedversion is a stronger argument for fixing it here:
RegistryWarningusespcapkit/foundation/registry/__init__.pypcapkit/foundation/registry/foundation.pypcapkit/foundation/registry/protocols.pyrg 'warn\(' pcapkit/foundation/registry/returns nothing at all — no function in thispackage warns about anything, so "its siblings warn and this one does not" is not true of the code
in this package. The siblings warn by delegating:
register_tcpforwards toTransport.register,register_ipv4_optiontoIPv4.register_option, and so on, and it is thoseclassmethods that carry
if code in cls.__xxx__: warn(..., RegistryWarning).So the real outlier property is sharper than the issue states:
register_protocolis the onlykeyed registrar in the package — and the only
__name__.upper()-keyed registry anywhere inpcapkit/— that mutates its target dict with no guard and no delegate that could supply one.There is no classmethod behind the module-level dict
pcapkit.protocols.__proto__, so the guardhas to be inline here. That is what this PR does.
Why fix (1) and not fix (2)
Every reader of the registry, and what a unique key would do to each:
ProtocolBase.expand_comp(protocols/protocol.py:697)value.upper()from a caller stringcomp = (value.upper(),). Breaks alias classes whose own name is absent fromid()—IPsec.id()returns('AH', 'ESP'), soframe['IPsec']would raiseProtocolNotFoundinstead of matching an ESP layer.ProtocolBase.__getitem__/__contains__/_check_term_thresholdexpand_comppacket['HTTP'],'HTTP' in packetandextract(protocol=...)all ride on it.ProtoChain.index/count/__contains__(corekit/protochain.py:113,133,207)expand_compReassemblyMeta.protocol(foundation/reassembly/reassembly.py:77)cls.name.upper()Rawas "the protocol this reassembly tracks".TraceFlowMeta.protocol(foundation/traceflow/traceflow.py:78)cls.name.upper()Raw.PayloadField.protocolsetter (corekit/fields/misc.py:267)protocolverbatim, no.upper()None, then falls back toRaw.pcapkit/protocols/__init__.py:74-75name.upper()over__all__Three things make (2) infeasible in this change:
Raw, soa re-keying would itself be a silent behaviour change — the same failure mode as the bug.
expand_comptakes a bare name by construction. It is handed a user string like'HTTP',so a qualified key does not merely move the lookup, it removes the ability to perform it. That
is a public contract change:
docs/source/pcapkit/protocols/index.rst:154documentspcapkit.protocols.__proto__withautodata, cross-referenced toregister_protocol.pcapkit/protocols/protocol.pyinparticular is owned by other open work right now.
So (1), with (2) recorded in the docstring as belonging to #514 rather than quietly dropped.
The one deliberate departure from house style
The siblings guard on mere presence (
if code in cls.__proto__). This guard reads "presentand the incumbent is a different class", because the sibling keys are caller-supplied
codeswhile this key is derived from the class, and this function is the funnel all nine wrapper
registrars end in.
register_tcp(p1, MyProto)followed byregister_udp(p2, MyProto)— one classunder two codes, supported and documented — reaches it twice with the same class and nothing
displaced. A presence-only guard would warn about an overwrite that overwrote nothing, and the
wholesale
RegistryWarningfilter that invites is exactly what would then hide the realcollision.
test_register_protocol_stays_quiet_when_nothing_is_displacedpins this, andtest_sibling_registries_still_warn_on_an_identical_re_registrationpins that the siblings werenot relaxed to match.
Evidence
Exit codes read from files, never from a pipeline. The
PASSED-line-with-SUBFAILED-subteststrap appeared in the "before" run exactly as expected — the per-test line read
PASSEDwhile bothits subtests failed — so the summary line is the thing to read.
Before (pristine
protocols.py, new tests) —rc=1:After (fixed) —
rc=0:Nothing else perturbed. Every test file that reads the registry by bare name, defines a
Protocolsubclass, asserts "no warnings", or checks docstrings —rc=0:tests/protocols/test_registry_runtime.pyis the pre-existing file asserting that the siblingregistrars warn on overwrite; it passes untouched.
tests/foundation/overall: 238 passed / 11 skipped / 388 subtests, against a baseline of234 passed / 381 subtests, with the same single pre-existing failure noted below.
Coverage did not go backwards.
pcapkit/foundation/registry/protocols.py, same test scope(
tests/foundation/registry/) both times, viacoverage run -m pytest:+5 statements and +2 branches with misses flat at 27 — every line and branch added is
executed. The missing ranges are the same regions shifted by the docstring's added lines
(
198-221 → 254-277,290-292 → 346-348,944-953 → 1000-1009). In that directory tests went9 → 13 and subtests 80 → 87.
Lint unchanged. Project
PYLINT_FLAGSon the module, pristine vs fixed, identical messagecounts:
10 C0301, 1 E0013, 3 R0022, 1 W0012, 4 W0404— zero new findings. Theisortcomplainton this file is pre-existing (reproduced on the pristine copy) and does not involve the import I
added.
Not done here, deliberately
pcapkit/foundation/registry/protocols.py:36is left alone. Narrowing that gate would breakuser classes subclassing
ProtocolBaseonly. Verified rather than trusted:issubclass(Protocol, ProtocolBase)isTrue,issubclass(ProtocolBase, Protocol)isFalse,and a
ProtocolBase-only subclass is accepted by the gate today. It stays wide.that. Per Design: adopt the EnumMeta/EnumSchema opt-in registration pattern for Protocol, Engine, Reassembly and TraceFlow #514's part-(c) sequencing this is c1, the step the alias work is meant to follow — so
this PR constrains Design: adopt the EnumMeta/EnumSchema opt-in registration pattern for Protocol, Engine, Reassembly and TraceFlow #514 by fixing the key space's observability while leaving its format
untouched, which is what keeps Design: adopt the EnumMeta/EnumSchema opt-in registration pattern for Protocol, Engine, Reassembly and TraceFlow #514 free to choose whatever key it wants.
tests/foundation/reassembly/test_tcp_runtime.py::TCPReassemblyRuntimeTests::test_sample_capture_reassembles_every_stream_byte_exactlyfails in this environment before and after, with
FileNotFoundError: sample capture 'test.pcap' not found— the generated sample captures are not committed. Environmental,pre-existing and unrelated; confirmed against the pristine tree.
Labels
fix+test. Notbreaking: this repo defines that label as "Alters public API or wireoutput", and nothing here does — no signature change, no registry-format change, the overwrite
still happens with the same result, and
import pcapkitgains zero warnings. The caveat worthstating rather than burying: a downstream project running under
-W errorthat today shadows abuilt-in protocol name would now get an exception where it previously got silence. That is the
intended point of the change, and it matches what every sibling registry has always done, so I
read it as
fixrather thanbreaking— say the word if you weight it the other way and I willadd the label.
I am not claiming CI green.