Repository navigation
refactor(foundation): declare each registry's code kind as a class attribute, not a module table (#2343) - #2392
Conversation
|
Possible to deprecate |
|
Yes — same shape as |
|
Review: NEEDS CHANGES at Test-only gap: Non-blocking notes from the review (not required for this PR): Dispatching the test fix now. |
…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.
41b095c to
5c03bf4
Compare
|
Amended |
|
Review: GOOD TO GO at |
|
Coverage: 91.67% (unit tier, Python 3.15,
Per-file detail: the |
…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.
…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.
…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.
…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.
…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/.
…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.
…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.
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.
…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.
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.
#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.
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.
make testpasses, and a test case covers the changeWhat is the purpose of your pull request?
refactor — code refactoringDescription
Follow-up to #2343/#2268, which already merged. You asked (after it merged) to move
_DESTINATION_KINDSfrom a module-level table to a class attribute declared directly on each destination, alongside__proto__— this is that change._DESTINATION_KINDSmapped a protocol class to the Enum registry_check_codevalidates 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.Link,Internet,Frame,PCAPNG,TCP,UDP,SCTP) now declares its own__proto_key__class attribute next to__proto__.register_protocol_codereads it straight off the owner the MRO-walk already finds, viavars()rather thangetattr()— a class keeping a registry of its own (Loopback,IPX) does not inherit its parent's pairing, whichgetattr()would have broken (caught bytest_number_and_own_registry_destinations_still_accept_an_int)._check_codealso 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_codecall site (register_ethertype,register_transtype, every option/parameter registrar) passes its own target the same way.register_linktypewrites into bothFrameandPCAPNG(keyed alike) and namesFramein the check rather than checking twice for no behavioural difference;register_apptypewrites intoTCPand/orUDP(resolved after the check, so the target isn't known yet) and namesTCP. Say the word if you'd rather either read differently.test_a_registry_of_its_own_does_not_inherit_its_parent_pairingnow actually registers throughregister_protocol_codeforLoopbackandIPXand pins both accepted (round 1's version only checked the attribute wasn't inherited, never called the registrar);test_a_rejection_names_the_destination_registryandtest_every_rejection_names_the_destination_registry(24 subtests, one per call site) pin the new error prefix;test_every_destination_kind_row_is_enforcednow walks everyProtocolBasesubclass declaring__proto_key__rather than a fixed dict's keys, so a new undeclared pairing is caught too.