Skip to content

schema: a forward match consumes nothing, so bill it nothing in len(schema) - #456

Merged
JarryShaw merged 4 commits into
mainfrom
fix-446-forward-match-field-length
Sep 18, 2026
Merged

JarryShaw merged 4 commits into
mainfrom
fix-446-forward-match-field-length

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Sep 18, 2026

Copy link
Copy Markdown
Owner

Closes #446

The defect and mechanism

ForwardMatchField peeks ahead without consuming: Schema.unpack() reads
field.length octets, unpacks them into the field's value, then rewinds the
stream past them (data.seek(-length, io.SEEK_CUR),
pcapkit/protocols/schema/schema.py:759 on main) and skips the
packet['__length__'] -= length decrement that every other field gets. But it
left the raw octets it had just read sitting in
self.__buffer__[field.name] (set unconditionally two lines earlier, at
schema.py:748). Schema.__bytes__()/__len__()
(schema.py:437-443) concatenate every slot in __buffer__, with no
exception for a forward match, so a schema containing one over-reports its
own length by exactly the match's width — the same octets are counted twice:
once in the forward-matched slot, once more where the field that actually
needs them reads them for real.

Where a declared area is then checked against that length —
ListField.unpack (pcapkit/corekit/fields/collections.py:187-189 for the
schema-item branch, :191-193 for the fixed-width branch, both
length -= len(data) then if length < 0: raise FieldValueError(...)) —
the over-count makes correct input fail. That is exactly what blocks
CGAParametersOption.parameters (pcapkit/protocols/schema/internet/mh.py:533),
a ListField of CGAParameter items: each CGAParameter
(mh.py:499-521) carries a load-bearing ForwardMatchField,
public_key_test (mh.py:509), that has to preview the public key's length
before public_key can be sized, so it cannot simply be deleted the way
#441's stray HOPOPT/IPv6-Opts twin can. (OptionField.unpack,
collections.py:399-451, is the other declared-area check the issue names,
but it never raises an "invalid length" error itself — it runs the option
list to length exhaustion instead — so the FieldValueError: Field parameters has invalid length. the issue quotes names parameters, the enclosing
ListField, not CGAParameter.extensions's own OptionField.)

Design chosen, and the evidence for it

The issue lays out two options: exclude forward-match slots from
__bytes__/__len__ (less disruptive, but changes what bytes(schema)
round-trips), or add a separate consumed-octet measure and use that
everywhere a declared area is checked (more honest, touches every check).

I chose the first, implemented by having unpack() zero a
ForwardMatchField's __buffer__ slot right after the rewind — and the
decisive evidence is that pack() already does exactly this, and has since
e1a406c8a (2023-04-06):

# schema.py:637-639 (pack)
if isinstance(field, ForwardMatchField):
    self.__buffer__[field.name] = b''
    continue

and it is already asserted by an existing, passing test — FeatureSchema in
tests/protocols/schema/test_schema_unit.py has a peek ForwardMatchField,
and test_schema_pack_unpack_and_mapping_methods (lines 69-70) already
expects peek's contribution to be absent from both bytes(schema) and
len(schema), for a schema built from field values (which goes through
pack()). unpack() was the only path where this wasn't true — it stored
the real matched octets instead of b'', for no reason connected to what the
field means. This fix makes unpack() agree with the convention pack()
already established and already has test coverage for; it does not introduce
a new convention.

There's a second, load-bearing reason this is correct rather than merely
convenient: since a forward match doesn't advance the stream, the octets it
reads are always re-read by whichever field actually needs them (that's the
whole point of previewing). So the octets are never lost by dropping the
duplicate — bytes(schema) after this fix reproduces exactly the octets
Schema.unpack() consumed from the wire, where before the fix it reproduced
more bytes than were consumed (the previewed region, twice).

The trade-off

bytes(schema) semantics do change for a schema obtained via .unpack()
that contains a ForwardMatchField: before this fix, bytes(schema) for such
a schema was longer than the octets actually consumed (the previewed region
counted twice); after, it equals the octets consumed. I'm treating this as a
bug fix rather than a behaviour change worth gating, because:

  • it aligns the two existing code paths (pack() vs unpack()) that were
    already inconsistent with each other on main, not introducing a new
    inconsistency;
  • an existing test already encodes the "a forward match contributes nothing"
    expectation for the pack() path;
  • I found no test or code path anywhere in the tree that reads
    bytes(unpacked_schema) and expects the previewed region counted twice —
    see the census below.

Census: len(schema) / bytes(schema) consumers

git grep against origin/main for both, outside schema.py itself:

  • pcapkit/corekit/fields/collections.pyListField.unpack and
    OptionField.unpack, both length -= len(data) against a declared area
    (the two named in the issue).
  • pcapkit/protocols/internet/hip.py:757,2882, hopopt.py:482,1265,
    ipv6_opts.py:493,1277, transport/tcp.py:698counter += len(schema) /
    opt_len = len(schema) while walking a sequence of options/parameters,
    same "how many octets did this account for" semantics.
  • pcapkit/protocols/protocol.py:474self.__init__(bytes(schema), len(schema)) in Protocol.from_schema(), reconstructing a protocol
    instance's raw bytes from its schema. This one is a live consumer of the
    exact inconsistency this PR fixes: depending on whether Schema.unpack's
    __updated__ flag got reset by a later field assignment (see the note at
    pcapkit/utilities/decorators.py:237-260, which documents that quirk for
    an unrelated reason), bytes(schema) on main could take either the
    buggy long form or the already-correct pack()-repacked form for the
    same schema object. After this fix both agree.
  • Tests: tests/protocols/misc/test_pcapng_unit.py asserts len(schema) == len(raw) six times, and test_pcapng_section_header_block_round_trips_ through_bytes round-trips a schema whose match field is a
    ForwardMatchField (PCAP-NG's byte-order probe) — still green after this
    fix (checked explicitly, see below).
  • tests/protocols/schema/test_schema_unit.py:69-71,280,283 — the pack()
    path's existing round-trip assertions, unaffected (this fix touches
    unpack() only).

No consumer anywhere expects bytes(schema)/len(schema) to include a
forward match's previewed octets.

Census: ForwardMatchField users

git grep -n ForwardMatchField against origin/main, outside misc.py/
schema.py (9 declarations, 8 in shipped schemas + 1 in the test suite):

None are wrapped in a ConditionalField, so the ConditionalField-unwrap
branch already present in both pack() and unpack() (field = field.field(packet)) was not a new case to handle — it already runs before
either function's ForwardMatchField check.

Test evidence

Two new cases in tests/protocols/schema/test_schema_unit.py, both verified
in both directions at e2d8ed6d1 (fails before, passes after):

test_forward_match_field_does_not_count_toward_length — a two-field
schema (length_peek a ForwardMatchField(UInt8Field), length the same
octet read for real, data sized from it) unpacked from b'\x02AB' (3
octets). Before the fix:

AssertionError: 4 != 3

After: len(unpacked) == 3 and bytes(unpacked) == b'\x02AB'.

test_schema_list_field_rejects_a_declared_area_that_a_forward_match_over_reports
— a ListField(length=4, item_type=SchemaField(length=2, schema=...)) over
two of the items above (b'\x01A\x01B', exactly 4 octets, the correct
declared area). Before the fix:

pcapkit.utilities.exceptions.FieldValueError: Field markers has invalid length.

— the same exception class and message shape CGAParameter.extensions hits
(FieldValueError: Field parameters has invalid length.). After the fix it
unpacks two items cleanly.

EXPECTED_FAILURES change

Update after merging main: this PR originally deleted the
'ipv6-opts-option/SMF_DPD' entry itself, because this fix, on its own
(without #449's removal of the stray field), independently turns that case
from a recorded PARSE failure into 'OK' — verified at the time:
test_round_trip_is_identity_or_a_recorded_gap failed with
AssertionError: 'OK' != 'PARSE' until the entry was deleted.

#449 has since merged to main (73edb096f) and deleted the same entry
first, for its own reason (removing the stray field), rewriting the
surrounding comment block into past tense to cover both the #432/hopopt
half and the #441/ipv6-opts half of the story. I merged main into this
branch (git merge --no-ff) and resolved the one resulting conflict in
tests/protocols/test_option_roundtrip_unit.py by taking main's comment
block as-is and dropping my own version — there is nothing left for this PR
to delete, since #449 already got there. The file is now byte-identical to
main's copy; this PR's only remaining diff is schema.py and the two new
tests in test_schema_unit.py.

I re-ran the round-trip harness on the merged tree
(bf95d1e07, fixtures regenerated fresh) to check for further fallout from
having #446's fix, #449's field removal, and #453's ListField item-unpack
fix all present together: test_round_trip_is_identity_or_a_recorded_gap
still passes, and no other recorded case flipped. mh-extension/Multi_Prefix
(the case that exercises CGAParameter through the public API) is still
unaffected: it fails exactly as recorded, with KeyError: 'length', because
#445's nested-__packet__ fault hits first, before this issue's
FieldValueError would ever be reached.

Suite counts

Earlier figures in this PR were wrong and are withdrawn. They were
measured with python -m unittest discover, which collected 875
(862 passed + 13 skipped) at e2d8ed6d1, where pytest collects 977 at that
same sha — 102 short, for a reason I did not chase down (unittest discover
apparently misses some tests pytest finds; there is nothing pytest-native
in tests/ — no bare def test_*, no @pytest.mark.parametrize,
no @pytest.fixture — so the exact mechanism is still unexplained). They
also compared a baseline measured before fixtures were regenerated against
an after-fix run measured after regenerating them, so the two runs were not
even over the same fixture set. Both problems are fixed below by using
pytest throughout and generating fixtures exactly once before either run.

That 977 (collected at e2d8ed6d1) and the 994/977 pair below
(collected/passed at da2422728) are unrelated measurements of different
quantities at different shas, and their sharing a digit is coincidence, not
confirmation of anything
da2422728 is four merged PRs ahead of
e2d8ed6d1, and those four PRs added 17 tests between them, which is the
entire reason collection moved from 977 to 994. Read the two figures below as
standalone, sha-qualified measurements, not as corroborating the paragraph
above.

main moved four commits while this was in flight
(#449, #453, #451, #450); the figures below are against the merged
tree, with main at da2422728 as the baseline and this branch's merge
commit bf95d1e07 as "after". Fixtures were regenerated once, from
bf95d1e07, with examples/generators/make_samples.py, before measuring
either side; the baseline measurement below reused those same fixture files
without touching them again.

Collected (pytest tests --collect-only -q | grep -c '^tests/.*::'):

  • da2422728 (baseline): 994
  • bf95d1e07 (this branch, merged): 996 (+2, this PR's two new tests)

Full run (pytest tests -q):

  • da2422728 (baseline): 977 passed, 17 skipped (994 total)
  • bf95d1e07 (this branch, merged): 979 passed, 17 skipped (996 total)

The delta is exactly the two new tests in test_schema_unit.py, both
passing; skipped count is unchanged. test_option_roundtrip_unit.py and
test_schema_unit.py together: 22/22 passed on the merged tree (see
above).

Run with PYTHONSAFEPATH=1, PYTHONPATH at the worktree root, interpreter
.venv/bin/python 3.14.7; pcapkit.__file__ printed and confirmed to
resolve inside the worktree before each run.

Coordination


Two corrections to this description, from the review, both verified independently and neither affecting the code or tests.

  1. The pack() side has zeroed a ForwardMatchField's buffer slot since e1a406c8a (2023-04-06), not since schema_final's generated typed __init__ is dead code, so Schema.__post_init__ never runs #422. git blame -L 636,640 origin/main -- pcapkit/protocols/schema/schema.py attributes those three lines to e1a406c8a7, and schema_final's generated typed __init__ is dead code, so Schema.__post_init__ never runs #422's own commits never touch that branch. The claim was wrong about when, not about whether — the precedent this fix mirrors is real and considerably older than stated, which if anything strengthens the "restore a long-standing invariant" reading.

  2. The ForwardMatchField census was stale rather than miscounted: it was taken before PR protocols: drop ipv6_opts' stray SMF_DPD test field, fix two length underflows #449 merged, and protocols: drop ipv6_opts' stray SMF_DPD test field, fix two length underflows #449 deleted the stray ipv6_opts.py:434 declaration that ipv6_opts' SMFIdentificationBasedDPDOption has a stray test field HOPOPT lacks, so identical octets fail there #441 was about. Current per-module counts on main are hopopt 2, ipv4 1, ipv6_opts 2, mh 1, pcapng 1, tcp 1 — 8 shipped declarations plus 1 in the test suite, not 9 + 1. The blast radius is one declaration smaller than described.

The fix, the tests and the measurements are unaffected: 996 collected / 979 passed / 17 skipped at bf95d1e07, mypy 124 errors in 40 files with or without the fix, and CGAParameter.public_key_test — the load-bearing case — still exists on current main (faf86d26b), so the fix is not obsoleted by #437's mh.py rewrite.

…chema)

Schema.unpack() kept the octets a ForwardMatchField read in __buffer__ even
though it rewinds the stream past them, so __bytes__()/__len__() -- which
concatenate every __buffer__ slot -- double-counted them: once in the
forward-matched slot, once more where the real field re-reads the same
octets. OptionField and ListField size a declared area by subtracting
len(item) as they go, so the over-count broke correct input, e.g.
CGAParameter's public_key_test (mh.py:509), which cannot simply drop the
field the way #441's stray one can. See #446.

- pcapkit/protocols/schema/schema.py: unpack() now zeroes a
  ForwardMatchField's __buffer__ slot after the rewind, mirroring what
  pack() already does for the same field type -- pack() has zeroed it since
  #422 and a schema built from field values already tests that way
  (test_schema_unit.py:69-70). unpack() was the one path left inconsistent;
  bytes(schema) now agrees with pack()'s existing convention on both paths,
  and now reproduces the octets actually consumed rather than double
  counting the previewed region.
- tests/protocols/schema/test_schema_unit.py: two new cases -- a minimal
  schema with one ForwardMatchField asserting len(schema) equals the
  octets consumed, and a ListField whose declared area is exactly right but
  was rejected before the fix with the same FieldValueError CGAParameter
  hits.
- tests/protocols/test_option_roundtrip_unit.py: deletes the
  'ipv6-opts-option/SMF_DPD' EXPECTED_FAILURES entry, which this fix turns
  'OK' on its own (independent of #449's removal of the stray field that
  exposed it).

Full suite: 864 passed, 13 skipped, at PYTHONSAFEPATH=1, interpreter 3.14.7.
Baseline at e2d8ed6 (origin/main): 862 passed, 13 skipped, before the 2
new tests existed.
…tionField

CGAParametersOption.parameters is the ListField of CGAParameter items whose
budget the forward-match over-count drains; CGAParameter.extensions is an
OptionField and never raises an invalid-length FieldValueError itself. Fix
the docstring to name the field that matches the issue's own traceback.
…-field-length

# Conflicts:
#	tests/protocols/test_option_roundtrip_unit.py
@JarryShaw

Copy link
Copy Markdown
Owner Author

Review of bf95d1e07 (branch fix-446-forward-match-field-length, closes #446)

Standing in for Copilot on this one. Read every file with git show <ref>:<path> against
refs/pr456/head (verified equal to bf95d1e07770d1b85db2a9a924c950a2ddb7a7b6) and
origin/main, never the ambient tree. All commands run from a dedicated worktree with
PYTHONSAFEPATH=1, PYTHONPATH at that worktree, interpreter
/local/home/jarryx/GitHub/PyPCAPKit/.venv/bin/python (3.14.7); pcapkit.__file__ was printed
and confirmed to resolve inside the worktree before every measurement.

CI

Settled: 21 SUCCESS + 2 SKIPPED (Docs test gate, Gate full-suite Python 3.14) + 1 SUCCESS
status context (pyup.io/safety-ci) = 24/24, zero red.
The dispatch-time snapshot (13 queued)
was mid-flight on a runner-concurrency-limited free tier; it finished clean after ~25 minutes of
polling.

Diff scope

Confirmed: only pcapkit/protocols/schema/schema.py (+20 lines: one functional line —
self.__buffer__[field.name] = b'' added after the data.seek(-length, io.SEEK_CUR) rewind in
unpack() — plus comments on both the pack() and unpack() ForwardMatchField branches) and
tests/protocols/schema/test_schema_unit.py (+98 lines, two new tests). bf95d1e07 is a merge
commit with parents 3fecd93f2 (the PR's own tip) and da2422728 (origin/main as it stood at
the time) — "took main" confirmed.

The diagnosis

pack() on main/head does zero a ForwardMatchField's buffer slot (schema.py:637-639,
self.__buffer__[field.name] = b''; continue) — verified directly, and independently confirmed by
running FeatureSchema(kind=9, maybe=0xAB, peek=0xFE, ...) from
test_schema_pack_unpack_and_mapping_methods: bytes(schema) == b'\x09\xab\x10\x11\x44\x00\x00body'
has no trace of peek's 0xFE, and len(schema) == 11 is exactly the non-peek fields' width.
So the pack-path convention the PR leans on is real.

But "pack() has done exactly this since #422" is not correct as a chronological claim.
git blame -L 635,640 origin/main -- pcapkit/protocols/schema/schema.py puts those exact lines
at e1a406c8a ("updated Schema for ForwardMatchField"), dated 2023-04-06 — and git show <sha> --stat for both of #422's commits (38a7741de, c2703e5d2) shows neither touches this branch of
pack() at all; #422 fixed an unrelated __post_init__/generated-__init__ bug. The behavior
predates #422 by roughly three years and #422 is not where it came from. This doesn't weaken the
actual argument (pack/unpack were inconsistent with each other regardless of when pack's side was
written), but the attribution in the PR body should be corrected or dropped.

The fix itself is a straight mirror of the existing pack() branch, placed correctly: it doesn't
touch the else: packet['__length__'] -= length skip that already exists for ForwardMatchField,
and it runs after setattr/packet[field.name] = value so the field's real value is unaffected —
only __buffer__ (which drives __bytes__/__len__) is zeroed. CGAParameter.public_key_test
(mh.py) is confirmed load-bearing exactly as described, and grep confirms no shipped
ForwardMatchField is wrapped in a ConditionalField, so the existing unwrap-before-check
ordering was never a concern.

The census — one miscount

The PR claims nine shipped ForwardMatchField declarations (hopopt ×2, ipv4 ×1, ipv6_opts
×3, mh ×1, pcapng ×1, tcp ×1) plus one in the suite. git grep -n "ForwardMatchField(" against
both origin/main (da2422728) and PR head (bf95d1e07) shows ipv6_opts.py with only
two (:414 and :540) — the third, the stray SMF_DPD test field the PR's own body cites
as "#441/PR #449's target", was already deleted by #449 (73edb096f) before this branch's final
merge picked it up. Real count: 8 shipped + 1 test = 9, not 10. This is stale bookkeeping
carried over from before #449 landed; it doesn't change the fix (which is generic to any
ForwardMatchField), but the blast-radius number in the PR body is one field too many.

The consumer census — checked, accurate

  • ListField.unpack (collections.py): length -= len(data), if length < 0: raise FieldValueError(...) — confirmed, and empirically triggered (see below).
  • OptionField.unpack: confirmed it has no length < 0 raise anywhere in the function; an
    over-reported len(data) just starves the while length > 0 loop early and leaves
    self._option_padding = length negative. Matches the claim that it "never raises... runs the
    option list to length exhaustion instead."
  • pcapkit/protocols/internet/hip.py:757,2882, hopopt.py:482,1265, ipv6_opts.py:493,1277,
    transport/tcp.py:698 — all counter += len(schema) / total_length += len(schema) / opt_len = len(schema), exact line matches.
  • pcapkit/protocols/protocol.py:474self.__init__(bytes(schema), len(schema)) in
    from_schema(), exact line match. The cited __updated__ quirk in
    utilities/decorators.py:237-260 is real and substantiates the "could take either the buggy
    long form or the already-correct form" claim.
  • tests/protocols/misc/test_pcapng_unit.py: assertEqual(len(schema), len(raw)) appears exactly
    six times (lines 2328, 2363, 2383, 2411, 2443, 2472). Ran the file in isolation:
    37 passed, including test_pcapng_section_header_block_round_trips_through_bytes, which
    round-trips SectionHeaderBlock's match ForwardMatchField through the public extract()
    API.

No consumer I checked regresses.

The merge / EXPECTED_FAILURES

git diff origin/main..refs/pr456/head -- tests/protocols/test_option_roundtrip_unit.py is empty
— byte-identical, confirming the conflict was resolved by taking main's (post-#449) version.
mh-extension/Multi_Prefix is still recorded as Gap('PARSE', "KeyError: 'length'", ...),
unaffected, because #445's fault hits first — confirmed by reading the entry directly.

Test evidence — reproduced in both directions

Ran the two new tests against origin/main's un-fixed schema.py (temporarily swapped into
this worktree, then restored — git status clean afterward) and got exactly the errors the PR
quotes: AssertionError: 4 != 3 and FieldValueError: Field markers has invalid length. Restored
the fix and both pass; the full targeted run —
pytest tests/protocols/schema/test_schema_unit.py tests/protocols/test_option_roundtrip_unit.py
— is 22 passed, matching the PR's "22/22" claim.

Suite counts, measured independently at bf95d1e07

Fixtures regenerated fresh (examples/generators/make_samples.py) before measuring.

  • pytest tests --collect-only -q: 996 collected (matches the PR's claim exactly).
  • pytest tests -q: 979 passed, 17 skipped, 0 failed, in 638s — matches the PR's claimed
    979/17 exactly. grep -c "^FAILED\|^ERROR" on the log: 0.

mypy

python -m mypy --follow-imports=silent --ignore-missing-imports --show-column-numbers --show-error-codes pcapkit: 124 errors in 40 files (496 source files checked) at bf95d1e07.
Re-ran with origin/main's schema.py swapped in (isolating this PR's only functional file, then
restored): identical 124/40 — this PR introduces zero new mypy errors, and the corrected
baseline (124/40 at da2422728, not the stale 128/41 some earlier briefs quoted) holds.

One thing that changed mid-review, worth flagging

origin/main moved from da2422728 to faf86d26b while this review was running (three new
commits: 94a93e721, 489eef651 "#437 — complete the Mobility Header registry", and faf86d26b
"#460"). #437 rewrites pcapkit/protocols/schema/internet/mh.py extensively (2305-line diff) —
the exact file CGAParameter lives in — and also touches tests/protocols/test_option_roundtrip_ unit.py. I checked: CGAParameter.public_key_test is still a ForwardMatchField on the new
main (now at mh.py:866), so the bug this PR fixes is still live and the fix is not obsoleted.
GitHub now reports mergeStateStatus: BEHIND but mergeable: MERGEABLE (no conflicts). This
isn't a defect in the PR — just a note that the "byte-identical to main" and census claims above
are accurate as of da2422728, the tip this branch actually merged, and will need a fresh look
against faf86d26b before merge if the gap widens further.

Verdict

GOOD TO MERGE at bf95d1e07. The fix is minimal, symmetric with the pre-existing pack()
convention, correctly placed, test-verified fail-before/pass-after with the exact errors quoted,
and regresses no consumer I could find. CI is fully green (24/24, zero red). Two corrections to
the PR body's own supporting evidence are worth making before/after merge — the "since #422"
attribution is wrong (predates it by ~3 years, per git blame), and the ForwardMatchField
census should read 8 shipped + 1 test = 9, not 9 + 1 = 10, since #449 already removed the stray
ipv6_opts field this census still counts. Neither affects the code or the tests, only the
narrative.

@JarryShaw
JarryShaw merged commit 0283a6d into main Sep 18, 2026
23 checks passed
@JarryShaw
JarryShaw deleted the fix-446-forward-match-field-length branch September 18, 2026 02:52
JarryShaw added a commit that referenced this pull request Sep 18, 2026
- main merged #437 (MH registry completion, including the four
  mh-extension codes and the _make_ext_multiprefix arithmetic fix) and
  #456/#446 (the ForwardMatchField double-count in Schema.__len__)
  since this branch's last merge. Combined with this PR's own fix,
  all four mh-extension/{Multi_Prefix,Exp_FFFD,Exp_FFFE,Exp_FFFF} cases
  now round-trip cleanly -- verified directly against the round-trip
  harness (all four return 'OK'), not assumed from the PR descriptions.
  Deleted their EXPECTED_FAILURES entries; a stale PARSE/KeyError
  expectation would otherwise have failed this module outright, per its
  own two-way assertion.
- The issue's own 40-octet CGA Parameters reproduction now parses
  completely end to end (confirmed directly: MH(raw, len(raw),
  extension=True) returns a populated CGAParametersOption, no
  exception). Rewrote test_cga_parameters_option_reaches_the_446_boundary
  _not_a_keyerror, which asserted the (now stale) FieldValueError
  boundary, as test_cga_parameters_option_now_parses_end_to_end,
  asserting the parsed fields directly.
- #437 had pinned the pre-fix KeyError as
  test_mh_cga_parameters_option_is_unparsable_upstream, explicitly so
  that "whoever fixes it finds out here" -- and it did: this run turned
  that test red once the merge above landed. Replaced it with
  test_mh_cga_parameters_option_now_parses, asserting the option parses
  and its fields are what the wire says, and fixed the now-stale
  cross-reference and claim in
  test_mh_pmipv6_options_round_trip_byte_for_byte's docstring (CGA_Parameters
  is still excluded from that test's cases, but no longer because it
  cannot be parsed -- that is now a separate, deliberate scope decision
  for whoever adds its full round-trip identity).
- Merged origin/main (0283a6d) with one conflict, in this exact
  region of tests/protocols/test_option_roundtrip_unit.py, resolved by
  re-deriving the correct entries from the actual post-merge behaviour
  rather than picking either side.

Verified: mypy pcapkit -> 123 errors/40 files (a fresh main, 0283a6d,
is 124 -- unchanged from before this merge). Round-trip harness: 7
passed, 363 subtests passed, 0 failed (up from 299 subtests before
#437 grew the mh-extension family to four codes). tests/protocols/
internet/test_mh_unit.py: 35 passed, 266 subtests passed, 0 failed.
Full local suite result to follow in the PR description.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ForwardMatchField's non-consuming bytes count toward Schema.__len__, so correct input fails a declared-length check

1 participant