fix(const): normalise the key get() looks up, and honour its documented default (#582, #583, #584) - #596
Conversation
|
❌ NEEDS CHANGES — head |
Cross-review appendix — PR #596Reviewer: Sonnet; PR authored on Opus 5. Reviewed at head
|
| test | reverted | fixed |
|---|---|---|
test_the_reported_case_returns_the_default |
exit 1 | exit 0 |
test_the_placeholder_still_raises |
exit 0 | exit 0 |
test_the_two_unverified_enums_from_the_issue |
exit 1 | exit 0 |
test_every_integer_path_consults_the_default |
exit 1 | exit 0 |
test_the_vendor_template_still_emits_the_fix |
exit 1 | exit 0 |
test_the_sweep_size_is_pinned |
exit 0 | exit 0 |
test_command_get_is_case_insensitive |
exit 1 | exit 0 |
test_ftp_read_parses_a_lowercase_request |
exit 1 | exit 0 |
test_method_get_is_case_insensitive |
exit 1 | exit 0 |
test_httpv1_method_regex_is_anchored |
exit 1 | exit 0 |
test_httpv1_read_header_uses_the_captured_method |
exit 1 | exit 0 |
9 of 11 fail without the fix, exactly as claimed, and the two that pass either way (test_the_placeholder_still_raises, test_the_sweep_size_is_pinned) are the ones that should, by design.
Pre-existing bug disclosure (found, not fixed)
Verified independently: BindingACKFlag(0x01) and (0x99) raise RecursionError on both this branch and main — confirmed pre-existing and unchanged, not introduced by this PR. BindingACKFlag(0x04) resolves fine, matching the claim that it's the undefined-combination case that recurses.
Changelog regeneration
python util/changelog_md.py --check exits 0 against the committed tree. Regenerated and diffed against main's CHANGELOG.md: exactly 3 bullets added (matching "three bullets"), no other line touched.
Regression
Scoped run (tests/const/ tests/protocols/application/ tests/protocols/transport/, not the whole tree): first attempt showed 12 failures, all FileNotFoundError: sample capture ... not found — a fresh worktree has no generated fixture captures, exactly the trap flagged for this repo. Ran python examples/generators/make_samples.py, reran: 201 passed, 338 subtests passed, exit 0.
Not independently checked
pcapng.OptionType's bespoke fallback was read but not exercised at runtime (low risk: untouched by the diff either way).- The exact coverage percentages in the PR's table were not re-derived (the pass/fail test evidence above is the load-bearing check; coverage deltas are secondary).
Disagreement log
One real, specific defect: the RFC 959 citation (section 4.1 → should be section 5.3), present in the commit message, PR body, changelog bullet, and test docstring. Everything else — the code mechanism for all three issues, the test evidence, the skip/never-raise scoping, the RFC 9110 citation, the pre-existing-bug disclosure, and the changelog regeneration — checked out under independent re-derivation.
aab3350 to
0b5eff9
Compare
0b5eff9 to
33692df
Compare
|
Re-pointing my verdict — PR #596 My prior review (verdict + appendix, both at 23:17Z) was pinned to the pre-rebase head Verified rather than assumed: The one defect I opened NEEDS CHANGES for is also unchanged. Confirmed by fetching the new head directly: ❌ NEEDS CHANGES — head |
33692df to
22a31ef
Compare
|
Re-pointing again — PR #596, head This is not a pure rebase this time — real content was added since Two genuine additions, both checked:
But this new addition introduces its own citation error, of the same species as the one already open. The new comment says *" ❌ NEEDS CHANGES — head
Both are one-line text fixes; the code, tests, and CI are otherwise sound — the new |
22a31ef to
04aa920
Compare
|
Re-pointing — PR #596, head Two things happened since my last comment ( 1. The RFC 959 citation is fixed. 2. The head then moved again to One item from my previous comment is still open: the ❌ NEEDS CHANGES — head |
04aa920 to
0afb705
Compare
0afb705 to
bf4d0c9
Compare
… own (#606) The stacklevel probes record with `simplefilter('always')`, which un-ignores the categories Python hides by default, then asserted their window held exactly one record and took the last of it. A `ResourceWarning` for a file handle an earlier test left for the collector therefore failed them, and did so unpredictably: the same failure was watched migrating between #596 and #600 nine minutes apart with neither branch touched and neither head moved, so what trips is decided by garbage-collection timing and collection order rather than by any test's code. It had reddened #577, #596 and #600 by then, none of which the test exercises. * `tests/utilities/test_stacklevel.py`: count the window's pcapkit warnings via a new `emissions()` helper and check the one found is the expected category, instead of requiring the probe's to be the only record there and picking it by position. Counting over `BaseWarning` rather than the exact category keeps what the old assertion did buy -- a second complaint from the package is still a failure -- and gives up only its sensitivity to warnings pcapkit never raised. Both probe helpers were affected, `warning_site()` and `emit()`. Adds a regression test that frames the probe with foreign warnings on both sides. * `tests/utilities/test_logging.py`: enter each `Extractor` as a context manager so `__exit__` closes the input file, removing the leaked `in.pcap` handle these three constructions were shedding into sibling modules' tests. `pytest tests/utilities/` 103 passed, and with `-W always::ResourceWarning` the unclosed-`in.pcap` warnings go from 2 to 0. The new test fails `3 != 1` without the fix. The leak's root cause is left alone deliberately: `Extractor._cleanup()` closes the handle only when the caller supplied the stream, never when pcapkit opened it, and that is production code wanting its own review. Fixes #606
…ed default * pcapkit/const/ftp/command.py, pcapkit/const/http/method.py: get() and _missing_ tested membership with the raw key but registered key.upper(), so the first lowercase or mixed-case token raised TypeError rather than resolving. Both now look up under the canonical upper-case name they register. FTP commands are case-insensitive per RFC 959 section 5.3 and this is reachable from wire data, so `retr file.txt` now parses (#582, #583). * pcapkit/protocols/application/httpv1.py: _RE_METHOD was unanchored, so re.match prefix-matched b'Get' down to b'G', and the reader then passed the whole para1 instead of the captured group. The pattern is anchored at both ends and match1.group('method') is what is looked up, so a token that is not a method is a malformed request line rather than a mis-parsed one. _RE_STATUS carried the same defect and leaked a bare ValueError out of int(para2) where the reader documents ProtocolError; it is anchored too, per RFC 9112 section 4's `status-code = 3DIGIT` (#583). * pcapkit/const/** (113 modules) and pcapkit/vendor/** (9 templates): get() dropped the caller's default on the integer path. It now consults default before letting the lookup error escape, keeping -1 as the "no default" placeholder so a caller that asked for no fallback still gets the error. pcapng/option_type.py and reg/apptype.py are left alone: their integer paths resolve every value already, so neither drops a default by raising (#584). * tests: 12 new cases in tests/const/test_const_enum_get.py, test_ftp_unit.py and test_http_unit.py; 10 of them fail without these fixes. Scoped test runs green: 310 tests, 1319 subtests, no EXPECTED_FAILURES entry started passing. Coverage rose on every changed file.
bf4d0c9 to
a79c77e
Compare
|
✅ GOOD TO MERGE — head Re-verifying the coordinator's reading of the two excluded templatesRead both files directly rather than accepting the description.
if key in temp_ns:
return temp_ns[key]
return extend_enum({NAME}, '%s_unknown_%d' % (namespace, key), key, '%s_unknown' % namespace)The constructed name embeds the key's own value (
Confirmed both files are genuinely untouched: The two previously-open citation issuesRFC 959 §4.1→§5.3 (FTP case-insensitivity): fixed several heads ago, confirmed still correct at this head. RFC 9112 vs 9110 ( CIRollup All prior findings from my earlier appendices on this PR (the |
Fixes #582
Fixes #583
Fixes #584
Are these one fix or three?
Three root causes, delivered as one PR, because two of them are the same defect in two copies of one generated template and the third shares the same file set.
key.upper()pcapkit/const/ftp/command.py:297-298(+_missing_at:310)pcapkit/const/http/method.py:175-176(+_missing_at:187)_RE_METHOD, and the whole token passed instead of the captured grouppcapkit/protocols/application/httpv1.py:57and:292Enum(key)directly, sodefaultis never consultedpcapkit/vendor/default.py:86-87→ 113 generatedpcapkit/const/**modules#582 and #583 item 1 are one fix applied twice — the string-keyed template is duplicated in
pcapkit/vendor/ftp/command.pyandpcapkit/vendor/http/method.py. #583 item 2 is an independent parse defect. #584 is a third, in the other (integer-keyed) template.Yes, these are generated - which is why the vendor templates are in the diff
The fix went into the vendor templates as well as the generated tree.
pcapkit/const/**is generated frompcapkit/vendor/**, so patching only the committed tree would have left the nextpython -m pcapkit.vendorrun to silently revert all of it. Both sides were changed and a test now renders the template and compares it, character for character, against the module generated from it.The two vendor templates NOT in this diff, and why they are already correct
13 vendor modules define their own
get(); 11 are in this diff. The two that are not:pcapkit/vendor/pcapng/option_type.pypcapkit/vendor/reg/apptype.pyMeasured, not assumed - every integer I probed resolves, so there is no raise for a
defaultto rescue:Both reach that through their own bespoke
extend_enumfallback rather than the sharedEnum(key)call, so the #584 defect - the integer path raises whiledefaultis dropped - simply is not present.reg/apptype.pyalso carriesTransportProtocol.get(key), whose signature has nodefaultparameter at all, so the rewrite would have produced aNameErrorthere; that is the one the script's signature check caught.Crucially, neither file is touched by this PR - the diff's file list contains neither - so there is nothing in them for a regeneration to revert. Their templates and generated modules agree as they stand: rendering each template's own
LINElambda offline reproduces the generatedget()blocks character for character (pcapngOptionType's single block, and both ofreg/apptype.py's).One honest caveat on
defaultin those two: it is still unused on their integer paths, so the documentation imprecision #584 complains about persists there. It is not the same bug, because no caller is ever denied a fallback - the lookup always succeeds. Left alone deliberately, andAppType.getespecially: it is the hottest of the set (#575's profiling measured 111extend_enumcalls in a single extraction) and sits on the per-packet path viapcapkit/protocols/transport/transport.py:165, so it deserves its own change with its own performance measurement rather than a drive-by edit.On regenerating
Direct answer: no, I did not verify byte-identical regeneration, because I could not run the crawlers. The detail is below rather than a claim that it passes.
I could not run
python -m pcapkit.vendorto prove the 113 generated modules come back byte-identical: the crawlers fetch IANA and Wikipedia over the network, and several are known-broken (#518 records 4 Wikipedia 403s and 3 pointing at a dead IETF URL), so a run would fail for reasons unrelated to this change and could rewrite modules from newer registry data. Saying that plainly rather than claiming a check I did not perform.What I did instead, offline and for all 13 templates with a
get(): render or expand each template and compare itsget()block against the module generated from it. All 13 agree. Forpcapkit/vendor/default.pythis is also pinned as a test (test_the_vendor_template_still_emits_the_fix), which calls the realLINElambda and asserts equality withpcapkit/const/arp/hardware.py- so a regeneration that dropped the fix would turn the suite red rather than pass quietly.#582 / #583 item 1 — the key that was looked up was not the key that was registered
'retr'misses the map (which holdsRETR), soextend_enumis called for a name that already exists:Wire-reachable.
pcapkit/protocols/application/ftp.py:36compiles the request pattern withre.Iandftp.py:99passes the match verbatim, and RFC 959 section 5.3 makes FTP commands case-insensitive — soretr file.txtwas a valid request this library could not parse.Both
get()and_missing_now resolve under the canonical upper-case name._missing_carried the identical mismatch and was fixed with it:Command('retr')raised the sameTypeErrorbefore.Resolving to the existing member, rather than registering a second one beside it, is load-bearing beyond not crashing — a duplicate
GETwould carry neither thesafenor theidempotentattribute of the real member.#583 item 2 — both halves, because either alone is still wrong
_RE_METHODwas unanchored andre.matchanchors only at the start:So
b'Get'madematch1truthy on one character, the request-line guard passed, and then the whole mixed-case token went toMethod.get.Fixing one half alone leaves a wrong answer, exactly as #583 says: normalising
Method.getalone parsesb'Get'asGEToff a one-character match; passing the captured group alone parses it as a method namedG. Both are in this PR. The pattern is anchored with\Zandmatch1.group('method')is what is looked up._RE_STATUStoo — found by auditing_RE_METHOD's siblings_RE_STATUS = re.compile(rb'\d{3}')(httpv1.py:64) carried the same unanchored-prefix defect, in the same function, and it escaped as the wrong exception type. That pattern is only a guard — the value comes fromint(para2)on the raw token — so a prefix match let a malformed status past the guard and then out ofint()uncaught, where_read_http_headerdocumentsRaises: ProtocolError. Measured on the pre-fix tree:Both are now
ProtocolError. RFC 9110 section 15 givesstatus-code = 3DIGIT, exactly three, so the anchor is what the grammar already said._RE_VERSIONwas audited at the same time and is safe as it stands, because both of its call sites read the captured group rather than the raw token.No
re.Iwas added, deliberately. Method tokens are case-sensitive per RFC 9110 section 9.1, soGetis notGET; it is now a malformed request line (ProtocolError) rather than a mis-parse. Measured after the fix:#584 — the sweep the issue asked for, and what "placeholder" means
get()delegated to the enum call, and_missing_cannot see the caller'sdefault. The integer path now consults it:-1is the placeholder the generated signature already carried, and it is what separates no default was supplied from a default was supplied and should be used. Keeping it means no existing caller changes behaviour — nothing in the library or the test suite passes a positionaldefaultto an integer-keyedget()— and a caller that asked for no fallback still gets the error rather than a silent substitution. That guard has its own test, so a future "fix" cannot quietly turn every failed lookup into a fallback.One thing to read carefully: #584's literal repro still raises, and that is the correct outcome. The default is now consulted;
'X'is simply not a valid default for an enum whose signature saysdefault: 'int'. What changed is which value is rejected, which is the observable proof that the default is reached at all:Scope, measured rather than assumed. #584 said "worth a sweep before fixing" and named
Hardware,OperationandLinkTypewith onlyHardwareverified. Sweeping all 118IntEnum/IntFlagregistries underpcapkit/const:default— the defect was live in all of them, and all 110 are fixed and asserted.ProtectionAuthority,CGAType,tcpFlags) — they auto-extend the whole integer space, so there is nothing for a default to supply.get(key, default)at all.regTransportProtocolis the interesting one: itsgethas an integer path of exactly the shape being rewritten but nodefaultparameter, so a blind pattern rewrite would have produced aNameError. The rewrite checked the signature, not the body.Two registries were deliberately left alone:
pcapngOptionTypeandregAppType. Both already have a bespoke integer fallback that resolves every value, so neither drops a default by raising, andAppType.getis on the per-packet path viapcapkit/protocols/transport/transport.py:165. #584's unverifiedAppTypedouble-extend claim is therefore not addressed here — see below.Not reachable from wire data, as #584 says:
Hardware(40)returnsUnassigned_40, so every value a 16-bit field can carry already resolves. This is a contract fix, not a parse fix.A second citation correction
The first draft of the
_RE_STATUSfix cited RFC 9110 section 15 forstatus-code = 3DIGIT. Wrong: that production is not in RFC 9110 at all. It is RFC 9112 section 4 ("Status Line"), becausestatus-codeis part of HTTP/1.1'sstatus-line = HTTP-version SP status-code SP [ reason-phrase ]; RFC 9110 section 15 covers what the codes mean and the IANA registry.Verified against the primary sources rather than accepted: fetched both RFCs and grepped them.
status-code = 3DIGITappears at RFC 9112 line 673, inside section 4 (heading at 645, section 5 at 695), and again in its collected ABNF at line 2106. It appears nowhere in RFC 9110. RFC 9112 settles the division itself, in the paragraph immediately after the production: "HTTP's core status codes are defined in Section 15 of [HTTP], along with the classes of status codes, considerations for the definition of new status codes, and the IANA registry for collecting such definitions."Corrected in three places — the changelog bullet (and so the regenerated
CHANGELOG.md), the comment above_RE_STATUSinpcapkit/protocols/application/httpv1.py, and thetest_httpv1_status_regex_is_anchoreddocstring.Deliberately not blanket-replaced, for the same reason as the RFC 959 fix:
:rfc:9110#section-9.1`` citations for method case-sensitivity are correct and untouched — that rule genuinely is RFC 9110.:rfc:9110#section-15.x`` citations inpcapkit/const/http/status_code.pyare correct and untouched — those are per-code semantics, which is exactly what section 15 is for.Both citation errors in this PR came from the issue text and were caught by cross-review rather than by me; recording that rather than quietly fixing them.
Evidence: failing before, passing after
Each new test run alone, verdict taken from the process exit code written to a file — this pytest has no
pytest-subtests, so a failing subtest's parent still printsPASSED.Against the unfixed tree (sources reverted, new tests in place):
Underlying failures, verbatim:
TypeError: 'RETR' already in use as <Command.RETR: Retrieve>,TypeError: 'USER' already in use as <Command.USER: User Name>,TypeError: 'GET' already in use as <Method.GET>,ValueError: 99999 is not a valid Hardware,AssertionError: b'G' != None,AssertionError: b'GET' != None.With the fix:
10 of 12 fail without the fix. The two that pass either way are the ones that should:
test_the_placeholder_still_raisespins behaviour the fix must preserve, andtest_the_sweep_size_is_pinnedis a structural pin.Coverage
Same scoped run, before and after (
coverage run, neverpytest-cov). Every changed file rose, despite each gaining 4-5 statements:pcapkit/const/arp/hardware.pypcapkit/const/arp/operation.pypcapkit/const/ftp/command.pypcapkit/const/http/method.pypcapkit/const/reg/linktype.pypcapkit/protocols/application/ftp.pypcapkit/protocols/application/httpv1.pyRegression runs
All scoped, never the whole tree. Exit codes read from files.
tests/test_docstring_contract.py+tests/const/tests/protocols/test_option_roundtrip_unit.py+tests/project/tests/protocols/application/+transport/tests/protocols/internet/+misc/+schema/Two worth calling out:
EXPECTED_FAILURESis untouched. No entry started passing, so none is deleted. Its 46 entries were read by importing the table (it is built with**unpacking and cannot be grepped); only one concerns HTTP at all,httpv2-frame/PRIORITY, and it is unrelated.test_docstring_contract.pypasses despite 113 generated docstrings changing. It walks every function underpcapkit/and cross-checksraiseagainst documentedRaises:; the new bareraiseneeded no clause, and thedefault:continuation lines sit at entry-depth + 4 so they are not mistaken for swallowed section headers. TheKNOWN_DEFECTSentries in the fourpcapkit/vendor/mh/*_flag.pyfiles andpcapkit/vendor/pcapng/option_type.pyare still defects — only the template strings in those files were edited, not theircontext()docstrings.Changelog: three bullets in
docs/source/changelog/1.5.0.rst,CHANGELOG.mdregenerated withpython util/changelog_md.py, and--checkexits 0.CI state
The one red check this PR ever had was not this change, and it is now fixed upstream.
Python 3.14(the full-tree unit suite) failed twice on a single test:The assertion at
tests/utilities/test_stacklevel.py:304expects exactly one captured warning and got two: the intended probe, plus a strayResourceWarningfor an unclosedexamples/captures/in.pcaphandle that the garbage collector finalised inside that test'scatch_warningsblock. Both times it was the only failure out of 1186 passed and 2858 subtests.Established as a pre-existing, order- and GC-dependent flake rather than assumed:
pcapkit/const/**,pcapkit/vendor/**,pcapkit/protocols/application/httpv1.pyand three test files. None opensin.pcap; none touches file handles, the warning machinery,stacklevelorsys.tracebacklimit. The one apparent hit —stacklevel=2inpcapkit/vendor/default.py— is at lines 520-601, whereas this change to that file is confined to theLINEtemplate string at lines 79-97.tests/utilities/test_stacklevel.pyis not in the diff at all.ResourceWarningneeds.It was filed as #606 and has since been fixed and merged as
4529fdb1f, which is in this PR's base after the last rebase. It was never papered over here: nofilterwarnings, no retry decorator, and no edit to that test in this PR.Found and deliberately NOT fixed
_missing_recursion in the four Mobility Header flag registries.pcapkit/const/mh/binding_ack_flag.py:73,binding_update_flag.py:88,handover_ack_flag.py:61,handover_initiate_flag.py:64each end_missing_withreturn cls(value), which re-enters_missing_forever for any in-range value that is not a defined combination. Measured on the pristine HEAD copy, so it is pre-existing and not introduced here:BindingACKFlag.get(0x01)andget(0x99)both giveRecursionError: maximum recursion depth exceeded, whileget(0x04)returns<BindingACKFlag.S: 4>— bit0x01is simply not a defined member.Not wire-reachable, verified by building real BU/BA/HI/HAck messages with the flags octet set to
0x01and to0xFFand parsing them throughMH(io.BytesIO(raw), len(raw), extension=True): all four parse cleanly.pcapkit/protocols/schema/internet/mh.py:2610,2639,2792,2821decode those octets withBitFieldover a per-named-bit namespace, which reads only the recognised bits and discards the rest, so these four enums are dead imports as far as the live parser is concerned.pcapngRecordTypeandSecretsTypeend with the same line but are safe, becauseextend_enumregisters the member first. No test references any of the four class names.Left out because it is a distinct defect in
_missing_rather thanget(), is not named by any of the three issues, and needs its own decision about what an undefined flag combination should return. Worth its own issue.Method.getsilently duplicates the two hyphenated methods.Method.BASELINE_CONTROLhas the nameBASELINE_CONTROLand the valueBASELINE-CONTROL, so the real wire token misses_member_map_andMethod.get('BASELINE-CONTROL')registers a second member namedBASELINE-CONTROLcarrying neithersafenoridempotent. Same forVERSION-CONTROL. This is name mangling at generation time (-→_), not case normalisation, and behaviour is unchanged by this PR ('BASELINE-CONTROL'.upper()is itself). Left alone because it needs its own decision — shouldMethod.get('BASELINE_CONTROL')work too? — and would otherwise widen this PR._RE_VERSIONis also unanchored (httpv1.py:59), soHTTP/1.1xmatches. Method.get raises TypeError on a mixed-case HTTP method, and httpv1.py:292 passes the whole token instead of the captured method #583 names only_RE_METHOD, and the version group is re-read from the match rather than from the raw token, so nothing mis-parses today. Not changed.http/status_code.py'sget()discardsextend_enum's return and re-looks-up withStatusCode[key]. Harmless —extend_enumregisters the member, so the second lookup finds the same object — and untouched beyond the integer-path fix.get()'s documented default is ignored on the integer path across the shared const/ enum template #584's unverified
AppTypedouble-extend claim is not addressed, for the reasons above:AppType.get's integer path already has its own fallback, it is on the per-packet path, and the claim was explicitly recorded as unverified.Cross-review
Reviewed by an independent agent on a different model (Sonnet; this change was authored on Opus).
Final verdict on this head (
bf4d0c9f9): GOOD TO GO, after three passes (aab3350a9,0afb70569,bf4d0c9f9). The first pass, againstaab3350a9, returned NEEDS CHANGES with three items — all now addressed, and all three independently re-measured by me rather than taken on the reviewer's word:0bd517a1c, both sides' bullets kept,CHANGELOG.mdregenerated,--checkexits 0, and GitHub reportsmergeable: MERGEABLE.test_every_integer_path_consults_the_defaultpermanently polluted two shared registries. Correct and confirmed by my own measurement —ProtectionAuthority8 members to 9,CGAType7 to 8, and it sticks. The reviewer also rightly noted that for those registries the probe asserted nothing about the fix, since theirtrynever raises. Fixed: the three always-resolving registries moved out of the sweep intotest_the_always_resolving_registries_have_nothing_to_fall_back_to, which probes them deliberately, andsetUpClassnow registersaddClassCleanup(purge_modules, ['pcapkit'])so the pollution is dropped by this module rather than incidentally by the next test'ssetUp. One correction to the reviewer's framing:tcpFlagsdoes not extend — it is anIntFlagand returns a pseudo-member (12 members before and after) — so the shared assertion is "resolves", not "extends"._RE_STATUSundisclosed. Correct, and worse than reported: as well as200x,b'HTTP/1.1 2000 OK'also escaped asValueError: 2000 is not a valid StatusCode. Fixed rather than merely disclosed, since it is the same defect class in the same function in a file this PR already changes, and it makes the reader honour its documentedRaises: ProtocolError.Two findings from the review worth recording, neither requiring a change:
b'GET! /x HTTP/1.1'andb'POST-DATA123 /x HTTP/1.1'. Under the old code the unanchored match madematch1truthy on a prefix and the whole token was registered, so these silently corrupted the globalMethodenum with members likeMethod.GET!. Rejecting them is strictly better, and it closes an unbounded-enum-growth-from-malformed-input path.tcp/flags.py,http/status_code.py,ipv6/extension_header.pyand all fourmh/*_flag.py) and reproduced 8 of the failing-then-passing cases from a scratch tree. It also confirmed via aenum'sEnum.__new__that a_missing_returning an existing member is not cached into_value2member_map_, so the registry is not warped — which I verified separately:_member_map_and_value2member_map_both grow by 0, andlist(Command)containsRETRexactly once.What the re-review verified on the final head
On the third pass it re-fetched both RFC texts itself rather than trusting the quotes, confirmed
status-code =hits RFC 9112 at lines 673 and 2106 (both inside section 4) and returns nothing in RFC 9110, checked from the diff that the new citation lands in exactly the three claimed places, and confirmed the two deliberate exclusions really are untouched — including thatpcapkit/const/http/status_code.py's only hunk is the integer-path fix, with no citation text altered. It re-ran--check(exit 0) and the scoped suite (45 passed, 138 subtests, exit 0) against the final head, and independently pulled #606 to confirm the CI flake disclosure is accurate.It re-derived rather than re-read, and could not falsify anything:
unittest.TextTestRunner, then checkedsys.modulesand re-imported.addClassCleanupfired,pcapkit.const.ipv4.protection_authoritywas gone, and a fresh import came back with 8 members (not 9) andCGATypewith 7 (not 8), with the class object identity actually different. It also confirmed from aenum's source that_create_pseudo_member_caches by value in_value2member_map_, which is whyFlags.get(1<<70) is Flags.get(1<<70, 0)holds without anyextend_enum— my correction to its first-pass framing checked out.assertEqual(int(resolved), UNRESOLVABLE)is real content, and because the threeget()calls are not wrapped inassertRaises, the test fails loudly if any of the three ever regresses into raising._RE_STATUS: confirmed both malformed statuses now raiseProtocolErrorandHTTP/1.1 200 OKstill parses to 200.rfc959.txtitself rather than trusting my report, and independently placed the sentence inside §5.3. It also noted §5.3's own text settles it — "The command functions and semantics are described in the Section on Access Control Commands... The command syntax is specified here."AppType.get(64999)andOptionType.get(64999)both auto-extend, 8182→8183 and 40→41 members), that a deliberately garbagedefault='not-even-an-int'passed toAppType.getis simply never consulted on that path, and then rendered each file's ownLINElambda with the realFLAGvalues read from the source rather than guessed and diffed theget()block byte for byte against the generated module — 1072 and 827 characters, identical.Notes for the reviewer
get()whose signature lackeddefaultand any integer path that was not the barereturn X(key)form. It reported 9 vendor templates and 113 const modules changed and 5 sites skipped, and those 5 skips are exactly the expected ones.origin/mainat4529fdb1f. Five rebases were needed as fix(changelog-md): cite a line the entry has, and accept an anchored :rfc: role (#588, #592) #595, fix(tcp): resolve the connection flags before building the options (#587) #597, fix(corekit): recompute _need_process from the width in force, not once from the placeholder (#591) #598, fix(corekit): size the width repair with a real ceiling, not a floored one (#599) #600 and test_stacklevel asserts an exact warning count, so a leaked in.pcap handle fails it on unrelated PRs #606 landed during this work; four of them conflicted on the shared changelog. Every time, all sides' bullets were kept indocs/source/changelog/1.5.0.rstandCHANGELOG.mdwas discarded and regenerated withpython util/changelog_md.pyrather than hand-merged, with--checkexiting 0 each time. Nothing underpcapkit/ortests/ever conflicted.:rfc:`9110#section-9.1``` and:rfc:9110#section-15``` roles now convert properly. One trap hit on the way: the converter rejects a double-backtick literal span that straddles a source line break, which is why the quotedValueError` text sits on one line.A citation correction
#582's body, and this PR's first draft, cited RFC 959 section 4.1 for FTP command case-insensitivity. The cross-review caught that as wrong, and it is: the rule is in section 5.3 (COMMANDS), which gives the command syntax - "The command codes are four or fewer alphabetic characters. Upper and lower case alphabetic characters are to be treated identically. Thus, any of the following may represent the retrieve command: RETR Retr retr ReTr rETr". Section 4.1 (FTP COMMANDS) only lists the per-command semantics.
Verified against the RFC text rather than taken on trust: the sentence sits at lines 2560-2572, between the
5.3. COMMANDSheading at 2548 and5.4.at 2743, while section 4.1 spans 1394-1963. Corrected in the changelog bullet, in the test docstring, and in the commit message.Worth noting what was not changed: the four existing
:rfc:959#section-4.1`` citations inpcapkit/const/ftp/command.pyand `pcapkit/vendor/ftp/command.py` are a different claim - the command kind (access control, transfer parameter, service) - and section 4.1 is the right citation for that. A blanket find-and-replace would have broken them.