Repository navigation
schema: let ListField.pack accept the tuple its own data models declare - #480
Conversation
…re (#476) Schema.pack's ListField branch accepted list and bytes but raised ProtocolUnbound on a tuple, even though several data models -- HIP's group_id, MH's prefixes/fid/bid/addresses/requests, and others -- declare that attribute as tuple[...] and _read_* hands one straight back to _make_* on a parse-then-reconstruct cycle. ListField.pack only ever iterates its argument, so the type distinction was never load bearing; it just broke every such round trip. - pcapkit/protocols/schema/schema.py: widen the ListField branch to isinstance(data, (list, tuple)), converting via list(data) before handing off to ListField.pack so its own Optional[list[_TL]] signature stays accurate for mypy. - tests/protocols/test_option_roundtrip_unit.py: delete the 16 EXPECTED_FAILURES entries this closes (hip-parameter/ACK, ACK_DATA, DH_GROUP_LIST, ESP_TRANSFORM, HIP_CIPHER, HIP_TRANSPORT_MODE, HIT_SUITE_LIST, NAT_TRAVERSAL_MODE, REG_FAILED, REG_INFO, REG_REQUEST, REG_RESPONSE, ROUTE_DST, ROUTE_VIA, TRANSPORT_FORMAT_LIST, VIA_RVS), each confirmed OK via examples/generators/options.py's roundtrip(). - tests/protocols/schema/test_schema_unit.py: add a direct unit test packing a ListField with a tuple, since a test that only ever builds a schema with a list cannot see this defect; also pins that a str (a sequence too) is still rejected. Checked the unpack direction and PCAP-NG/MH, which also declare tuple[...] on ListField/OptionField-backed attributes: unpack always builds a fresh list itself, so there is nothing to widen there, and a sweep of all 322 examples/generators/options.py cases shows zero RECONSTRUCT failures before or after this change outside the 16 HIP ones -- MH's _make_ext_multiprefix, _make_opt_context_request, and its fid/bid helpers already work around the same ProtocolUnbound at each call site with an explicit list(...), and PCAP-NG's NRB records never go through ListField at all (joined into a NUL-separated string). Same defect, same function; no separate issue filed. Build: brazil-build n/a (this is a plain pip/GitHub project). mypy pcapkit/protocols/schema/schema.py --config-file mypy.ini: 2 pre-existing errors (__prepare__ override, unpack "no self" false positive), 0 new. pytest tests/ -q: 1032 passed, 17 skipped, 1631 subtests passed, 0 failed (896s). Reverting the schema.py hunk reproduces exactly 16 subtest failures in test_option_roundtrip_unit.py and 1 in test_schema_unit.py, all ProtocolUnbound: unsupported type <class 'tuple'>.
Setup and scope check
The fix itself (schema.py:685-702)elif isinstance(data, (list, tuple)):
...
self.__buffer__[field.name] = field.pack(list(data), packet)
Is
The The 16 deletions, verified by direct enumeration (not suite-green inference)Loaded Per-label result, calling
And across the full 322-case set:
And: every one of Stale-entry check on the remaining 61 (mirror-defect concern): re-ran Also ran the real suite rather than only my scripts: The new test (
|
Closes #476
Summary
Schema.pack'sListFieldbranch (pcapkit/protocols/schema/schema.py:685-694) acceptedlistandbytesbut raisedProtocolUnboundon atuple, even though several data models declare the corresponding attribute astuple[...]-- HIP'sgroup_id, MH'sprefixes/fid/bid/addresses/requests, and others -- and_read_*hands one straight back to_make_*on a parse-then-reconstruct cycle.isinstance(data, (list, tuple)), converting vialist(data)before callingfield.packsoListField.pack's ownOptional[list[_TL]]signature stays accurate for mypy.ListField.packonly ever iterates its argument, so the list/tuple distinction was never load-bearing -- it just broke every such round trip.EXPECTED_FAILURESentries intests/protocols/test_option_roundtrip_unit.pythis closes:hip-parameter/ACK,ACK_DATA,DH_GROUP_LIST,ESP_TRANSFORM,HIP_CIPHER,HIP_TRANSPORT_MODE,HIT_SUITE_LIST,NAT_TRAVERSAL_MODE,REG_FAILED,REG_INFO,REG_REQUEST,REG_RESPONSE,ROUTE_DST,ROUTE_VIA,TRANSPORT_FORMAT_LIST,VIA_RVS. Each was individually re-enumerated fromexamples/generators/options.py'scases()and confirmedroundtrip()now returns'OK'.tests/protocols/schema/test_schema_unit.pythat packs aListFieldwith a tuple (a schema built directly with a list can never see this defect) and confirms astr-- also a sequence -- is still rejected.The unverified tail
Checked whether the same list-only assumption exists in the
unpackdirection or in PCAP-NG/MH, which also declaretuple[...]onListField/OptionField-backed attributes:unpack: no assumption to widen.ListField.unpackalways builds a freshlistitself off the wire; it never inspects what a previous cycle produced.examples/generators/options.pycases shows zeroRECONSTRUCTfailures, before or after this change, outside the 16 HIP ones. Reading why:_make_ext_multiprefix,_make_opt_context_request, and the flow-idfid/bidhelpers inpcapkit/protocols/internet/mh.pyalready work around the identicalProtocolUnbound: unsupported type <class 'tuple'>at each call site with an explicitlist(...)conversion (one of them even has a comment quoting that exact error), and PCAP-NG's NRBrecordsnever goes throughListFieldat all -- it's joined into a NUL-separated string and packed as a plain byte string. So nothing else needed filing.Test plan
pytest tests/ -q(PYTHONPATH pointed at this worktree,pcapkit.__file__verified to resolve there first): 1032 passed, 17 skipped, 1631 subtests passed, 0 failed (896s)mypy pcapkit/protocols/schema/schema.py --config-file mypy.ini: 2 pre-existing errors (__prepare__override,unpack"no self" false positive), 0 newoptions.cases()+roundtrip(): all'OK'schema.pyhunk reproduces exactly 16 subtest failures intest_option_roundtrip_unit.pyand 1 failure in the newtest_schema_unit.pytest, allProtocolUnbound: unsupported type <class 'tuple'>