Repository navigation
fix(protocols): find the payload by protocol name and follow IPv6 next headers, not key order (#2003) - #2014
Conversation
…2003) The order of an Info's keys is not part of its contract, and on PyPy it follows the attribute layout of an earlier instance of the same class. Two rebuilds from to_dict() read structure off that order. - Protocol._lookup_payload took the last nested key as the payload. It now takes the nested key that is the info_name of a protocol in __proto__ (or its fallback), wherever it sits. - IPv6._lookup_exthdr walked the nested keys in order. It now follows the next fields from data.next, and each step takes the first header not yet taken that is held under the key the step's parser writes (new helper IPv6._exthdr_parser; HIP's version-dependent alias is read off the info). - The repeated-exthdr and MultiInfo tests no longer pin the order across keys; the values of one key keep their order. - New test reorders an instance's __dict__ as PyPy does, so CPython covers it. On CPython both lookups answer every call over the sample captures exactly as before. PyPy 8.0.1: the 1453, 1610 and 1484 tests now pass.
|
GOOD TO GO at
Caveat: the payload is the first registry name present, so a hand-built dict with a decoy key named after an earlier-registered protocol (e.g. |
|
Coverage: 91.55% (unit tier, Python 3.15,
Per-file detail: the |
|
Why don't we use a dunder name to save the payload key name so that the lookup can be way much easier? |
|
And for bookkeeping the IPv6 extension headers, i say we use an OrderedMultiInfo or OrderedMultiDict there as those headers are not exactly presented and stored in nested format in the initial design - they are supposed to be stored at the same level as an OrderedMultiDict. |
|
The payload key already has a dunder: The name match only runs on data rebuilt from A new dunder would be dropped in exactly the same place. The one way a dunder could replace the fallback is to un-exclude Extension headers: I'll rework the IPv6 half as you ruled. The headers will sit at the same level, in an ordered multi container, so the chain is read from the stored order instead of being recovered from the |
|
I prototyped the Result so far. With Where it breaks.
Which do you want?
My recommendation is (3): it keeps this PR small, and the data-model change gets its own review. |
|
My thoughts are that we dont have to roundtrip from JSON or other format dumps. |
|
Understood: data loaded back from a dump does not need to rebuild. Here is what I'm doing on this PR:
The PR stays at |
|
|
|
Other dunders like |
|
Corrected plan, now the worker's brief:
Dumps and |
|
if we must keep |
|
Yes, that works, and it fixes #2003's payload half at its source. What it involves:
I'll put this in #2014 as a separate commit after the |
|
The name match in |
|
Agreed, no guessing. In the dunder commit:
|
…2003) The extension headers of an IPv6 info are now held, in wire order, in one field, ``extension_headers``: an OrderedMultiInfo keyed as before (``hopopt``, ``opts``, ...), so a repeated header keeps every info (#1453) and the order is the headers' own rather than that of the Info's keys, which differs on PyPy. ``_lookup_exthdr`` reads the chain from it, the code of each header being the ``next`` of the one before, with the #1473 rule that a header under IPv6_Ext's key is rebuilt with IPv6_Ext. The ``next``-field walk and ``_exthdr_parser`` are gone, and so is ``__exthdr__``: the parse and the dict rebuild now take the chain from the same place. Tests reading the per-header top-level keys read them from ``extension_headers``; the dump tests pin its shape, one single-key mapping per header.
…2003) ``__next_name__`` and ``__next_type__`` are no longer excluded, so ``from_data(info.to_dict())`` dispatches the payload exactly as ``from_data(info)`` does, whatever the key order. - ``ProtocolBase._lookup_payload`` and ``_lookup_nested`` are gone: the payload is dispatched on the two keys only. A dict without them, or without the key ``__next_name__`` names, rebuilds no payload, as for a protocol with no next layer; the ``exclude`` parameter of ``_make_payload`` goes with them. - ``Info.__repr__``/``__str__`` and the dumpers leave every dunder key out, so no dump carries ``__next_type__`` or ``__short_read__``. - ``Data``, ``from_data`` and ``_make_payload`` document that a hand-written dict must carry the metadata, and for IPv6 the headers under ``extension_headers``. The #1447 test now pins that ``to_dict`` carries the keys and a dump does not; the #2003 tests pin the rebuild on reordered keys and that a dict without the keys is not guessed at.
|
Yes, that's cleaner. The plan becomes two class-level lists, both extended per subclass the way
Both are documented on |
Reverts the extension_headers field of cf6931e and carries the metadata through to_dict instead. - Data_IPv6 keeps its per-header keys (repeats kept, #1453). Its __exthdr__ is an OrderedMultiInfo keyed by ExtensionHeader, of (parser, info) in wire order, so the #1473 IPv6_Ext fallback rides along. _lookup_exthdr is gone: the chain comes from __exthdr__ only, and a dict without it rebuilds no extension header. - Info.__included__, merged across bases like __excluded__, names the dunder keys iteration, len() and to_dict carry; every other dunder key is left out. __excluded__ now means only "left out of repr, str and the dumps". Data names __next_name__, __next_type__ and __short_read__ in both, Data_IPv6 adds __exthdr__; HTTP/2 Flags includes __value__ and PCAP-NG blocks __truncated_raw__, both still dumped. - to_dict's export records its __excluded__ (kept by copy, deep copy and pickle), and the dumpers skip those keys; _is_dunder is gone. - The hand-written-dict docs name every metadata key. Tests pin the restored shape, order independence on a reordered __dict__, __included__ inheritance, and dumps/repr without the keys.
|
NEEDS CHANGES at A Sonnet cross-review confirmed everything else:
Noted, not blocking:
|
…2003) Adds __included__ next to __excluded__ for Info and Data in the API docs, and doc comments stating its contract: dunder keys are dropped from to_dict, iteration and len() unless listed, and a subclass, or a custom Info, lists its own.
|
GOOD TO GO at A Sonnet review of the docs commit found:
The earlier round-3 review covered the rest at |
|
okay we should probably keep and for those |
|
Will do:
The PR is at |
…2003) __exthdr__ is main's tuple of (parser class, info) in wire order again, not an OrderedMultiInfo keyed by ExtensionHeader; the tuple order is the wire order, and the parser class keeps the #1473 IPv6_Ext fallback. to_dict carries the tuple as it is and from_dict restores it, so the chain is still rebuilt from it alone. Tests and the annotation describe the tuple form.
|
Pushed
How the audit was done: the |
|
GOOD TO GO at A Sonnet cross-review confirmed:
Not checked, and not blocking:
|
make pylint,make mypy,make isort)make testpasses, and a test case covers the change (narrow selections only, see below)What is the purpose of your pull request?
fix— corrects a defectDescription of your pull request and other information
On PyPy,
Info.__dict__key order follows an earlier instance's attribute layout. A rebuild fromto_dict()that recovered the payload or the IPv6 extension-header chain from key order therefore picked the wrong layer (#2003). Following the owner's rulings, no key order is used any more:main's layout: top-level per-header keys, plus__exthdr__as atupleof(parser class, info)in wire order. The fix(ipv6): from_data(info.to_dict()) crashes when an IPv6_Ext stands in for a dedicated extension header #1473 fallback travels with the parser class._lookup_exthdris deleted, and the chain is rebuilt only from__exthdr__.__next_name__/__next_type__, and the name-based lookup is deleted. A hand-written dict has to carry these keys, plus__short_read__and, for IPv6,__exthdr__. Without them nothing is rebuilt, and the docstrings say so.__included__names the dunders thatto_dict(), iteration andlen()carry; any other dunder is dropped.__excluded__now only means "left out ofrepr/strand the dumps".to_dict()now carries the dispatch dunders and__exthdr__. A customInfomust list its own dunder keys in__included__(HTTP/2Flagsand the PCAP-NG block model now do).Only narrow test selections were run (
tests/protocols,tests/corekit,tests/dumpkit,tests/foundation,tests/toolkit). PyPy is not installed here, so its key reordering was simulated by reordering__dict__.Closes #2003