Skip to content

netprotocols 2.2.1

Latest

Choose a tag to compare

@github-actions github-actions released this 07 Sep 19:16
· 53 commits to master since this release
097ec13

Fixed

  • Closed a decode-throughput regression against dpkt that nobody had
    been watching tier over tier (#147).
    Re-measuring every held claim
    after #107/#124 closed showed netprotocols had gone from 1.16× faster
    than dpkt right after Tier 1 to ~11% slower after Tiers 2-4 — a
    reversal none of the five tiers' own re-measurements had caught,
    because none of them profiled, only re-measured before/after.
    cProfile against the corpus decode loop found two causes, neither
    of them the tiers first suspected: bytes_to_ipv6 (added by #99 for
    Pyodide portability, not one of the three tiers originally named)
    formatted its eight address words one f-string at a time through a
    generator, ~18% of total corpus decode time; decode_frame()'s
    Packet construction paid a redundant isinstance check, through
    Protocol's ABC machinery, on a list already guaranteed to hold only
    Protocol instances, ~7-8% of decode_frame()'s own time. Both
    fixed without changing observable behavior: bytes_to_ipv6 now
    formats all eight words in one %-format call (verified
    byte-identical against glibc's inet_ntop via the existing
    hypothesis test, 2.1× faster in isolation); Packet gained an
    internal _from_decoded() fast-construction path, used only by
    decode_frame() — the public Packet(...) constructor still
    validates arbitrary arguments exactly as before.
  • scripts/benchmark.py now measures decode_frame(), the
    documented public chain-walking API, instead of a hand-rolled loop
    that predated it (#147).
    The benchmark's decode_netprotocols()
    kept its own copy of the pre-#88 walk loop, never updated when
    decode_frame() shipped, so every decode-throughput figure this
    project has published — including every number in docs/CLAIMS.md
    section 1 before this release — described code the documented API's
    callers never actually ran. benchmarks/baseline.json, five tiers
    and this fix stale at a v1.3.0-era figure, was refreshed to match.

See docs/CLAIMS.md's "Re-measured after closing the
dpkt-throughput regression" section for the full profiling writeup,
before/after numbers under both the old and new benchmark
methodology, and updated 1.1/1.2/1.6 figures.

Development

  • Addressed CodeFactor's static-analysis findings on the tooling
    scripts (#156).
    scripts/pyodide/check_in_pyodide.py extracted
    the built wheel into a hardcoded, predictable /tmp/netprotocols- wheel path; it now uses tempfile.mkdtemp(), verified against a
    real Pyodide runtime (node scripts/pyodide/run_in_pyodide.mjs)
    decoding the full corpus unchanged. scripts/check_fixtures.py's
    _check_l3 — one 137-line function walking IPv4, IPv6, GRE
    tunneling, VLAN and every upper-layer checksum — is split into one
    small function per protocol; output is byte-identical on the full
    97-frame fixture corpus before and after. scripts/benchmark.py's
    three broad except Exception: continue blocks are unchanged but
    now documented: dpkt and scapy have no shared "malformed frame"
    exception the way this project's own ProtocolError does, so a
    bare except is the correct interop boundary, not a swallowed bug.
    The flagged duplication between benchmark.py and
    check_in_pyodide.py's minimal pcap readers — nearly byte-identical
    read_pcap/corpus_frames functions — is now shared via a new
    scripts/_pcap.py. An initial pass left the duplication in place,
    reasoning both scripts' docstrings required each verification path
    to stand fully independent of the others; re-reading them shows that
    claim is narrower than stated — "standalone" there means not
    depending on netprotocols (the thing each script verifies), the same
    usage CONTRIBUTING.md and check_fixtures.py's own docstring use,
    never independence from sibling scripts. _pcap.py is itself
    stdlib-only and does not import netprotocols, so it preserves the
    property the docstrings actually document. Verified against a real
    Pyodide runtime after the extraction: the corpus still decodes
    unchanged, and check_fixtures.py's own, materially different
    read_pcap (it additionally validates linktype and length) is
    untouched, since it was never part of this duplication.

Documentation

  • Corrected six README.md claims an independent audit against the
    live repo found stale or overstated, none of them accidental
    regressions — each had simply drifted from what it once described.

    Wheel size (85.6 KB / ~30× smaller than scapy) was measured against
    the 2.0.0 wheel, two releases behind; rebuilt from HEAD, it's
    88.3 KB / ~29× (docs/CLAIMS.md §1.5 corrected to match). The typing
    bullet's "107 of [scapy's] files" enable strict mypy checking was a
    raw wc -l over scapy's enabled-files list including blank lines
    and comments; filtered the way scapy's own tooling does, it's 89
    (docs/CLAIMS.md §2.1 corrected, with the mismeasurement documented
    so it doesn't recur). The Pyodide/browser bullet credited "dpkt and
    pypacker import cleanly" to the real-Pyodide CI job; that job only
    ever imports netprotocols under Pyodide (confirmed by reading
    scripts/pyodide/check_in_pyodide.py) — the dpkt/pypacker rows come
    from a separate sys.meta_path-blocklist simulation under ordinary
    CPython, not from CI (docs/CLAIMS.md §3.1 reworded to say so
    explicitly). The CI-gating bullet's closing clause, "the rest have no
    performance benchmark at all," was contradicted by the very citation
    it points to — docs/CLAIMS.md §1.7 documents that scapy and
    PyTCP-net_proto do ship benchmark scripts, just not CI-wired; reworded
    to the claim the table actually supports (none of the ten gates on
    a regression). The Contributing section's "pytest, mypy, and
    ruff check — all three are enforced by CI on Python 3.12-3.14"
    overstated the matrix: only pytest runs on 3.12-3.14; mypy and
    ruff check each run on 3.12 only. The Roadmap section's "everything
    through 2.2.0 has landed on master" didn't mention that a further,
    unreleased fix (#147) has since landed on top of 2.2.0 — added a
    clause naming it. A seventh finding — the Roadmap's claim that "only
    2.0.0 was an actual PyPI release" — is contradicted by PyPI itself
    (2.2.0, and in fact five earlier minor/patch versions, are also
    published there); left unfixed pending a maintainer decision on
    whether 2.2.0's publication was intentional, since fixing the prose
    first would paper over a real process question.
  • Resolved the seventh finding above: 2.2.0's PyPI publication was
    confirmed intentional.
    The Roadmap section's "per the roadmap's own
    release policy, only 2.0.0 was an actual PyPI release" is now
    "the roadmap's original 'only at a major version bump' release policy
    wasn't actually held to: both 2.0.0 and 2.2.0 — a minor release — are
    on PyPI," and the roadmap table's 2.2.0 row gained the same
    released marker 2.0.0 already carried. Issue #107, the source
    both docs draw this from, updated its own stated policy line with a
    dated correction rather than silently rewriting history.
  • A fresh independent audit for #101 found four more genuine drifts
    and fixed them, none of them regressions — just the register falling
    behind a codebase and a README that both kept moving.
    docs/CLAIMS.md
    §5.3's statement count (1,935) had drifted from the live count of
    2,091 as the codebase grew — the coverage percentage held steady only
    because the miss count never moved, masking it; pyproject.toml's own
    inline comment was still quoting an even older 1,441 and is now
    corrected too. §5.7's "79 tests" for tests/test_walk.py undercounted
    the live 85: one test parametrized over the corpus's first 40 frames
    now collects its full 40 instances because the corpus has since grown
    past that slice, not because tests were added. §1.6 overstated its own
    claim about README.md, saying the decode-depth figure travels with the
    throughput figure "in the same sentence" there; they're adjacent
    bullets, not one sentence — corrected. README's Pyodide/browser bullet
    said outright that "pypacker was not tested", contradicted by
    docs/CLAIMS.md §3.1's own simulation table two paragraphs away, which
    shows pypacker importing cleanly under the identical CPython-side
    check used for dpkt; reworded to say so, and to stop implying the
    scapy-import failure was observed directly inside the real Pyodide CI
    run rather than established by source citation plus that simulation.
    README's CI-gating bullet lumped PyTCP-net_proto in with three
    libraries whose benchmark code targets the library being compared;
    docs/CLAIMS.md §1.7's own citation notes PyTCP-net_proto's benchmark
    exercises its RX daemon instead — reworded to keep that distinction
    rather than flattening it.
  • Restored this file's own missing ## [2.2.0] version header. A
    prior documentation commit deleted it while inserting an adjacent
    bullet, which silently re-merged the entire tagged, PyPI-published
    2.2.0 release back under ## [Unreleased] — the section below is
    what a git show of the v2.2.0 tag's own CHANGELOG.md actually
    contained at release time.