Skip to content

refactor(foundation): declare each registry's code kind as a class attribute, not a module table (#2343) - #2392

Merged
JarryShaw merged 1 commit into
mainfrom
refactor/2343-proto-key-attrs
Oct 27, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
refactor/2343-proto-key-attrs

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

What is the purpose of your pull request?

  • refactor — code refactoring

Description

Follow-up to #2343/#2268, which already merged. You asked (after it merged) to move _DESTINATION_KINDS from a module-level table to a class attribute declared directly on each destination, alongside __proto__ — this is that change.

  • Problem: _DESTINATION_KINDS mapped a protocol class to the Enum registry _check_code validates its code against, found by walking the destination's MRO for whichever ancestor owns __proto__ and looking that owner up in the table. The pairing lived apart from the __proto__ it describes.
  • Fix: each of the seven destination classes (Link, Internet, Frame, PCAPNG, TCP, UDP, SCTP) now declares its own __proto_key__ class attribute next to __proto__. register_protocol_code reads it straight off the owner the MRO-walk already finds, via vars() rather than getattr() — a class keeping a registry of its own (Loopback, IPX) does not inherit its parent's pairing, which getattr() would have broken (caught by test_number_and_own_registry_destinations_still_accept_an_int). _check_code also takes the destination class explicitly now, so its error names which registry refused: "TCP: code must be … not True" rather than just "code must be …". Every other _check_code call site (register_ethertype, register_transtype, every option/parameter registrar) passes its own target the same way.
  • Two judgment calls, both flagged rather than hidden: register_linktype writes into both Frame and PCAPNG (keyed alike) and names Frame in the check rather than checking twice for no behavioural difference; register_apptype writes into TCP and/or UDP (resolved after the check, so the target isn't known yet) and names TCP. Say the word if you'd rather either read differently.
  • Test: test_a_registry_of_its_own_does_not_inherit_its_parent_pairing now actually registers through register_protocol_code for Loopback and IPX and pins both accepted (round 1's version only checked the attribute wasn't inherited, never called the registrar); test_a_rejection_names_the_destination_registry and test_every_rejection_names_the_destination_registry (24 subtests, one per call site) pin the new error prefix; test_every_destination_kind_row_is_enforced now walks every ProtocolBase subclass declaring __proto_key__ rather than a fixed dict's keys, so a new undeclared pairing is caught too.

@JarryShaw JarryShaw added refactor Restructuring for its own sake — neither a fix nor a new capability (refactor: prefix) review: running A cross-review is in flight against the current head - no verdict yet labels Oct 27, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Possible to deprecate _CODE_DESTINATIONS as well?

@JarryShaw

Copy link
Copy Markdown
Owner Author

Yes — same shape as __proto_key__, but on the enum side rather than derived from the protocol side. _CODE_DESTINATIONS maps an enum registry to the protocol class(es) that fan out to it; I'll add a dunder class attribute to each of the 4 enum registry classes (EtherType, TransType, LinkType, PayloadProtocolIdentifier) naming its own destination class(es) directly, and have _iter_code_targets read that off type(code) instead of the module-level dict. One open question before I start: pcapkit/const/reg/*.py look crawler-regenerated rather than hand-written — checking that first so the new attribute survives regeneration rather than living only in the crawler template. Will open this as its own follow-up PR once I've confirmed where it can safely live.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Review: NEEDS CHANGES at 41b095cc0 (Sonnet). Production code confirmed correct: identical owner/pairing across all 46 ProtocolBase subclasses, a 1,470-case destination×key fuzz matches base exactly, all 25 _check_code call sites pass the right target, mypy/pylint/vermin unchanged, docs clean.

Test-only gap: test_a_registry_of_its_own_does_not_inherit_its_parent_pairing never calls register_protocol_code — it only asserts __proto_key__ isn't on Loopback, which still passes if vars() were swapped back to getattr(). No test covers IPX either, though the PR body claims both are pinned. Fix: actually register through register_protocol_code for both Loopback and IPX and assert acceptance; confirmed that form passes on this head and fails under getattr(). Will also correct the PR body's "pins the Loopback/IPX case" claim.

Non-blocking notes from the review (not required for this PR): register_apptype's explicit-transport error-precedence choice is unpinned; 9 registrars still restate the pairing in a docstring the class attribute now also carries; a few copy-pasted comment preambles.

Dispatching the test fix now.

@JarryShaw JarryShaw added review: needs-changes Cross-review at the current head says changes are required; see the verdict comment and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 27, 2026
…tribute, not a module table (#2343)

Problem: _DESTINATION_KINDS mapped a protocol class to the Enum registry
_check_code validates its code against, found by walking the destination's
MRO for whichever ancestor owns __proto__ and looking that owner up in the
table. The pairing lived apart from the __proto__ it describes, and a new
destination needed both a new __proto__ and a new row in a module dict
elsewhere.

Fix: each of the seven destination classes (Link, Internet, Frame, PCAPNG,
TCP, UDP, SCTP) now declares its own __proto_key__ class attribute, alongside
__proto__, pairing its Enum registry with whether a bare int is also valid.
register_protocol_code reads it straight off the owner the MRO-walk already
finds (via vars(), not getattr, so a class keeping a registry of its own --
Loopback, IPX -- does not inherit its parent's pairing). _check_code also
takes the destination class explicitly now, so its error names which
registry refused, not just which enum: "TCP: code must be ... not True"
rather than "code must be ... not True". Every other call site of
_check_code (register_ethertype, register_transtype, every option/parameter
registrar) passes its own target the same way.

Two call sites write into two registries keyed alike and pass one of the two
as target, rather than checking twice for no behavioural difference:
register_linktype (Frame and PCAPNG) names Frame; register_apptype (TCP
and/or UDP, resolved after the check) names TCP.

Test: test_a_registry_of_its_own_does_not_inherit_its_parent_pairing pins the
Loopback/IPX case; test_a_rejection_names_the_destination_registry and
test_every_rejection_names_the_destination_registry (24 subtests, one per
call site) pin the new error prefix; test_every_destination_kind_row_is_enforced
now walks every ProtocolBase subclass declaring __proto_key__ rather than a
fixed dict's keys, so a new undeclared pairing is caught too.
@JarryShaw
JarryShaw force-pushed the refactor/2343-proto-key-attrs branch from 41b095c to 5c03bf4 Compare October 27, 2026 16:43
@JarryShaw JarryShaw added review: running A cross-review is in flight against the current head - no verdict yet and removed review: needs-changes Cross-review at the current head says changes are required; see the verdict comment labels Oct 27, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Amended 41b095cc0 → 5c03bf413: test_a_registry_of_its_own_does_not_inherit_its_parent_pairing now actually calls register_protocol_code for Loopback and IPX (mocking register like the neighbouring int-key test) and asserts both are accepted — the round-1 version only checked the attribute wasn't inherited and never exercised the registrar, so it would have passed unchanged under the old getattr() behaviour. Verified fail-without-fix: 3 new subtests fail under getattr(), pass under vars(). 78 passed / 533 subtests, unchanged otherwise. Updated the PR body's test bullet to describe this accurately.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Review: GOOD TO GO at 5c03bf413 (Sonnet, round 2). Independently re-derived the fail-without-fix claim on an isolated copy: swapping back to getattr() fails all 3 new subtests plus the sibling int-key test, exactly as expected; unmodified head passes 78/533/0. Confirmed the mock in the new test hides nothing load-bearing — register_protocol_code, the owner lookup, and _check_code all run for real, only the final destination.register call is mocked. PR body wording is accurate.

@JarryShaw JarryShaw added review: good-to-go Cross-review at the current head says ready; CI state is separate and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 27, 2026
@JarryShaw JarryShaw moved this to In review in PyPCAPKit Oct 27, 2026
@github-actions github-actions Bot added this to the PyPCAPKit v1.5 milestone Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Coverage: 91.67% (unit tier, Python 3.15, 5c03bf413, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 4 20 2 95.16%
pcapkit/const 19601 880 2620 867 92.12%
pcapkit/corekit 2596 74 818 37 96.51%
pcapkit/dumpkit 282 3 102 3 98.44%
pcapkit/foundation 3046 24 1062 26 98.78%
pcapkit/interface 138 4 52 3 96.32%
pcapkit/protocols 17491 209 4602 178 98.24%
pcapkit/toolkit 914 36 334 10 95.51%
pcapkit/utilities 502 7 144 5 98.14%
pcapkit/vendor 4434 1961 1018 167 52.49%

Per-file detail: the coverage-html artifact of this run.

@JarryShaw
JarryShaw merged commit c91de70 into main Oct 27, 2026
65 checks passed
@JarryShaw
JarryShaw deleted the refactor/2343-proto-key-attrs branch October 27, 2026 22:27
@github-project-automation github-project-automation Bot moved this from In review to Done in PyPCAPKit Oct 27, 2026
JarryShaw added a commit that referenced this pull request Oct 27, 2026
…registries themselves

Follow-up to #2392/#2343. _CODE_DESTINATIONS was a module-level dict
mapping an enum registry type to the protocol class(es) owning the
__proto__ dispatch registry keyed by that enum, so register_protocol_code
could infer a destination from a bare enum member. Mirrors __proto_key__,
which #2392 moved the opposite direction (destination -> enum) onto the
destination classes themselves.

Each of the 4 enum registry classes (EtherType, TransType, LinkType,
PayloadProtocolIdentifier) now declares __proto_dest__, a tuple of
ModuleDescriptor naming its destination class(es) lazily -- const sits
below protocols in the dependency order, so a real class reference would
make const import protocols; ModuleDescriptor (already used for __proto__
dispatch tables elsewhere) avoids that without inventing a parallel
string-path scheme. _iter_code_targets reads the dunder off type(code)
via getattr() and resolves each descriptor's .klass.

getattr(), not vars(): verified aenum refuses to extend an enumeration
that already has members, so none of these four registries can gain a
subclass with its own registry -- the hazard __proto_key__'s vars() call
guards against (Loopback/IPX owning a differently-keyed registry) is
structurally impossible here. Where an enum registry is subclassed
(AppType -> TCP/UDP/SCTP/DCCP) the base has no members and the parts
share one key space, so inheriting the pairing is correct.

const/reg/*.py and const/sctp/payload_protocol_identifier.py are
regenerated, not hand-edited: the vendor crawlers (vendor/reg/*.py,
vendor/sctp/payload_protocol_identifier.py) gain a PROTO_DEST class
attribute, threaded through vendor/default.py's LINE template and
context(); a throwaway script recovered each committed file's ENUM/MISS
blocks and re-rendered through the unmodified template first, asserting
byte-identity with the committed file, before re-rendering with
PROTO_DEST to confirm the diff is purely the template's effect.

Tests: tests/vendor/test_vendor_proto_dest_2392_unit.py (new) plus
ProtoDestDeclarationTests in test_protocol_code_registration_unit.py and
an update to test_dispatch_reachability_unit.py, which read the dunder
instead of the deleted dict.
JarryShaw added a commit that referenced this pull request Oct 27, 2026
…registries themselves

Follow-up to #2392/#2343. _CODE_DESTINATIONS was a module-level dict
mapping an enum registry type to the protocol class(es) owning the
__proto__ dispatch registry keyed by that enum, so register_protocol_code
could infer a destination from a bare enum member. Mirrors __proto_key__,
which #2392 moved the opposite direction (destination -> enum) onto the
destination classes themselves.

Each of the 4 enum registry classes (EtherType, TransType, LinkType,
PayloadProtocolIdentifier) now declares __proto_dest__, a tuple of
ModuleDescriptor naming its destination class(es) lazily -- const sits
below protocols in the dependency order, so a real class reference would
make const import protocols; ModuleDescriptor (already used for __proto__
dispatch tables elsewhere) avoids that without inventing a parallel
string-path scheme. _iter_code_targets reads the dunder off type(code)
via getattr() and resolves each descriptor's .klass.

getattr(), not vars(): verified aenum refuses to extend an enumeration
that already has members, so none of these four registries can gain a
subclass with its own registry -- the hazard __proto_key__'s vars() call
guards against (Loopback/IPX owning a differently-keyed registry) is
structurally impossible here. Where an enum registry is subclassed
(AppType -> TCP/UDP/SCTP/DCCP) the base has no members and the parts
share one key space, so inheriting the pairing is correct.

const/reg/*.py and const/sctp/payload_protocol_identifier.py are
regenerated, not hand-edited: the vendor crawlers (vendor/reg/*.py,
vendor/sctp/payload_protocol_identifier.py) gain a PROTO_DEST class
attribute, threaded through vendor/default.py's LINE template and
context(); a throwaway script recovered each committed file's ENUM/MISS
blocks and re-rendered through the unmodified template first, asserting
byte-identity with the committed file, before re-rendering with
PROTO_DEST to confirm the diff is purely the template's effect.

Tests: tests/vendor/test_vendor_proto_dest_2392_unit.py (new) plus
ProtoDestDeclarationTests in test_protocol_code_registration_unit.py and
an update to test_dispatch_reachability_unit.py, which read the dunder
instead of the deleted dict.
JarryShaw added a commit that referenced this pull request Oct 27, 2026
…num registries themselves

Follow-up to #2392/#2343. _CODE_DESTINATIONS was a module-level dict
mapping an enum registry type to the protocol class(es) owning the
__proto__ dispatch registry keyed by that enum, so register_protocol_code
could infer a destination from a bare enum member. Mirrors __proto_key__,
which #2392 moved the opposite direction (destination -> enum) onto the
destination classes themselves.

Each of the 4 enum registry classes (EtherType, TransType, LinkType,
PayloadProtocolIdentifier) now declares __proto_dest__, a tuple of
ModuleDescriptor naming its destination class(es) lazily -- const sits
below protocols in the dependency order, so a real class reference would
make const import protocols; ModuleDescriptor (already used for __proto__
dispatch tables elsewhere) avoids that without inventing a parallel
string-path scheme. _iter_code_targets reads the dunder off type(code)
via getattr() and resolves each descriptor's .klass.

getattr(), not vars(): verified aenum refuses to extend an enumeration
that already has members, so none of these four registries can gain a
subclass with its own registry -- the hazard __proto_key__'s vars() call
guards against (Loopback/IPX owning a differently-keyed registry) is
structurally impossible here. The one enum registry that is subclassed,
AppType, is irrelevant here only because it declares no __proto_dest__
at all -- a class-level destination would in fact be the wrong model for
it, since register_apptype routes each member to the registry named by
that member's own transport protocol, which no single shared destination
could express.

const/reg/*.py and const/sctp/payload_protocol_identifier.py are
regenerated, not hand-edited: the vendor crawlers (vendor/reg/*.py,
vendor/sctp/payload_protocol_identifier.py) gain a PROTO_DEST class
attribute, threaded through vendor/default.py's LINE template and
context(); a throwaway script recovered each committed file's ENUM/MISS
blocks and re-rendered through the unmodified template first, asserting
byte-identity with the committed file, before re-rendering with
PROTO_DEST to confirm the diff is purely the template's effect.

Tests: tests/vendor/test_vendor_proto_dest_2392_unit.py (new) plus
ProtoDestDeclarationTests in test_protocol_code_registration_unit.py and
an update to test_dispatch_reachability_unit.py, which read the dunder
instead of the deleted dict.
JarryShaw added a commit that referenced this pull request Oct 28, 2026
…num registries themselves (#2403)

Follow-up to #2392/#2343. _CODE_DESTINATIONS was a module-level dict
mapping an enum registry type to the protocol class(es) owning the
__proto__ dispatch registry keyed by that enum, so register_protocol_code
could infer a destination from a bare enum member. Mirrors __proto_key__,
which #2392 moved the opposite direction (destination -> enum) onto the
destination classes themselves.

Each of the 4 enum registry classes (EtherType, TransType, LinkType,
PayloadProtocolIdentifier) now declares __proto_dest__, a tuple of
ModuleDescriptor naming its destination class(es) lazily -- const sits
below protocols in the dependency order, so a real class reference would
make const import protocols; ModuleDescriptor (already used for __proto__
dispatch tables elsewhere) avoids that without inventing a parallel
string-path scheme. _iter_code_targets reads the dunder off type(code)
via getattr() and resolves each descriptor's .klass.

getattr(), not vars(): verified aenum refuses to extend an enumeration
that already has members, so none of these four registries can gain a
subclass with its own registry -- the hazard __proto_key__'s vars() call
guards against (Loopback/IPX owning a differently-keyed registry) is
structurally impossible here. The one enum registry that is subclassed,
AppType, is irrelevant here only because it declares no __proto_dest__
at all -- a class-level destination would in fact be the wrong model for
it, since register_apptype routes each member to the registry named by
that member's own transport protocol, which no single shared destination
could express.

const/reg/*.py and const/sctp/payload_protocol_identifier.py are
regenerated, not hand-edited: the vendor crawlers (vendor/reg/*.py,
vendor/sctp/payload_protocol_identifier.py) gain a PROTO_DEST class
attribute, threaded through vendor/default.py's LINE template and
context(); a throwaway script recovered each committed file's ENUM/MISS
blocks and re-rendered through the unmodified template first, asserting
byte-identity with the committed file, before re-rendering with
PROTO_DEST to confirm the diff is purely the template's effect.

Tests: tests/vendor/test_vendor_proto_dest_2392_unit.py (new) plus
ProtoDestDeclarationTests in test_protocol_code_registration_unit.py and
an update to test_dispatch_reachability_unit.py, which read the dunder
instead of the deleted dict.
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 28, 2026
JarryShaw added a commit that referenced this pull request Oct 28, 2026
…egistry and registration prose (#1103)

#1103 whole-repo prose sweep over the maker-message wave (#2296, #2404, #2405,
#2407) and the __proto_dest__ registry refactor (#2392, #2403).

Docstrings corrected where the prose did not match the code:

* ``Protocol`` -> ``ProtocolBase`` in the ``register``/``register_protocol``
  cross-references (protocol.py, internet.py, link.py, transport.py, sctp.py,
  registry/protocols.py).
* ``Raises:`` entries for the ``bool`` port guard (#2287) in TCP, UDP and SCTP
  ``make``, and for HTTP/1's request-without-``uri`` guard.
* ``protochain`` documented on both sides of its narrowing to ``None``
  (protocol.py, misc/null.py, #2317), and ``__init__`` names the
  ``__own_make_keywords__`` exception to its keyword check.
* IP reassembly names the IPv6 ``M`` bit and which buffer an atomic fragment is
  isolated from (#2270); ``_check_code`` names ``register_protocol_code`` among
  its callers (#2186).
* HIP's shared lifetime helper says where the octet appears instead of counting
  the makers that carry it, and ``_make_opt_pad`` warns and makes a ``PadN``
  rather than raising as its reader does.

Before/after narration replaced with present tense, the pattern #1103's round 6
banned: the ``__proto_dest__`` prose in the registration and vendor tests, and
the #2296 maker-message module docstring.

AST guard (``ast.dump`` via util/vendor_code_changed.py's ``code_of``, with
docstrings and bare string literals stripped) confirms all 16 touched files are
prose-only against e48062c. 135 tests and 334 subtests pass across the four
touched test modules, tests/test_docstring_contract.py, and the convention and
issue/PR-role guards in tests/project/.
JarryShaw added a commit that referenced this pull request Oct 28, 2026
…egistry and registration prose (#1103)

#1103 whole-repo prose sweep over the maker-message wave (#2296, #2404, #2405,
#2407) and the __proto_dest__ registry refactor (#2392, #2403).

Docstrings corrected where the prose did not match the code:

* ``Protocol`` -> ``ProtocolBase`` in the ``register``/``register_protocol``
  cross-references (protocol.py, internet.py, link.py, transport.py, sctp.py,
  registry/protocols.py).
* ``Raises:`` entries for the ``bool`` port guard (#2287) in TCP, UDP and SCTP
  ``make``, and for HTTP/1's request-without-``uri`` guard.
* ``protochain`` documented on both sides of its narrowing to ``None``
  (protocol.py, misc/null.py, #2317), and ``__init__`` names the
  ``__own_make_keywords__`` exception to its keyword check.
* IP reassembly names the IPv6 ``M`` bit and which buffer an atomic fragment is
  isolated from (#2270); ``_check_code`` names ``register_protocol_code`` among
  its callers (#2186).
* HIP's shared lifetime helper says where the octet appears instead of counting
  the makers that carry it, and ``_make_opt_pad`` warns and makes a ``PadN``
  rather than raising as its reader does.

Before/after narration replaced with present tense, the pattern #1103's round 6
banned: the ``__proto_dest__`` prose in the registration and vendor tests, the
``register_protocol_code`` key-check narration, and the #2296 and #2406
maker-message module docstrings.

AST guard (``ast.dump`` via util/vendor_code_changed.py's ``code_of``, with
docstrings and bare string literals stripped) confirms all 16 touched files are
prose-only against e48062c. 130 tests and 334 subtests pass across the four
touched test modules, tests/test_docstring_contract.py, and
tests/project/test_conventions_doc_claims.py,
tests/project/test_docs_issue_pr_roles_1103_unit.py and
tests/project/test_issue_refs_in_comments.py.
JarryShaw added a commit that referenced this pull request Oct 28, 2026
…egistry and registration prose (#1103)

#1103 whole-repo prose sweep over the maker-message wave (#2296, #2404, #2405,
#2407) and the __proto_dest__ registry refactor (#2392, #2403).

Docstrings corrected where the prose did not match the code:

* ``Protocol`` -> ``ProtocolBase`` in the ``register``/``register_protocol``
  cross-references (protocol.py, internet.py, link.py, transport.py, sctp.py,
  registry/protocols.py).
* ``Raises:`` entries for the ``bool`` port guard (#2287) in TCP, UDP and SCTP
  ``make``, and for HTTP/1's request-without-``uri`` guard.
* ``protochain`` documented on both sides of its narrowing to ``None``
  (protocol.py, misc/null.py, #2317), and ``__init__`` names the
  ``__own_make_keywords__`` exception to its keyword check.
* IP reassembly names the IPv6 ``M`` bit and which buffer an atomic fragment is
  isolated from (#2270); ``_check_code`` names ``register_protocol_code`` among
  its callers (#2186).
* HIP's shared lifetime helper says where the octet appears instead of counting
  the makers that carry it, and ``_make_opt_pad`` warns and makes a ``PadN``
  rather than raising as its reader does.

Before/after narration replaced with present tense, the pattern #1103's round 6
banned:

* the ``__proto_dest__`` prose in the registration and vendor tests, and the
  ``register_protocol_code`` key-check narration;
* the #2296 and #2406 maker-message module docstrings -- readers are exempt by
  design rather than "deliberately untouched", and the HTTP ``frame=None`` case
  is outside the guard rather than one the guard "has not widened to";
* the ``_NameLease`` rationale and the ``register_protocol`` name-collision
  note in registry/protocols.py, which described a fix and a redesign in the
  past tense;
* TCP's ``_make_mptcp_unknown`` length comment, which contrasted the current
  expression with "the former" one.

AST guard (``ast.dump`` via util/vendor_code_changed.py's ``code_of``, with
docstrings and bare string literals stripped) confirms all 16 touched files are
prose-only against e48062c. 129 tests pass (1 pre-existing skip) with 334
subtests across the four touched test modules, tests/test_docstring_contract.py,
tests/project/test_conventions_doc_claims.py,
tests/project/test_docs_issue_pr_roles_1103_unit.py and
tests/project/test_issue_refs_in_comments.py.
JarryShaw added a commit that referenced this pull request Oct 28, 2026
Problem: of the 60 pull requests merged in the #2312-#2407 range, only
#2334, #2336, #2337, #2340 and #2348 wrote a 1.5.0 changelog entry, and
#2398 and #2403 only reworded lines that were already there. Every other
user-visible change in the range -- wire-semantics fixes, maker guards,
reassembly behaviour, public-API and error-message changes -- went
unrecorded, so the entry no longer describes the release.

Fix: one entry per user-visible pull request, in each module section's
existing style and ordered by the issue it closes. Two pairs share a
bullet, since each is the direct follow-up of the other: #2393 with
#2401, and #2405 with #2407. The diff on the entry file is purely
additive.

Internal-only work is left out deliberately: dependency and action bumps
(#2387 to #2391), test-only and docs-prose passes (#2333, #2376, #2379,
#2380, #2402), the unreachable-branch removal (#2378), and the #: comment
marker the HIP Group and Packet crawlers now write (#2381). That last one
renders nothing either way: the marker lands inside the generated
_missing_ body, conf.py's exclude-members drops _missing_ from autodoc,
and a comment in a method body is not an attribute comment to begin with.

#2392 and #2403 are recorded together, under Added rather than Fixed, for
the declarations they introduce: __proto_key__ on the seven classes owning
a __proto__ registry (#2392), __proto_dest__ on the four enum registries
and Vendor.PROTO_DEST to generate it (#2403). Both PRs added the autodoc
entries, so both are rendered public attributes. #2392's rejection-message
change is deliberately not recorded as a fix: the bare "code must be ..."
text it prefixed came from _check_code, which #2193 introduced and which
is in no release tag -- absent at v1.5.0b14 -- so no released version ever
emitted it. #2403 is no longer skipped as internal because it reworded an
existing bullet to name __proto_dest__ while nothing credited the
attribute's introduction.

Four claims are corrected against the source rather than carried over:
FTP_DATA shipped in pcapkit/protocols/application/ftp.py from 1.0.0, not
1.4.0 (class FTP_DATA(Raw) is there at v1.0.0); #2365's pre-fix fallback
read cmmd.value, so a .value-carrying object was accepted unchecked
rather than raising; #2399 left the dpkt adapter and the adapters'
ModuleNotFound raises alone, so it claims no universal prefix; and
#2397's old tail did name the version it got, so the entry quotes it and
gains the :issue:`2302` it was missing. #2370's "a Section Header Block
... still fails" sentence is dropped: #2369 settles the byte order first,
and an SHB built from raw octets with ctx=None now rebuilds byte-exact in
both byte orders.

CHANGELOG.md is regenerated with util/changelog_md.py.

Test: tests/project/ in full, 0 failures, including test_changelog_md.py's
in-step and supported-subset checks; util/changelog_md.py --check reports
the two files in step; the entry parses under docutils with no structural
warning.
JarryShaw added a commit that referenced this pull request Oct 28, 2026
…egistry and registration prose (#1103)

#1103 whole-repo prose sweep over the maker-message wave (#2296, #2404, #2405,
#2407) and the __proto_dest__ registry refactor (#2392, #2403).

Docstrings corrected where the prose did not match the code:

* ``Protocol`` -> ``ProtocolBase`` in the ``register``/``register_protocol``
  cross-references (protocol.py, internet.py, link.py, transport.py, sctp.py,
  registry/protocols.py).
* ``Raises:`` entries for the ``bool`` port guard (#2287) in TCP, UDP and SCTP
  ``make``, and for HTTP/1's request-without-``uri`` guard.
* ``protochain`` documented on both sides of its narrowing to ``None``
  (protocol.py, misc/null.py, #2317), and ``__init__`` names the
  ``__own_make_keywords__`` exception to its keyword check.
* IP reassembly names the IPv6 ``M`` bit and which buffer an atomic fragment is
  isolated from (#2270); ``_check_code`` names ``register_protocol_code`` among
  its callers (#2186).
* HIP's shared lifetime helper says where the octet appears instead of counting
  the makers that carry it, and ``_make_opt_pad`` warns and makes a ``PadN``
  rather than raising as its reader does.

Before/after narration replaced with present tense, the pattern #1103's round 6
banned:

* the ``__proto_dest__`` prose in the registration and vendor tests, and the
  ``register_protocol_code`` key-check narration;
* the #2296 and #2406 maker-message module docstrings -- readers are exempt by
  design rather than "deliberately untouched", and the HTTP ``frame=None`` case
  is outside the guard rather than one the guard "has not widened to";
* the ``_NameLease`` rationale and the ``register_protocol`` name-collision
  note in registry/protocols.py, which described a fix and a redesign in the
  past tense;
* TCP's ``_make_mptcp_unknown`` length comment, which contrasted the current
  expression with "the former" one.

AST guard (``ast.dump`` via util/vendor_code_changed.py's ``code_of``, with
docstrings and bare string literals stripped) confirms all 16 touched files are
prose-only against e48062c. 129 tests pass (1 pre-existing skip) with 334
subtests across the four touched test modules, tests/test_docstring_contract.py,
tests/project/test_conventions_doc_claims.py,
tests/project/test_docs_issue_pr_roles_1103_unit.py and
tests/project/test_issue_refs_in_comments.py.
JarryShaw added a commit that referenced this pull request Oct 28, 2026
Problem: of the 60 pull requests merged in the #2312-#2407 range, only
#2334, #2336, #2337, #2340 and #2348 wrote a 1.5.0 changelog entry, and
#2398 and #2403 only reworded lines that were already there. Every other
user-visible change in the range -- wire-semantics fixes, maker guards,
reassembly behaviour, public-API and error-message changes -- went
unrecorded, so the entry no longer describes the release.

Fix: one entry per user-visible pull request, in each module section's
existing style and ordered by the issue it closes. Three pairs share a
bullet, since each is the direct follow-up or counterpart of the other:
#2393 with #2401, #2405 with #2407, and #2392 with #2403. The diff on the
entry file is purely additive.

Internal-only work is left out deliberately: dependency and action bumps
(#2387 to #2391), test-only and docs-prose passes (#2333, #2376, #2379,
#2380, #2402), the unreachable-branch removal (#2378), and the #: comment
marker the HIP Group and Packet crawlers now write (#2381). That last one
renders nothing either way: the marker lands inside the generated
_missing_ body, conf.py's exclude-members drops _missing_ from autodoc,
and a comment in a method body is not an attribute comment to begin with.

#2392 and #2403 are recorded together, under Added rather than Fixed, for
the declarations they introduce: __proto_key__ on each class owning a
__proto__ registry (#2392), __proto_dest__ on the enum registries and
Vendor.PROTO_DEST to generate it (#2403). The bullet names those classes
and registries rather than counting them, per the no-counted-numbers rule
for the unreleased entry. Both PRs added the autodoc entries, so both are
rendered public attributes. #2392's rejection-message
change is deliberately not recorded as a fix: the bare "code must be ..."
text it prefixed came from _check_code, which #2193 introduced and which
is in no release tag -- absent at v1.5.0b14 -- so no released version ever
emitted it. #2403 is no longer skipped as internal because it reworded an
existing bullet to name __proto_dest__ while nothing credited the
attribute's introduction.

Four claims are corrected against the source rather than carried over:
FTP_DATA shipped in pcapkit/protocols/application/ftp.py from 1.0.0, not
1.4.0 (class FTP_DATA(Raw) is there at v1.0.0); #2365's pre-fix fallback
read cmmd.value, so a .value-carrying object was accepted unchecked
rather than raising; #2399 left the dpkt adapter and the adapters'
ModuleNotFound raises alone, so it claims no universal prefix; and
#2397's old tail did name the version it got, so the entry quotes it and
gains the :issue:`2302` it was missing. #2370's "a Section Header Block
... still fails" sentence is dropped: #2369 settles the byte order first,
and an SHB built from raw octets with ctx=None now rebuilds byte-exact in
both byte orders.

CHANGELOG.md is regenerated with util/changelog_md.py.

Test: tests/project/ in full, 0 failures, including test_changelog_md.py's
in-step and supported-subset checks; util/changelog_md.py --check reports
the two files in step; the entry parses under docutils with no structural
warning.
JarryShaw added a commit that referenced this pull request Oct 28, 2026
#1103 whole-repo prose sweep over the maker-message wave (#2296, #2404, #2405,
#2407) and the __proto_dest__ registry refactor (#2392, #2403).

Docstrings corrected where the prose did not match the code:

* ``Protocol`` -> ``ProtocolBase`` in the ``register``/``register_protocol``
  cross-references (protocol.py, internet.py, link.py, transport.py, sctp.py,
  registry/protocols.py).
* ``Raises:`` entries for the ``bool`` port guard (#2287) in TCP, UDP and SCTP
  ``make``, and for HTTP/1's request-without-``uri`` guard.
* ``protochain`` documented on both sides of its narrowing to ``None``
  (protocol.py, misc/null.py, #2317), and ``__init__`` names the
  ``__own_make_keywords__`` exception to its keyword check.
* IP reassembly names the IPv6 ``M`` bit and which buffer an atomic fragment is
  isolated from (#2270); ``_check_code`` names ``register_protocol_code`` among
  its callers (#2186).
* HIP's shared lifetime helper says where the octet appears instead of counting
  the makers that carry it, and ``_make_opt_pad`` warns and makes a ``PadN``
  rather than raising as its reader does.

Before/after narration replaced with present tense, the pattern #1103's round 6
banned:

* the ``__proto_dest__`` prose in the registration and vendor tests, and the
  ``register_protocol_code`` key-check narration;
* the #2296 and #2406 maker-message module docstrings -- readers are exempt by
  design rather than "deliberately untouched", and the HTTP ``frame=None`` case
  is outside the guard rather than one the guard "has not widened to";
* the ``_NameLease`` rationale and the ``register_protocol`` name-collision
  note in registry/protocols.py, which described a fix and a redesign in the
  past tense;
* TCP's ``_make_mptcp_unknown`` length comment, which contrasted the current
  expression with "the former" one.

AST guard (``ast.dump`` via util/vendor_code_changed.py's ``code_of``, with
docstrings and bare string literals stripped) confirms all 16 touched files are
prose-only against e48062c. 129 tests pass (1 pre-existing skip) with 334
subtests across the four touched test modules, tests/test_docstring_contract.py,
tests/project/test_conventions_doc_claims.py,
tests/project/test_docs_issue_pr_roles_1103_unit.py and
tests/project/test_issue_refs_in_comments.py.
JarryShaw added a commit that referenced this pull request Oct 28, 2026
Problem: of the 60 pull requests merged in the #2312-#2407 range, only
#2334, #2336, #2337, #2340 and #2348 wrote a 1.5.0 changelog entry, and
#2398 and #2403 only reworded lines that were already there. Every other
user-visible change in the range -- wire-semantics fixes, maker guards,
reassembly behaviour, public-API and error-message changes -- went
unrecorded, so the entry no longer describes the release.

Fix: one entry per user-visible pull request, in each module section's
existing style and ordered by the issue it closes. Three pairs share a
bullet, since each is the direct follow-up or counterpart of the other:
#2393 with #2401, #2405 with #2407, and #2392 with #2403. The diff on the
entry file is purely additive.

Internal-only work is left out deliberately: dependency and action bumps
(#2387 to #2391), test-only and docs-prose passes (#2333, #2376, #2379,
#2380, #2402), the unreachable-branch removal (#2378), and the #: comment
marker the HIP Group and Packet crawlers now write (#2381). That last one
renders nothing either way: the marker lands inside the generated
_missing_ body, conf.py's exclude-members drops _missing_ from autodoc,
and a comment in a method body is not an attribute comment to begin with.

#2392 and #2403 are recorded together, under Added rather than Fixed, for
the declarations they introduce: __proto_key__ on each class owning a
__proto__ registry (#2392), __proto_dest__ on the enum registries and
Vendor.PROTO_DEST to generate it (#2403). The bullet names those classes
and registries rather than counting them, per the no-counted-numbers rule
for the unreleased entry. Both PRs added the autodoc entries, so both are
rendered public attributes. #2392's rejection-message
change is deliberately not recorded as a fix: the bare "code must be ..."
text it prefixed came from _check_code, which #2193 introduced and which
is in no release tag -- absent at v1.5.0b14 -- so no released version ever
emitted it. #2403 is no longer skipped as internal because it reworded an
existing bullet to name __proto_dest__ while nothing credited the
attribute's introduction.

Four claims are corrected against the source rather than carried over:
FTP_DATA shipped in pcapkit/protocols/application/ftp.py from 1.0.0, not
1.4.0 (class FTP_DATA(Raw) is there at v1.0.0); #2365's pre-fix fallback
read cmmd.value, so a .value-carrying object was accepted unchecked
rather than raising; #2399 left the dpkt adapter and the adapters'
ModuleNotFound raises alone, so it claims no universal prefix; and
#2397's old tail did name the version it got, so the entry quotes it and
gains the :issue:`2302` it was missing. #2370's "a Section Header Block
... still fails" sentence is dropped: #2369 settles the byte order first,
and an SHB built from raw octets with ctx=None now rebuilds byte-exact in
both byte orders.

CHANGELOG.md is regenerated with util/changelog_md.py.

Test: tests/project/ in full, 0 failures, including test_changelog_md.py's
in-step and supported-subset checks; util/changelog_md.py --check reports
the two files in step; the entry parses under docutils with no structural
warning.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Restructuring for its own sake — neither a fix nor a new capability (refactor: prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant