fix(reg)!: drop TransportProtocol's IntFlag base now that nothing composes - #836
Conversation
|
NEEDS CHANGES — changing Measured on this head (
The root error is keeping Two more, smaller:
|
1c6d93c to
2fba732
Compare
|
Fix pushed as So a minted member no longer gets bit-decomposed, and the bit-decomposition path survives only for a genuine bare int all of whose bits are real transports — which is the pre-existing tested behaviour for hand-built composites. The The third flag-count pin is fixed and the two previously-untested pins were measured rather than guessed: Back to |
|
Cross-review (sonnet) on Two narration fixes wanted before this is ready, both verified by me:
One pre-existing defect, out of scope here, recorded so it is not lost: |
2fba732 to
3567359
Compare
|
GOOD TO GO — amended to Every changed line is docstring prose. The corrected docstring now states the thing I asked for and states it accurately: the misread-as-composite defect existed only in an intermediate round of this PR, stock The commit message's count is corrected to 104 tests under plain Zero stash entries in the main checkout and the worker's worktree; no leftover scratch worktrees. Granting |
|
Merged-together integration check — clean. CI only ever proved each PR alone: #834, #835 and #836 were all cut from Merged all three into a scratch worktree at That last one is not this change. Verified with a stock control — a clean So 356 tests pass on the combined tree with zero failures attributable to the merge, and the three may be merged in any order. |
| # next integer after the current maximum. | ||
| max_val = max(TransportProtocol.__members__.values()) | ||
| return extend_enum(TransportProtocol, key.lower(), max_val * 2) | ||
| return extend_enum(TransportProtocol, key.lower(), max_val + 1) |
There was a problem hiding this comment.
Do not allow extension of TransportProtocol at all.
There was a problem hiding this comment.
Taking this as a ruling — TransportProtocol stops being extensible. Checked what depends on minting first, and it is narrow:
- The only in-tree caller is
_dispatch's ownTransportProtocol.get(proto.lower())(apptype.py:2400, and the generator's copy atvendor/.../apptype.py:355), reached only whenprotoarrives as astr. So refusing an unknown name turns a silently-minted junk member into aValueErrorat that one site. - Nothing else calls
.get()with a name that could miss;tests/vendor/test_vendor_reg_apptype_generator_unit.py:183pins the call-site count at 1, which a changed miss path does not disturb.
This also subsumes the pre-existing defect I recorded on #775: TP.get(' tcp') and TP.get('tcp ') currently mint members literally named ' tcp' and 'tcp ' because .get() lowercases but does not strip. With extension gone they raise instead, so that case dies here rather than waiting for #775.
Sending it back to implement: drop the extend_enum call and the max_val computation, refuse an unrecognised name the same way the | case already does, and fold the two probes that currently assert minting (TP.get('bogus') -> 9 and the minted-member _dispatch test) into assertions that it is refused. _dispatch's bare-int composite branch stays — a bare int can still reach it by hand even when no member can be minted.
review: good-to-go withdrawn until that lands.
There was a problem hiding this comment.
debating but maybe we should strip the protocol after lower cased as well?
There was a problem hiding this comment.
Measured the question rather than guessed, and my recommendation is refuse, do not strip — but it is a judgement call and one word from you overrides it.
There is no house precedent for stripping. Across pcapkit/const/ (132 enum files), zero use .strip() anywhere, and TransportProtocol.get is the only one that case-folds at all. The other two get() shapes differ from each other, let alone from this one:
pcapkit/const/tcp/flags.py:80 return Flags[key] # exact, no folding at all
pcapkit/const/ftp/command.py:306 name = key.upper() # folds, then STILL mints on miss
pcapkit/const/reg/apptype/…:76 key.lower() # folds, minting removed by your ruling
And no in-tree source can produce a padded name. proto reaches .get() only as a keyword argument to AppType.get / register_apptype — programmatic, not parsed text — and the generator never calls .get() with IANA column data at generation time; tests/vendor/test_vendor_reg_apptype_generator_unit.py:183 pins the generated base module to exactly one TransportProtocol.get( call site. So ' tcp' can only arrive from a caller bug.
The asymmetry that decides it for me: before your ruling ' tcp' silently minted junk, which is the actual defect. After it, ' tcp' raises ValueError — loud and correct. Stripping would make it succeed, which is a third behaviour and the only one that hides the caller's mistake. .lower() earns its keep because IANA canonically spells TCP while members are lowercase; there is no equivalent upstream that emits whitespace.
One scoping note so the ruling is not over-applied: ftp/command.py:306 still mints on a miss, and should — FTP commands are genuinely open-ended, whereas this registry is partitioned over a closed set of four transports. Dropping extensibility is right here and would be wrong there.
Labelling needs: decision and holding the worker on the refuse path meanwhile. Say the word and I will add .strip() instead.
There was a problem hiding this comment.
Commented in the main thread.
There was a problem hiding this comment.
And yes, follow house convention and thus your recommendation.
There was a problem hiding this comment.
Confirmed and already in flight — no .strip(), following the engine-selection convention (extraction.py:922 lowercases only; no .strip() anywhere in pcapkit/foundation/). needs: decision is cleared.
One thing I should not overstate: your "yes, follow house convention and thus your recommendation" landed ten seconds after I posted a separate reading in the main thread, so I am treating it as confirming the .strip() recommendation only, not the other one.
Still my assumption rather than your ruling, flagged there and repeated here so it does not slip through: I read "treat it as a whole, instead of splitting" as covering the bare-int path too, so _dispatch stops decomposing proto via show_flag_values and a bare 3 will report "3 names no transport protocol registry" instead of "tcp|udp names 2 transport protocols". It is the coherent consequence of having no Flag base and it drops the last show_flag_values use from the apptype family — but it does change a user-visible message on an on-contract input, so one word reverses it if you meant only the string side.
|
Added two inline comments |
3567359 to
a5539ed
Compare
|
NEEDS CHANGES — amended to Verified on the new head: The defect. if '|' in key:
raise ValueError(f'{key!r} is not a valid {TransportProtocol.__name__}')
raise ValueError(f'{key!r} is not a valid {TransportProtocol.__name__}')So the Two acceptable shapes, and I have asked the worker to pick one and justify it: delete the branch and its NOTE, folding any surviving rationale into a short comment on the single raise; or keep the branch and give it a genuinely different, more useful message, e.g. naming that Everything else checks out: parent is The |
a5539ed to
abee187
Compare
|
Dead branch fixed in
It also removed three stale One imprecision I am deliberately not spending a round on: for the pathological input Head is new, so the previous verdict does not carry: back to |
…poses Closes #808. Blocked on #806 (merged as #815), which retyped every member's `proto` to a single transport, leaving nothing that builds or relies on a composite `TransportProtocol` value. - `TransportProtocol` becomes a plain `aenum.IntEnum`. The four transports keep their exact values (tcp=1, udp=2, sctp=4, dccp=8, undefined=0) via `cast(...)` rather than `auto()`, since IntEnum's `auto()` numbers sequentially and would renumber them. - Removed `_missing_`'s composing fallback/range guard (a plain IntEnum's default `_missing_` already rejects anything undeclared). - `.get()` refuses an unrecognised name outright rather than minting one -- maintainer ruling: "Do not allow extension of TransportProtocol at all." It used to mint at `max_val + 1` (an intermediate revision of this PR; stock still doubles, `max_val * 2`); there is nothing left to walk now. Two earlier rounds of this PR gave a `'|'`-containing string, e.g. `'tcp|udp'`, its own distinct message on the theory that it names a composite rather than merely an unknown name; the owner's final ruling drops that distinction outright rather than refining it -- "since it's no longer a Flag, `|` joined values are no longer parsed and accepted, we will treat it as a whole, instead of splitting" -- so `'|'` gets the identical generic refusal any other unrecognised name does. `.get()` still only case-folds, never strips whitespace, per the same ruling: the owner pointed at engine selection (`extraction.py:922`, lower-only, no `.strip()` anywhere in `pcapkit/foundation/`) as the convention to match, and normalising is the only part of that convention adopted -- engine selection warns and falls back to a default on a miss, `.get()` still raises. - `_dispatch` no longer decodes a bare-int `proto`'s bits at all, matching the same ruling: a composite built by hand, e.g. `TransportProtocol.tcp | TransportProtocol.udp` (a bare `int` since `|` falls through to `int.__or__` now), used to be split via `show_flag_values` into a `ProtocolError` naming every transport whose bit was set -- the GitHub issue #759 fix, present on stock and refined once more in an intermediate round of this PR to tell a clean composite (`3`, real bits only) apart from a stray bit (`17`, one real bit plus one nothing declares). The owner's ruling retires that decoding entirely rather than refining it further: a bare-int composite is now refused exactly like any other value naming no registry -- one plain `ValueError`, whether the int is `3`, `17`, or `TransportProtocol.undefined`. This removes the last use of `show_flag_values` and of `ProtocolError` from this module, so both imports are dropped along with the docstring `Raises:` entries naming `ProtocolError` on `_dispatch`/`get`/`get_all`. User-visible consequence: `AppType.get(80, proto=17)` and `AppType.get(80, proto=3)` were both `ProtocolError` on stock `ad4805f5f` and are both `ValueError` now. Neither is a regression on a *supported* input -- a bare `int` was off-contract until this PR widened `proto`'s annotation to include it -- but the exception type a caller now sees for that input has changed. - Widened `proto`'s type annotation to include `int` across `_dispatch`/`get`/`get_all`, and cast at the one dict-key site mypy cannot infer, to match the type mypy actually needs to stay clean. - Applied identically to the vendor generator template; verified the generated `TransportProtocol` class and `_dispatch`/`get` bodies are byte-identical between the two by rendering the template's `BASE` lambda directly against text extracted from the committed const file, rather than running the network-dependent vendor crawl. - `pcapkit/foundation/registry/protocols.py` (in scope once #835 merged into `ad4805f5f`): corrected `register_apptype`'s own NOTE, which justified resolving a string transport via `__members__` rather than `TransportProtocol[name]` with two claims this PR made false -- that `Flag.__getitem__` parses `'tcp|udp'` into the value `3`, and that `TransportProtocol` is an `IntFlag`. Neither holds once `|` is retired: `TransportProtocol['tcp|udp']` now raises a bare `KeyError`, same as `TransportProtocol['bogus']`, which is the corrected reason `__members__.get(...)` is still used -- this function's own contract is `RegistryError` on a miss, not `KeyError`. The `registries.get(1)` conclusion right after it is unchanged and stays: `hash(TransportProtocol.tcp) == hash(1)` regardless of the base, so an `int` key still hits a `TransportProtocol`-keyed dict entry. No behaviour changed here, only the comment explaining it. - `tests/dumpkit/test_nameless_enum_rendering_unit.py`'s flag-registry sweep drops from 7 to 6 registries (TransportProtocol no longer matches `issubclass(_, aenum.Flag)`) and from 4 to 3 distinct `_missing_` field widths; re-measured and re-pinned rather than assumed, prose updated to match. - Updated tests pinning removed Flag mechanics and two enum-sweep size pins (Flag count 7->6, IntEnum count 111->112). Converted every test whose premise the rulings above removed: the `.get('bogus')` minting probe and its misread-as-composite regression now assert refusal instead; the composite-string test lost its distinct-message assertions; the bare-int composite test (`test_a_bare_int_composite_is_refused_as_a_whole`, renamed from `test_a_proto_naming_two_transports_is_refused_rather_than_resolved`) now asserts the identical plain `ValueError` for `3` that a stray bit and `undefined` already got, through all three entry points; and `test_transport_protocol_can_no_longer_be_extended_at_runtime` pins the registration's removal rather than its shape. Corrected two rounds of stale narration a cross-review caught along the way: three comments/docstrings citing a `TransportProtocol.__getitem__` contrast that no longer has anything to contrast (there is no composite-specific branch left to justify), and two docstrings attributing the `max_val + 1` minting scheme to stock rather than to this PR's own now-superseded intermediate revision -- stock mints at `max_val * 2` (`16` for `'quic'`/`'bogus'`), measured on `ad4805f5f`. Also normalised three `3118ed796` "stock" references to `ad4805f5f` for consistency with the rebased base, since the claims hold at either commit. - Corrected an earlier claim: `list(TransportProtocol)` now yields all five members (4 on stock) since `Flag` hid the zero-valued `undefined` from iteration and plain `IntEnum` does not. Per-member repr/str/name/value are still byte-identical; nothing in-tree iterates the class bare, only through `__members__` (5 either way). `register_apptype` and its own tests needed no change beyond the NOTE above: they already reject anything that is not `isinstance(proto, TransportProtocol)`, which a bare int (what `|` now produces) satisfies identically, and its own no-strip case-fold resolution was already the model `.get()`'s normalisation follows. Built and tested against current `main` (`ad4805f5f`): `tests/const/` (77), `tests/foundation/registry/` (19), `tests/vendor/test_vendor_reg_apptype_generator_unit.py` (6) and `tests/dumpkit/test_nameless_enum_rendering_unit.py` (6) all pass via plain unittest, 108 total. mypy (114 errors/38 files) is identical before and after this change once line-number drift from the new `protocols.py` comment is accounted for -- zero new errors -- and isort is clean on all three touched source files.
abee187 to
d2772d1
Compare
|
All three rulings and F1–F3 land in Ruling 3 checked for a regression rather than assumed.
F1 fixed with the right reason, not a patched sentence. The replacement NOTE drops both false claims and explains what the guard is actually for: F2 fixed — For the record, and now in scope since a bare New head, so no verdict carries: |
|
GOOD TO GO — delta cross-review (opus) on A The asymmetry I briefed as the worst case has no reachable instance — but with a correction to the reviewer's own numbers. It reported 5 Also confirmed independently by the review: strings show zero resolution flips and zero exception-type changes across One number corrected in passing: the const file is 215,014 chars, not 215,013 — a trailing newline, identical on stock, so both figures describe agreement. Granting |
|
Ready to merge. CI on State for the record:
Unpublished decisions left where they belong rather than taken silently: the Merging is yours. When this lands it closes #808, which unblocks #775 and #807, and #816 is expected to dissolve with #775. |
…t minting (#860) Step 2 of #860 (PR 2 of 2, following AppType's 8 non-AppType siblings in #869). - Mix pcapkit.const.reg.apptype.apptype.AppType into EnumRegistry, alongside its four transport subclasses (TCP/UDP/SCTP/DCCP). AppType keeps its own get/get_all/register_alias, which already dispatch by transport protocol, and gains a working register (previously absent -- the base's generic one would have built a member with svc='<null>') and an _unregistered_member override reconstructing svc/port/proto. - Convert AppType._missing_'s 766 range-bounded extend_enum calls, plus the one more inside get()'s own second mint site, to _unregistered_member -- owner ruling: "only IANA registered ones are legit values ... get will not have sufficient information to create new ones." All 766+1 branches convert uniformly, including the 8 that named a real (if span-assigned rather than individually declared) service, since none of them mints at import time -- unlike FEATCode's earlier fix, there is no self-mutation defect here to address by declaring members statically. - Move TransportProtocol from power-of-two values to sequential auto(), per GitHub issue #836's ruling retiring `|`-composite decoding and the owner's further #860 ruling that the spacing itself then had nothing left to protect; delete the stale comment claiming the values must stay power-of-two. AppType._dispatch treats a composed or bare int identically as a whole either way, so the renumbering changes what specific integers mean, not only what hand-composed ones do -- e.g. a bare, uncomposed 4 (previously sctp's value) now silently resolves as dccp's, with no exception, since a real member sits at 4 under either numbering. No test pins any of this, per the owner's explicit instruction; the class comment states it instead. - Update corekit/enum.py's own docstring, which described AppType's EnumRegistry mixin as future work ("stays as it is until tier two lands"), and its get() docstring's str-valued-registry census (119 -> 124). - Update tests/vendor/test_vendor_reg_apptype_generator_unit.py's #770 pin, which asserted the old bare-literal-vs-auto() distinction that no longer exists now that every TransportProtocol member (undefined included) is auto()-valued: under auto(), a member's own value infers as Any, which is assignable to TransportProtocol with no cast at all, so no single member's wrapper is load-bearing against a mypy error any more -- measured directly against all three shapes (baseline, undefined unwrapped, undefined reverted to the bare literal 0). What the mypy test still depends on is staying on auto() at all, not on which member is wrapped; its own "4 errors" claim is corrected to the 3 [assignment] + 762 [arg-type] this tree's own get()/get_all() Union-typed proto (tolerates a bare int default) and the new _unregistered_member site (still plainly typed) actually produce once reverted that far. - Fix four tests in the existing suite that pinned the old minting/power-of- two behaviour directly (test_const_apptype_split_unit.py, test_const_enum_builtin_parity.py), and add 17 new tests plus prose/count updates in test_const_enum_no_mint.py and test_const_registry_protocol.py. All 5 crawlers regenerate byte-identically on a second run; git status is clean relative to this commit. mypy/pylint deltas are the same classes of finding this codebase already tolerates elsewhere (aenum stub gaps, import-outside-toplevel/protected-access/no-member in test internals), suppressed with # type: ignore[override]/pylint: disable=arguments-differ, arguments-renamed where the codebase's own convention already does so. 403 test methods pass across the complete tests/const/ and tests/protocols/transport/ directories, plus 87 across tests/vendor/ (excluding the live-network crawler-reachability test) and 55 across the TransportProtocol-consuming suites outside those directories.
…t minting (#860) Step 2 of #860 (PR 2 of 2, following AppType's 8 non-AppType siblings in #869). - Mix pcapkit.const.reg.apptype.apptype.AppType into EnumRegistry, alongside its four transport subclasses (TCP/UDP/SCTP/DCCP). AppType keeps its own get/get_all/register_alias, which already dispatch by transport protocol, and gains a working register (previously absent -- the base's generic one would have built a member with svc='<null>') and an _unregistered_member override reconstructing svc/port/proto. - Convert AppType._missing_'s 766 range-bounded extend_enum calls, plus the one more inside get()'s own second mint site, to _unregistered_member -- owner ruling: "only IANA registered ones are legit values ... get will not have sufficient information to create new ones." All 766+1 branches convert uniformly, including the 8 that named a real (if span-assigned rather than individually declared) service, since none of them mints at import time -- unlike FEATCode's earlier fix, there is no self-mutation defect here to address by declaring members statically. - Move TransportProtocol from power-of-two values to sequential auto(), per PR #836's ruling retiring `|`-composite decoding and the owner's further #860 ruling that the spacing itself then had nothing left to protect; delete the stale comment claiming the values must stay power-of-two. AppType._dispatch treats a composed or bare int identically as a whole either way, so the renumbering changes what specific integers mean, not only what hand-composed ones do -- e.g. a bare, uncomposed 4 (previously sctp's value) now silently resolves as dccp's, with no exception, since a real member sits at 4 under either numbering. No test pins any of this, per the owner's explicit instruction; the class comment states it instead, and every docstring that used to claim a hand-built composite is refused unconditionally (_dispatch's and get's own) is corrected to say it is looked up as a whole and resolves or is refused depending on whether some real member happens to equal it. - Update corekit/enum.py's own docstring, which described AppType's EnumRegistry mixin as future work ("stays as it is until tier two lands"), and its get() docstring's str-valued-registry census (119 -> 124). - Update tests/vendor/test_vendor_reg_apptype_generator_unit.py's #770 pin, which asserted the old bare-literal-vs-auto() distinction that no longer exists now that every TransportProtocol member (undefined included) is auto()-valued: under auto(), a member's own value infers as Any, which is assignable to TransportProtocol with no cast at all, so no single member's wrapper is load-bearing against a mypy error any more -- measured directly against all three shapes (baseline, undefined unwrapped, undefined reverted to the bare literal 0). What the mypy test still depends on is staying on auto() at all, not on which member is wrapped; its "4 errors" claim is corrected to the 3 [assignment] + 762 [arg-type] this tree produces once reverted that far, and to the two separate changes that moved the count there: PR #836 widened get()/get_all()'s own proto annotations to tolerate a bare int default before this issue touched anything, and this issue's own new _unregistered_member site is what brought the count back up from 2 to 3. - Fix four tests in the existing suite that pinned the old minting/power-of- two behaviour directly (test_const_apptype_split_unit.py, test_const_enum_builtin_parity.py), and add 17 new tests plus prose/count updates in test_const_enum_no_mint.py and test_const_registry_protocol.py. All 5 crawlers regenerate byte-identically on a second run; git status is clean relative to this commit. mypy/pylint deltas are the same classes of finding this codebase already tolerates elsewhere (aenum stub gaps, import-outside-toplevel/protected-access/no-member in test internals), suppressed with # type: ignore[override]/pylint: disable=arguments-differ, arguments-renamed where the codebase's own convention already does so. 403 test methods pass across the complete tests/const/ and tests/protocols/transport/ directories, plus 87 across tests/vendor/ (excluding the live-network crawler-reachability test) and 55 across the TransportProtocol-consuming suites outside those directories.
…t minting (#860) Step 2 of #860 (PR 2 of 2, following AppType's 8 non-AppType siblings in #869). - Mix pcapkit.const.reg.apptype.apptype.AppType into EnumRegistry, alongside its four transport subclasses (TCP/UDP/SCTP/DCCP). AppType keeps its own get/get_all/register_alias, which already dispatch by transport protocol, and gains a working register (previously absent -- the base's generic one would have built a member with svc='<null>') and an _unregistered_member override reconstructing svc/port/proto. - Convert AppType._missing_'s 766 range-bounded extend_enum calls, plus the one more inside get()'s own second mint site, to _unregistered_member -- owner ruling: "only IANA registered ones are legit values ... get will not have sufficient information to create new ones." All 766+1 branches convert uniformly, including the 8 that named a real (if span-assigned rather than individually declared) service, since none of them mints at import time -- unlike FEATCode's earlier fix, there is no self-mutation defect here to address by declaring members statically. - Move TransportProtocol from power-of-two values to sequential auto(), per PR #836's ruling retiring `|`-composite decoding and the owner's further #860 ruling that the spacing itself then had nothing left to protect; delete the stale comment claiming the values must stay power-of-two. AppType._dispatch treats a composed or bare int identically as a whole either way, so the renumbering changes what specific integers mean, not only what hand-composed ones do -- e.g. a bare, uncomposed 4 (previously sctp's value) now silently resolves as dccp's, with no exception, since a real member sits at 4 under either numbering. No test pins any of this, per the owner's explicit instruction; the class comment states it instead, and every docstring that used to claim a hand-built composite is refused unconditionally (_dispatch's and get's own) is corrected to say it is looked up as a whole and resolves or is refused depending on whether some real member happens to equal it. Also fixed a generator-only comment (process()'s own, no const twin) still claiming a member's proto "is a single bit" -- true under the old power-of-two spacing, not under auto(). - Update corekit/enum.py's own docstring, which described AppType's EnumRegistry mixin as future work ("stays as it is until tier two lands"), and its get() docstring's str-valued-registry census (119 -> 124). - Update tests/vendor/test_vendor_reg_apptype_generator_unit.py's #770 pin, which asserted the old bare-literal-vs-auto() distinction that no longer exists now that every TransportProtocol member (undefined included) is auto()-valued: under auto(), a member's own value infers as Any, which is assignable to TransportProtocol with no cast at all, so no single member's wrapper is load-bearing against a mypy error any more -- measured directly against all three shapes (baseline, undefined unwrapped, undefined reverted to the bare literal 0). What the mypy test still depends on is staying on auto() at all, not on which member is wrapped; its "4 errors" claim is corrected to the 3 [assignment] + 762 [arg-type] this tree produces once reverted that far, and to the two separate changes that moved the count there: PR #836 widened get()/get_all()'s own proto annotations to tolerate a bare int default before this issue touched anything, and this issue's own new _unregistered_member site is what brought the count back up from 2 to 3. - Fix four tests in the existing suite that pinned the old minting/power-of- two behaviour directly (test_const_apptype_split_unit.py, test_const_enum_builtin_parity.py), and add 17 new tests plus prose/count updates in test_const_enum_no_mint.py and test_const_registry_protocol.py. All 5 crawlers regenerate byte-identically on a second run; git status is clean relative to this commit. mypy/pylint deltas are the same classes of finding this codebase already tolerates elsewhere (aenum stub gaps, import-outside-toplevel/protected-access/no-member in test internals), suppressed with # type: ignore[override]/pylint: disable=arguments-differ, arguments-renamed where the codebase's own convention already does so. 403 test methods pass across the complete tests/const/ and tests/protocols/transport/ directories, plus 87 across tests/vendor/ (excluding the live-network crawler-reachability test) and 55 across the TransportProtocol-consuming suites outside those directories.
…t minting (#860) Step 2 of #860 (PR 2 of 2, following AppType's 8 non-AppType siblings in #869). - Mix pcapkit.const.reg.apptype.apptype.AppType into EnumRegistry, alongside its four transport subclasses (TCP/UDP/SCTP/DCCP). AppType keeps its own get/get_all/register_alias, which already dispatch by transport protocol, and gains a working register (previously absent -- the base's generic one would have built a member with svc='<null>') and an _unregistered_member override reconstructing svc/port/proto. - Convert AppType._missing_'s 766 range-bounded extend_enum calls, plus the one more inside get()'s own second mint site, to _unregistered_member -- owner ruling: "only IANA registered ones are legit values ... get will not have sufficient information to create new ones." All 766+1 branches convert uniformly, including the 8 that named a real (if span-assigned rather than individually declared) service, since none of them mints at import time -- unlike FEATCode's earlier fix, there is no self-mutation defect here to address by declaring members statically. - Move TransportProtocol from power-of-two values to sequential ones, per PR #836's ruling retiring `|`-composite decoding and the owner's further #860 ruling that the spacing itself then had nothing left to protect; delete the stale comment claiming the values must stay power-of-two. undefined is declared as an explicit cast('TransportProtocol', 0) and tcp/udp/sctp/dccp continue from it via plain auto(), per the owner's own final ruling on the declaration shape ("undefined direct uses 0. then other real transport use auto. so we don't have to define a _start_ and the undefined declaration is explicit") -- an earlier revision of this same change used an explicit _start_ = 0 with every member on auto(), which the owner's ruling superseded. AppType._dispatch treats a composed or bare int identically as a whole either way, so the renumbering changes what specific integers mean, not only what hand-composed ones do -- e.g. a bare, uncomposed 4 (previously sctp's value) now silently resolves as dccp's, with no exception, since a real member sits at 4 under either numbering. No test pins any of this, per the owner's explicit instruction; the class comment states it instead, and every docstring that used to claim a hand-built composite is refused unconditionally (_dispatch's and get's own) is corrected to say it is looked up as a whole and resolves or is refused depending on whether some real member happens to equal it. - Update corekit/enum.py's own docstring, which described AppType's EnumRegistry mixin as future work ("stays as it is until tier two lands"), and its get() docstring's str-valued-registry census (119 -> 124). - Update tests/vendor/test_vendor_reg_apptype_generator_unit.py's #770 pin: undefined is once again the one bare-literal-under-cast member among four auto()-valued siblings, exactly as #770 first shaped it, so its wrapper is load-bearing against a mypy error again -- measured directly, stripping it alone (leaving auto() elsewhere) now reproduces 3 [assignment] + 762 [arg-type] errors (765 total), while stripping tcp's wrapper instead stays clean. The two separate changes behind the 3-not-4 count: PR #836 widened get()/get_all()'s own proto annotations to tolerate a bare int default before this issue touched anything, and this issue's own new _unregistered_member site is what brought the count back up from 2 to 3. - Fix four tests in the existing suite that pinned the old minting/power-of- two behaviour directly (test_const_apptype_split_unit.py, test_const_enum_builtin_parity.py), and add 17 new tests plus prose/count updates in test_const_enum_no_mint.py and test_const_registry_protocol.py. All 5 crawlers regenerate byte-identically on a second run; git status is clean relative to this commit. mypy/pylint deltas are the same classes of finding this codebase already tolerates elsewhere (aenum stub gaps, import-outside-toplevel/protected-access/no-member in test internals), suppressed with # type: ignore[override]/pylint: disable=arguments-differ, arguments-renamed where the codebase's own convention already does so. 216 test methods pass across test_const_apptype_split_unit.py, test_const_enum_no_mint.py, test_const_enum_builtin_parity.py, test_const_registry_protocol.py and test_vendor_reg_apptype_generator_unit.py, plus 87 across tests/vendor/ (excluding the live-network crawler- reachability test) and 55 across the TransportProtocol-consuming suites outside those directories.
…t minting (#860) Step 2 of #860 (PR 2 of 2, following AppType's 8 non-AppType siblings in #869). - Mix pcapkit.const.reg.apptype.apptype.AppType into EnumRegistry, alongside its four transport subclasses (TCP/UDP/SCTP/DCCP). AppType keeps its own get/get_all/register_alias, which already dispatch by transport protocol, and gains a working register (previously absent -- the base's generic one would have built a member with svc='<null>') and an _unregistered_member override reconstructing svc/port/proto. - Convert AppType._missing_'s 766 range-bounded extend_enum calls, plus the one more inside get()'s own second mint site, to _unregistered_member -- owner ruling: "only IANA registered ones are legit values ... get will not have sufficient information to create new ones." All 766+1 branches convert uniformly, including the 8 that named a real (if span-assigned rather than individually declared) service, since none of them mints at import time -- unlike FEATCode's earlier fix, there is no self-mutation defect here to address by declaring members statically. - Move TransportProtocol from power-of-two values to sequential ones, per PR #836's ruling retiring `|`-composite decoding and the owner's further #860 ruling that the spacing itself then had nothing left to protect; delete the stale comment claiming the values must stay power-of-two. undefined is declared as an explicit cast('TransportProtocol', 0) and tcp/udp/sctp/dccp continue from it via plain auto(), per the owner's own final ruling on the declaration shape ("undefined direct uses 0. then other real transport use auto. so we don't have to define a _start_ and the undefined declaration is explicit") -- an earlier revision of this same change used an explicit _start_ = 0 with every member on auto(), which the owner's ruling superseded. AppType._dispatch treats a composed or bare int identically as a whole either way, so the renumbering changes what specific integers mean, not only what hand-composed ones do -- e.g. a bare, uncomposed 4 (previously sctp's value) now silently resolves as dccp's, with no exception, since a real member sits at 4 under either numbering. No test pins any of this, per the owner's explicit instruction; the class comment states it instead, and every docstring that used to claim a hand-built composite is refused unconditionally (_dispatch's and get's own) is corrected to say it is looked up as a whole and resolves or is refused depending on whether some real member happens to equal it. - Update corekit/enum.py's own docstring, which described AppType's EnumRegistry mixin as future work ("stays as it is until tier two lands"), and its get() docstring's str-valued-registry census (119 -> 124). - Update tests/vendor/test_vendor_reg_apptype_generator_unit.py's #770 pin: undefined is once again the one bare-literal-under-cast member among four auto()-valued siblings, exactly as #770 first shaped it, so its wrapper is load-bearing against a mypy error again -- measured directly, stripping it alone (leaving auto() elsewhere) now reproduces 3 [assignment] + 762 [arg-type] errors (765 total), while stripping tcp's wrapper instead stays clean. The two separate changes behind the 3-not-4 count: PR #836 widened get()/get_all()'s own proto annotations to tolerate a bare int default before this issue touched anything, and this issue's own new _unregistered_member site is what brought the count back up from 2 to 3. - Fix four tests in the existing suite that pinned the old minting/power-of- two behaviour directly (test_const_apptype_split_unit.py, test_const_enum_builtin_parity.py), and add 17 new tests plus prose/count updates in test_const_enum_no_mint.py and test_const_registry_protocol.py. All 5 crawlers regenerate byte-identically on a second run; git status is clean relative to this commit. mypy/pylint deltas are the same classes of finding this codebase already tolerates elsewhere (aenum stub gaps, import-outside-toplevel/protected-access/no-member in test internals), suppressed with # type: ignore[override]/pylint: disable=arguments-differ, arguments-renamed where the codebase's own convention already does so. 216 test methods pass across test_const_apptype_split_unit.py, test_const_enum_no_mint.py, test_const_enum_builtin_parity.py, test_const_registry_protocol.py and test_vendor_reg_apptype_generator_unit.py, plus 87 across tests/vendor/ (excluding the live-network crawler- reachability test) and 55 across the TransportProtocol-consuming suites outside those directories (tests/corekit/test_fields_numbers_port_option_ no_mint_unit.py, tests/dumpkit/test_nameless_enum_rendering_unit.py, tests/dumpkit/test_common_unit.py, tests/foundation/registry/ test_protocols.py, tests/utilities/test_compat.py).
… them, per #719 tests/ is exempt from the issue-citation rule (documentation.rst, ruled on #719): the fact cited lives in the pull request, not the issue. - PR #836 restored for the TransportProtocol-extension refusal, the |-composite decoding retirement, and the stale-comment deletion; the rulings are not on #808 at all. - PR #783 for the f-string convention; PR #847 for the mint criterion. - The de-quotation stands: wording stays as statements, no quotation marks.
…quest (#719) (#982) * docs(pcapkit,ci): cite the issue a defect belongs to, not the pull request (#719) Per the owner's ruling on #719, replace every reference to a pull-request number in pcapkit/** and .github/workflows/** comments and docstrings with the issue it closed, or a description where no issue covers it. - 92 real PR citations in pcapkit/ (93 was the estimate; the gap is RFC packet-diagram and hex-format-spec false positives, plus one cross-repo issue citation that only coincidentally matched a PyPCAPKit PR number). - 13 PR citations in .github/workflows/, matching the estimate exactly. - Several citations named two or three numbers for one claim where a PR closed several issues, or several PRs closed the same issue; deduplicated rather than left reading "#425 and #425". Four review rounds caught the same category error recurring: several sites had relocated a verbatim quote or a specific finding into the issue number rather than describing where the ruling was actually given, so the quote no longer existed where the sentence pointed. Fixed each by naming the issue while locating the ruling honestly -- "a ruling given in review of the work for #N" -- the same shape already used on this repo's conventions docs. Two sites needed the inverse correction instead: the #923 quote in enum.py/exceptions.py genuinely is recorded on #923's own thread, just attributed there to the pull request that implemented it, so those read "a ruling recorded on GitHub issue #923" rather than pointing elsewhere. Also fixed a lost conjunction and an ordinal/number mismatch in corekit/enum.py, a self-contradicting below/above pointer repeated across three internet/ files, and a number collision in http.py where one issue ended up naming both a defect and the change that closed it. Final sweep: grepped the whole tree for the word "verbatim" -- the marker that makes a quote-attribution claim falsifiable -- across all 30 files under pcapkit/ that carry it, and checked every quote this way names against the actual issue thread. Caught two more of the same defect: vendor/__main__.py's #872 citation (the quote is in the implementing pull request's review, not #872 itself) and four sites across mh.py attributing to #935 a ruling that only exists in the review of the pull request that implemented it -- #935's own thread holds just the superseded widen-not-delete proposal. Both fixed the same way. Every other quote-bearing claim the sweep found -- #911, #937, three distinct #877 quotes, both #842 quotes, and the #860/#808/#806/#886/#917 rewrites from earlier in this pass -- resolves to the thread it names. Verified: targeted pytest across every touched module passes, including the test that pins the vendor/const apptype.py get() region as byte-identical, reconfirmed after each amendment. Both edited workflow YAML files parse before and after with unchanged key counts. * docs(corekit): cite #719, not #937, for the AbsentType ruling AbsentType's docstring attributed the owner's "document it as private type/class... not for public use is enough" quote to #937. #937 itself quotes that ruling verbatim under "The owner's ruling, verbatim (from #719)" -- it re-attributes rather than originates it. Per the house rule to cite the issue a ruling was settled on (docs/source/contributing/conventions/documentation.rst:196-200), point the attribution at #719 and re-wrap the paragraph to the file's existing ~78-column width. The neighbouring, unrelated #937 citation describing what #937 did to sentinel naming is untouched. tests/corekit/ passes (400 passed, 16 skipped) against this worktree's own pcapkit (confirmed via pcapkit.__file__); pylint on the file is 9.77/10, unchanged by this edit -- the one finding is a pre-existing, unrelated too-few-public-methods warning on NoValueType. * docs(tests): re-point six ruling citations at their actual threads, per #719 Swept tests/ for owner-ruling quotes attributed to the wrong GitHub thread, the same defect class #719 fixed under pcapkit/. Confirmed each by grepping the quote's distinctive text against the cited thread's body/comments; a hit elsewhere is a re-quote or a different thread's own words, not the source. - test_sentinel_exports_unit.py: the already-reported #937->#719 fix for AbsentType's privacy ruling. - test_enum_lookup_reparent_930_unit.py (4 sites) and test_mh_unit.py: "I prefer (2) directly" and the question that drew it are in pull request #940's thread, not issue #935 -- #935 only carries the first ruling ("I lean on 1"). - test_vendor_snapshot_restore_unit.py: the contextlib/atomic-write ruling is in pull request #873's thread; issue #872 has zero comments. - test_vendor_reg_apptype_generator_unit.py (2 sites): the "undefined direct uses 0" ruling is in pull request #874's thread, not issue #860 or #770. - test_const_enum_no_mint.py (2 sites): the mint/unmint criterion was settled on pull request #847 and confirmed on #775 -- the reverse of what the text said, per #861's own description of the same ruling; and "Q1 - bare it is." is pull request #838's thread, not #775's. One occurrence left unresolved rather than guessed at: the "Preserve each branch's existing name argument..." quote (4 sites in test_const_enum_no_mint.py, attributed to "#775's final round") does not appear verbatim in #775, #847, or #878 (the implementing PR) by body, comments, review comments, or commit message -- only a paraphrase in #878's own PR description/commit message, which is the author's prose rather than a quoted ruling. Flagged for the owner rather than fixed. tests/corekit/, tests/vendor/, tests/const/ pass (400/16, 118, 299 respectively, pcapkit.__file__ confirmed inside this worktree); tests/protocols/internet/test_mh_unit.py passes standalone (52/0) -- the full directory has 5 unrelated pre-existing failures from ungenerated examples/captures/ fixtures, untouched by this change. Refs #719 * docs(tests): paraphrase four fabricated or altered owner quotations (#719) Per #719's citation ruling (de-quote, never reproduce a verbatim quote that may have come from outside GitHub): - test_const_enum_no_mint.py (4 sites): a quotation attributed to "the owner's ruling, verbatim" never appears in #775, #847, #861 or #878 (or anywhere in the repo's comment corpus). Replaced with a paraphrase attributed to PR #878's own body, which carries the real design note in different words. - test_sentinel_exports_unit.py / test_const_registry_protocol.py: a quote attributed to #911 silently dropped half of what the owner wrote on #719 and swapped `__all__` for "users". Replaced with a paraphrase naming #719 as where it was settled and #911 as the issue that carried it out. - test_const_enum_no_mint.py / test_const_enum_builtin_parity.py (4 sites): a "verbatim" quote of #860 silently corrected the owner's typo ("entires" -> "entries"). Paraphrased, which drops the question of reproducing or flagging the typo. - test_enum_lookup_reparent_930_unit.py: "the owner's final ruling there" had #935 as its nearest antecedent instead of #940; named #940 explicitly and paraphrased the adjacent quote. Verified: ast.parse and reST markup pairing clean on every touched file; tests/const (299 tests) and the targeted pytest sweep of all touched files (261 passed, 2013 subtests) are green. tests/corekit's full discover run shows 5 pre-existing failures in test_sentinel_exports_unit.py, confirmed identical on the unedited originals -- a cross-file test-order dependency unrelated to this change. * test(vendor,corekit): fix a surviving fabricated ruling and a wrong citation (#719) - tests/vendor/test_ipx_socket_unit.py: the "owner's ruling" attribution for keeping the hex-suffixed Xerox name survived in this file after the prior commit removed the same false attribution from four sites in test_const_enum_no_mint.py. Reworded to credit PR #878's own design note, matching the wording already used at the repaired sites. - tests/corekit/test_sentinel_exports_unit.py: the docstring cited the #719 export ruling ("only export objects, not types") as grounds for keeping ABSENT out of __all__, but ABSENT is an object, so that ruling argues for including it, not excluding it. Re-grounded the sentence on the privacy ruling already quoted ~15 lines below instead, without re-quoting it. Both changes are prose-only: tokenizing each file before and after with comments and docstrings stripped produces identical token sequences. tests/vendor passes 118/118 except one pre-existing, test-order-dependent flake in test_vendor_snapshot_restore_unit.py (reproduces identically on the pre-edit tree); tests/project/test_conventions_doc_claims.py passes 38/38. * test(corekit,const): narrow the blanket paraphrase, restoring quotations that cite correctly (#719) The last two commits paraphrased every disputed owner quotation away. That was right for one case and wrong for two: a quotation that exists nowhere has to be paraphrased, but a quotation that is real and was only cited to the wrong thread lost its audit trail for nothing, since the defect was the pointer, not the words. Per the owner's ruling, narrow the fix to match. Restored as quotations, correctly cited: - tests/corekit/test_sentinel_exports_unit.py (~L4-7) and tests/const/test_const_registry_protocol.py (~L1363): the sentinel export rule, split back into its two real sources instead of one spliced sentence -- #719's "we should ONLY export the objects ... and leave the types ... out", and #911's own "we only expose the final objects to users", with #911 noted as both executor and source. - tests/const/test_const_enum_no_mint.py (~L88, ~L1876, ~L2363) and tests/const/test_const_enum_builtin_parity.py (~L655): the #860 minting ruling, including its load-bearing first sentence ("I think we should not mint on get still actually") and the owner's own "entires" typo, marked [sic] rather than silently corrected. Left alone: the four #878 fabricated-quote sites in test_const_enum_no_mint.py, which cite no real thread and stay paraphrased, and the ABSENT privacy sentence, which is a correct paraphrase of a different ruling. Verified: ast.parse and reST markup clean on all four files; code token sequences (docstrings/comments stripped) identical before and after; each restored quotation substring-matches its source comment after whitespace/markup normalisation. tests/const: 299 OK. tests/ project/test_conventions_doc_claims: 38 OK, 1 skipped. * test(corekit,const): convert restored quotations to statements with context, per #719 The previous commit restored eight verbatim quotations to fix a narrowing that had dropped their context. The owner has since ruled that neither form is right: a narrowed paraphrase without context does not help a reader who was not in the thread, but a verbatim quotation makes the docstring read as a discussion rather than documentation. - Sentinel export rule (corekit/test_sentinel_exports_unit.py, const/test_const_registry_protocol.py): state that a module's `__all__` lists a sentinel's object but deliberately leaves its type out, and why (the type is not part of the public surface), citing #719 as where it was settled and #911 as where the implementing work belongs. - #860 minting rule, four sites (const/test_const_enum_no_mint.py x3, const/test_const_enum_builtin_parity.py): state that `get()` must not mint and only `register()` creates a new entry, and why (only IANA-registered values are legitimate and `get()` lacks the information to construct one), citing #860. Each site is fitted to its own surrounding prose rather than one paragraph pasted four times. Drops the `[sic]` each quotation carried, since there is nothing left to reproduce. - Fixed two sentences left orphaned by the quotations' removal: an antecedent ("the three") that depended on the deleted quote's wording, and a sentence whose "get() as well as _missing_" had the emphasis backwards relative to the rule's own subject. The four PR #878 paraphrase sites in test_const_enum_no_mint.py were already in this third form and are unchanged. Verified: ast.parse on all four files; tokenize with comments and docstrings stripped shows an identical token sequence before/after (prose-only); tests/const (299) and tests/project/test_conventions_doc_claims.py (38, 1 skip) pass; tests/corekit (400, 5 failures, 16 skipped) matches the documented pre-existing sentinel-identity failures. * test(corekit,const): state the remaining owner rulings in our own words, per #719 The four files still carried owner-attributed quotations beside the eight converted earlier, so each read half as documentation and half as a thread. - Replace each quoted ruling with a statement of the rule, the reason a reader needs, and the issue where it was given (#842, #864, #775, #860, #911, #719, #647, #808, #759, #857). - Rename the dangling "privacy ruling quoted below" reference to point at the statement that replaced the quotation. - Leave RFC text, code literals and ordinary prose untouched. Prose only: tokens with comments and docstrings stripped are identical before and after; tests/const 299 OK, tests/corekit unchanged (5 known). * test(const): restore ruling citations to the pull requests that carry them, per #719 tests/ is exempt from the issue-citation rule (documentation.rst, ruled on #719): the fact cited lives in the pull request, not the issue. - PR #836 restored for the TransportProtocol-extension refusal, the |-composite decoding retirement, and the stale-comment deletion; the rulings are not on #808 at all. - PR #783 for the f-string convention; PR #847 for the mint criterion. - The de-quotation stands: wording stays as statements, no quotation marks.
Please follow the guide below
make mypy,make isort; ran both directly against the changed files)make testpasses, and a test case covers the change (scoped totests/const/,tests/foundation/registry/,tests/vendor/,tests/dumpkit/per the affected trees)docs/source/changelog/— N/A, changelog centralised in docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620) #657What is the purpose of your pull request?
fix— corrects a defectDescription of your pull request and other information
Closes #808. Unblocked by #806, which merged as #815 and retyped every member's
prototo a single transport — leaving nothing that builds or relies on a compositeTransportProtocolvalue. The premise was verified with an AST sweep (validated against a known-positive control) plus the maintainer's own AST-sweep test before anything was touched.This is a breaking change.
TransportProtocol's base moves fromaenum.IntFlagto a plainaenum.IntEnum, so&,^,~and|'s member-composing behaviour are gone —tcp | udpfalls through toint.__or__and returns a bareint. The four transports keep their exact existing values (tcp=1,udp=2,sctp=4,dccp=8,undefined=0), not renumbered._missing_is removed: a plainIntEnumalready rejects an undeclared value, soTransportProtocol(3)now raisesValueErrorwhere it used to composetcp|udp. Also breaking and easy to miss:list(TransportProtocol)now yields all five members instead of four, becauseFlaghid the zero-valuedundefinedfrom iteration and plainIntEnumdoes not. Every member's ownrepr/str/.name/.valuestay byte-identical, and nothing inpcapkititerates the class bare — only through__members__, which is 5 either way.Two maintainer rulings on this PR shaped the rest of it, beyond what #808 asked for.
First: "Do not allow extension of TransportProtocol at all."
TransportProtocol.getno longer mints on a miss — theextend_enumcall and themax_valcomputation are gone, and an unrecognised name raisesValueError. That closes a pre-existing defect on the way past: because.get()lowercases but does not strip,tcpandtcpused to mint permanent members literally named' tcp'and'tcp '. They now raise. Whether to strip was considered and ruled against, to keep the convention engine selection already sets atextraction.py:922, which lowercases only.Second: "since it's no longer a Flag,
|joined values are no longer parsed and accepted, we will treat it as a whole, instead of splitting."AppType._dispatchno longer decomposesprotothroughshow_flag_values; a value that names no registry is refused whole.show_flag_valuesandProtocolErrorare consequently unused here and their imports are dropped.User-visible exception-type change.
AppType.get(80, proto=3)andproto=17both raisedProtocolErrorbefore and both raiseValueErrornow —3 names no transport protocol registry of AppType. A bareintwas off-contract before this PR (proto: TransportProtocol | str); the annotation now includesint, so this is a change on a newly supported input rather than a regression. Two smaller moves in the same direction:proto=Noneandproto=2.5raisedTypeErrorbefore — from~inside the bit decomposition — and now raiseValueError. One move the other way, and the only input anywhere that went from raising to resolving:proto=1.0andproto=2.0raisedTypeErrorbefore and now returnhttp [80 - tcp]andhttp [80 - udp], becausehash(1.0) == hash(TransportProtocol.tcp)and the registries dict is int-keyed. Afloatis off-contract either way; stock's rejection was an accident of~and the new acceptance is an accident of dict hashing, so neither is designed behaviour.pcapkit/foundation/registry/protocols.pyis touched for one comment only, no behaviour: its NOTE justified preferring__members__overTransportProtocol[...]on the grounds thatFlag.__getitem__silently composes'tcp|udp'into3and that the class is anIntFlag— both false once the base is dropped. The guard itself is still right and unchanged:__getitem__raises a bareKeyErrorwhere this function's contract isRegistryError, and a bareintstill hashes and compares equal to its matching member.Untouched: the four per-transport generated registries. Re-pinned after measuring rather than assuming:
tests/dumpkit/test_nameless_enum_rendering_unit.py's sweep at 7→6 flag registries and 4→3_missing_field widths, withlen(nameless)deliberately left at 5 becauseTransportProtocolwas never in that set.Applied identically to the vendor generator template, with the rendered output diffed against the committed const file to confirm they still agree byte-for-byte.
Tests: an unrecognised name refused rather than minted, a
|-spelled string refused as an ordinary unknown, a bare-int composite refused whole,TransportProtocolno longer extensible at runtime, and the base/value/iteration-count pins — each checked to fail before the change and pass after.