Skip to content

docs: prune the Help Wanted page, fix the uninstallable docs extra, quiet 5 Sphinx warnings - #408

Merged
JarryShaw merged 7 commits into
mainfrom
docs/prune-pep-and-fix-toc
Sep 16, 2026
Merged

JarryShaw merged 7 commits into
mainfrom
docs/prune-pep-and-fix-toc

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Three things: the [docs] extra could not install at all, pep.rst was asking for help with work already finished, and a handful of Sphinx warnings were ours rather than pre-existing.

The [docs] extra was uninstallable

pip install -e '.[docs]' failed outright, so nobody could build the documentation from a clean checkout:

  • sphinx-opengraph 404s on PyPI. The real package is sphinxext-opengraph, which is what docs/source/conf.py:61 imports.
  • sphinxcontrib-mermaid was missing from the extra entirely, though conf.py:65 loads it.

Both fixed; the extra now resolves (verified with pip install --dry-run, exit 0).

pep.rst was soliciting help for finished work

Which is worse than saying nothing — a contributor reads it and starts on something already shipped. Every claim was checked against the code before editing.

Solicitations removed, each verified present: SCTP (all 13 RFC 9260 chunk types, 8 parameters, 13 error causes, real CRC32c, PPID dispatch), both new engines, the logging integration, the test suite.

Narrowed rather than deleted, which is more useful than a stale "help wanted":

Item Now says
ESP the four missing algorithms, confirmed absent; 5 encryption and 5 integrity algorithms actually applied
Mobility Header FMIPv6 has landed, so the vague "requires some help" became: 10 of 24 message data types, 51 of 71 options, 3 of 4 CGA extensions
SCTP narrowed to the registered-but-unimplemented surplus: 17 of 30 chunk types, 24 of 32 parameters, 10 of 23 error causes
NotImplemented list kept, with NDP's stub corrected to link/ (not internet/) and QUIC dropped — no stub exists

Three places the page was simply wrong:

  1. It claimed BaseWarning still calls warnings.simplefilter. That was removed in 487d3da82, with tests. Deleted; the warn() double-reporting item is still true and stays.
  2. "84 test modules" — the real count is 86.
  3. "bundled with the distribution" — [tool.setuptools.packages.find] excludes test*, so an installed PyPCAPKit has no tests package. Now recorded as a still-wanted item rather than a boast.

The page also claimed to mirror discussion 106 while carrying 4 of its 6 comments; the missing two are added.

The pcap-ct paragraph said "a way out that has not been adopted yet". It has been adopted — as its own PCAP_CT engine in #405. It now describes what shipped, why a separate engine rather than a second backend (both distributions own the import name pcap; with both installed pcap-ct wins and upstream is shadowed), and the two caveats that remain: both are betas, and a runtime libpcap.so.1 is still required because the libpcap wheel's config sets LIBPCAP = None.

Sphinx warnings: 268 → 263

The TOC suspicion did not pan out, and that is worth recording so nobody re-investigates it. Across 128 pages: 0 orphans, 0 broken toctree entries, 0 automodule targets pointing at nothing, 0 unresolvable :doc: targets, 0 pages with multiple toctree parents. All 19 newly added modules resolve to a reachable page, and the const-enumeration convention is correctly followed — esp.rst and sctp.rst live under docs/source/pcapkit/const/, and the ESP protocol page only cross-references them rather than automodule-ing them.

What was actually fixed:

  • sphinx.ext.autodoc.typehints removed from extensions — an internal submodule with no setup(), so it only ever produced a warning. The real work is done by the third-party sphinx_autodoc_typehints.
  • The duplicate-object warnings on esp.rst and context.rst, the only two pages in the protocols/corekit tree using automodule. The cause needed two attempts: :no-index: alone removed only half of them, because the module docstring itself carries .. module:: — as 362 pcapkit modules do. Dropping the redundant .. module:: from the .rst and keeping :no-index: leaves the docstring as the single declaration. Both files carry a comment explaining why they deviate.

The 263 remaining are pre-existing and none are in the new modules, itemised so the next person doesn't have to re-count:

count cause
158 ambiguous cross-references — short names like ``:class:TransType``` matching both const.` and `vendor.`
~40 unresolvable forward references — TYPE_CHECKING-only names in annotations
38 duplicate object descriptions on established pages (ipv6_opts, hopopt, tcp, mh, …), 0 involving esp/sctp/context
22 guarded-import failures — environment-only, absent in CI with .[all]
5 autodoc signature crashes on __protocol_type__/__schema__

Left alone deliberately: those pages render correctly today, and fixing the duplicates means judgement calls about rendered output on pages this PR has no other reason to touch.

Not done here

The 158 ambiguous cross-references and ~40 forward references need docstring changes across pcapkit/, which is a separate sweep. Same for the 5 autodoc crashes.

…warnings

**`pep.rst` was soliciting help for work already done**, which is worse than
saying nothing to a would-be contributor. Every claim was checked against the code
before editing, not taken from a list.

Removed the solicitations for SCTP (all 13 RFC 9260 chunk types, 8 parameters, 13
error causes, real CRC32c, PPID dispatch), the two new engines, the logging
integration and the test suite -- all verifiably present. Narrowed rather than
deleted where the work is only partly done, which is more useful than a stale
"help wanted":

* ESP -- the four missing algorithms confirmed genuinely absent, and sharpened
  with the real numbers: 5 encryption and 5 integrity algorithms are applied.
* Mobility Header -- FMIPv6 *has* landed, so the vague "requires some help" became
  what actually remains: 10 of 24 message data types, 51 of 71 options, 3 of 4 CGA
  extensions.
* SCTP -- narrowed to the registered-but-unimplemented surplus: 17 of 30 chunk
  types, 24 of 32 parameters, 10 of 23 error causes.
* The `NotImplemented` list stays, with two corrections: NDP's stub is under
  `link/`, not `internet/`, and QUIC was dropped because no stub exists.

Three places where the page was simply wrong: it claimed `BaseWarning` still calls
`warnings.simplefilter` (removed in `487d3da82`, with tests); it said 84 test
modules where there are 86; and it claimed the suite is "bundled with the
distribution" when `[tool.setuptools.packages.find]` excludes `test*`, so an
installed PyPCAPKit has no `tests` package -- now recorded as a still-wanted item
rather than a boast. The page also claimed to mirror discussion 106 while carrying
4 of its 6 comments; the missing two are added.

The `pcap-ct` paragraph said "a way out that has not been adopted yet". It has
been adopted -- as its own `PCAP_CT` engine in #405 -- so it now describes what
shipped, why a separate engine rather than a second backend (both distributions own
the import name `pcap`, and with both installed pcap-ct wins while upstream is
shadowed), and the two caveats that remain: both are betas, and a runtime
`libpcap.so.1` is still required because the `libpcap` wheel's published config
sets `LIBPCAP = None`.

**The `[docs]` extra could not install at all.** It named `sphinx-opengraph`, which
404s on PyPI; the real package is `sphinxext-opengraph`, which is what
`docs/source/conf.py:61` imports. `sphinxcontrib-mermaid` was missing entirely
though `conf.py:65` loads it. So `pip install -e '.[docs]'` failed outright, which
means nobody could build the docs from a clean checkout. Both fixed and verified
to resolve.

**Sphinx warnings 268 -> 263.** The TOC suspicion did not pan out, and that is
worth recording: across 128 pages there are 0 orphans, 0 broken toctree entries, 0
`automodule` targets pointing at nothing, and 0 unresolvable `:doc:` targets. All
19 newly added modules resolve to a reachable page, and the const-enumeration
convention is correctly followed -- `esp.rst` and `sctp.rst` live under
`docs/source/pcapkit/const/`, and the ESP protocol page only cross-references them.

What was fixed: `sphinx.ext.autodoc.typehints` removed from `extensions` (an
internal submodule with no `setup()`, so it only ever warned; the real work is done
by the third-party `sphinx_autodoc_typehints`), and the duplicate-object warnings on
`esp.rst`/`context.rst`. Those two were the only pages in the protocols/corekit tree
using `automodule`, and the cause needed two attempts: `:no-index:` alone removed
only half, because the *module docstring itself* carries `.. module::`, as 362
pcapkit modules do. Dropping the redundant `.. module::` from the `.rst` and keeping
`:no-index:` leaves the docstring as the single declaration.

The 263 remaining are pre-existing and none are in the new modules: 158 ambiguous
cross-references (short names like ``:class:`TransType``` matching both `const.*`
and `vendor.*`), ~40 `TYPE_CHECKING`-only forward references, 38 duplicate object
descriptions on established pages, 22 guarded-import failures that are
environment-only, and 5 autodoc signature crashes on
`__protocol_type__`/`__schema__`. Left alone deliberately: those pages render
correctly today and fixing them means judgement calls about rendered output.
@JarryShaw
JarryShaw requested a lite review from Copilot September 16, 2026 04:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

Changes are limited to docs/dependency fixes and the updated dependencies align with the actual Sphinx extensions configured in docs/source/conf.py.

Pull request overview

This PR fixes the documentation build/install experience and updates the “Help Wanted” page to reflect current project status, while addressing a small set of Sphinx warnings originating in this repo.

Changes:

  • Fix the .[docs] optional dependency set by replacing the missing sphinx-opengraph package and adding the missing Mermaid dependency.
  • Prune and correct docs/source/pep.rst so it no longer solicits work that has already landed and better reflects current gaps.
  • Reduce Sphinx warnings by removing an invalid extension entry and avoiding duplicate module declarations in two automodule pages.
File summaries
File Description
pyproject.toml Fixes docs extra dependency resolution (opengraph + mermaid).
docs/source/pep.rst Updates “Help Wanted” content to match current implementation status and remaining gaps.
docs/source/pcapkit/protocols/internet/esp.rst Avoids duplicate Sphinx object registration by removing redundant .. module:: and using :no-index:.
docs/source/pcapkit/corekit/context.rst Same duplicate-registration mitigation pattern as esp.rst.
docs/source/conf.py Removes sphinx.ext.autodoc.typehints from extensions to eliminate a warning and rely on sphinx_autodoc_typehints.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/source/pep.rst Outdated
Copilot's review on #408 was right. The intro said proposals are recorded
"here in the discussion thread" and then "maintained here", conflating the page
with the thread -- and once this branch added an ``important`` block that
carefully distinguishes the two, the paragraph directly contradicted the note
three lines above it.

Reworded so "the discussion thread" and "this page" are always distinct: the
proposals were *raised* in the thread, and the page is where they are *kept up to
date*. Also fixed the tense, since 16k lines and 800 commits are long past, and
pointed the questions sentence at real links rather than a bare "this thread"
that had no referent on a rendered page.
@JarryShaw

Copy link
Copy Markdown
Owner Author

Fixed in 6a62710be — the review was right, and the contradiction was sharper than it looked.

The intro said proposals are recorded "here in the discussion thread" and then "documented and maintained here", conflating the page with the thread. And because this branch added an .. important:: block that carefully distinguishes the two, that paragraph ended up contradicting the note three lines above it.

Reworded so "the discussion thread" and "this page" are always distinct: the proposals were raised in the thread; the page is where they are kept up to date. Two things fixed while there:

  • The tense — 16k lines and 800 commits are long past, so "As PyPCAPKit reaches" became "reached".
  • "please leave a note either in this thread" had no referent on a rendered page, since the page is not the thread. It now links the discussion and the Q&A category explicitly.

rstcheck clean on the file.

…odule

These two were the **only two of 128 pages** using `automodule`; every other page
declares `.. module::` and then lists its members explicitly. Converted to match:
the descriptive prose is carried in the `.rst`, and each class, function and data
item gets its own entry.

That also removes the `:no-index:` workaround this branch had added, for a better
reason than suppressing a symptom -- the duplicate-object warnings were *created
by* `automodule`, not by two `.. module::` directives coexisting.

Which answers the question the workaround raised. 365 modules carry `.. module::`
in their docstring and 122 of 128 pages carry one too, and that is not a
duplication: **without `automodule`, Sphinx never parses the module docstring at
all**, so the docstring's directive is inert text in a `.py` file and the page's is
the only registration. `automodule` was what made them collide.

Two traps the conversion had to handle, either of which would have silently
degraded the page:

* `esp.py`'s docstring defines the `|cryptography|` substitution and its link
  target, and **eight rendered member docstrings reference them** --
  `load_cryptography`, `_CRYPTO`, `CipherSuite.requires_cryptography`,
  `ESPStatus.UNSUPPORTED`, `SecurityAssociation.decrypt`/`encrypt`. `automodule`
  had been supplying those definitions invisibly; dropping it without carrying
  them into the page would have broken every one.
* Both pages were rendering their title **twice**, because `automodule` emitted the
  docstring's own title as a second heading. Now gone.

Verified rather than assumed. Full builds before and after: 267 total, 263 warning
lines, and a `diff` of the sorted warning sets is byte-for-byte identical -- zero
new, zero removed. Neither page had a warning attributed to it either way.

Member coverage checked by grepping every `id="pcapkit…"` out of the built HTML:
`context.html` 13 -> 14, `esp.html` 74 -> 75, **nothing lost**; the additions are
`_CT` and `_CRYPTO`, which `automodule` had skipped. The rendered visible text was
diffed too: every sentence, table row and footnote survives.
…a decision

Two corrections to the Test Cases section, both from measurement.

The module count said 86; `find tests -name 'test_*.py'` counts **91**. The number
has been wrong twice now (84 before this branch, 86 after), which is an argument
for not quoting it at all -- but it is useful context for a contributor, so it is
corrected rather than dropped.

"Shipping the suite ... is not done" framed a deliberate choice as an omission.
Built both artefacts to settle it: the **sdist carries all 91 modules**, the
**wheel carries none**. So a distribution packager building from source has them,
and `pip install pypcapkit` does not.

Keeping the wheel lean is the right call, and the reason is stronger than "wheels
don't usually ship tests": the suite *could not run* from an installed package
anyway. The generated sample captures are not shipped, and `tests/_tiers.py`
resolves paths from a repository root that an installed package does not have.
Anyone who wants to run the tests wants the repository. Reworded to say that,
rather than leaving a standing invitation to "fix" it by stuffing tests into the
wheel.

That left a single remaining item under a "Two things are still wanted" heading, so
the list became a sentence.
@JarryShaw
JarryShaw merged commit da9d6e1 into main Sep 16, 2026
24 checks passed
JarryShaw added a commit that referenced this pull request Sep 16, 2026
…rate (#417)

* foundation: recover a failed next layer through the protocol, not the schema

`beholder` read the fallback payload from `self.__header__.get_payload()`. That
is what `ProtocolBase._get_payload()` wraps, so the two agree for every protocol
whose payload is a schema field -- and disagree for the two that override it.
SCTP carries user data inside a DATA chunk and PCAP-NG inside a block, so
neither header schema has a `payload` field at all, and reaching for the schema
raises `ProtocolUnbound("unknown field: 'payload'")` *from the recovery path*.
A next-layer parse failure that should have degraded to `Raw` became a crash
that aborted the frame.

It was unreachable until now only because `SCTP.__proto__` had no entries: with
nothing registered on a payload protocol identifier, no next-layer parse could
fail. Registering anything on one exposes it immediately.

- `beholder` now calls `self._get_payload()`, the wrapper both overrides.
- It also forwards `alias=proto`, which the success path in
  `_import_next_layer` already passes. Without it, a payload that failed to
  parse came back as a bare `Raw` while an *unregistered* number came back
  named -- so registering a protocol made the output less informative than
  leaving the number alone. A plain integer has no `name` and still renders as
  `Raw`, so this only adds a name where the registry key is an enumeration.

The four `beholder` tests are rewritten onto shared stand-ins that carry a
`_get_payload`, as a real protocol does, plus two new ones: the alias forwarding
and an overridden `_get_payload` whose schema accessor raises, which is the SCTP
shape.

Unit tier green, and mypy reports the same five pre-existing errors in
`decorators.py` as it does at HEAD -- no new ones.

* protocols: implement NGAP over SCTP, decoding aligned PER through pycrate (#251)

NGAP (3GPP TS 38.413) is the 5G RAN-to-AMF control plane. It has no header of
its own: an SCTP DATA chunk whose payload protocol identifier names NGAP carries
exactly one aligned-PER `NGAP-PDU` and nothing else, so nothing about it is
visible until an ASN.1 decoder has run.

- `pcapkit.protocols.application.ngap`, with the matching schema and data
  models, and exports added everywhere `FTP` is listed.
- Registered on `SCTP.__proto__` as a *default*, for PPID 60
  (`NG_Application_Protocol`) and 66 (`NGAP_over_DTLS_over_SCTP`), following how
  `TCP.__proto__` declares 21 and 80 rather than calling `register_sctp` at
  import time from somewhere.
- `pycrate` is an optional extra, `pip install pypcapkit[NGAP]`, imported inside
  the parse path and never at module import, and deliberately excluded from
  `all` -- it lands ~238 MB of `pycrate_asn1dir` (87 spec modules) to obtain the
  one 4.9 MB `NGAP.py`, and it is LGPL-2.1+ where this package is BSD-3-Clause.
  Absent, it fails the way `pcapkit.protocols.internet.esp` fails without
  `cryptography`: a `ProtocolError` that `beholder` degrades to `Raw`.
- Decoding is generic rather than per-procedure: the value tree is mapped by
  ASN.1 *shape*, so all 81 elementary procedures and 438 protocol IEs work
  without 519 hand-written cases and a new 3GPP release needs no code change.
  The PDU kind, procedure code, criticality, message type name and IE list are
  surfaced as first-class fields.
- `ProcedureCode`, `Criticality` and `ProtocolIE` live in `ngap.py`, not
  `pcapkit.const`: 3GPP publishes them in the ASN.1 of TS 38.413 rather than in
  a crawlable registry, so there is no vendor module either. The SCTP PPIDs are
  IANA's and are used from `pcapkit.const.sctp` as they stand.

`reset_val()` is never called on the parse path, and there is a comment plus a
test saying so: it walks all 318 submodules of the compiled specification and
costs ~102 ms against the decode's ~0.15 ms, and `from_aper()` overwrites the
value anyway. The module-level PDU object is stateful and `get_val()` hands back
its own containers, so a lock is held across the decode *and* the conversion.

Five SCTP tests used PPID 60 as an arbitrary placeholder with junk payloads and
now describe the registered default instead; their `SCTP.__proto__.clear()`
teardowns are replaced with a snapshot restore, since clearing the registry now
destroys the defaults for every later test in the process.

Unit tier: 670 passed with pycrate, 613 passed / 65 skipped without it, no
failures either way. Round trip verified byte-exact on a real 58-octet
`NGSetupRequest`.

* docs: add the NGAP protocol page (#251)

Follows the convention the sibling application pages use: the module docstring
copied into the `.rst` under a `.. module::` directive, then `autoclass` per
class, rather than `automodule` -- which was the review feedback on #408.

`ProcedureCode` and `ProtocolIE` are rendered with `:no-members:`. Between them
they carry 519 members, each named for the 3GPP identifier it came from, and a
page listing all of them is longer than the specification's own tables and no
more useful; a note says where to read them instead, and why there is no
matching `pcapkit.vendor` module.

No `pycrate` intersphinx mapping: the project publishes no `objects.inv`, so the
link is a plain external reference through the same `|pycrate|_` substitution
`esp.rst` uses for `cryptography`.

One line added to the application-layer toctree.

Sphinx build verified against a clean export of HEAD with the same interpreter:
285 warnings before, 285 after -- none added, none removed.

* tests: update the Raw fallback assertion the beholder alias fix invalidates

CI's Integration leg failed on all six interpreters with one real failure:
`test_malformed_http_payload_falls_back_to_raw` asserted
`tcp.payload.info.protocol is None`, and forwarding `alias` from `beholder` makes
it 80. Neither the unit tier nor the local runs cover that tier, which is how it
reached CI.

The new value is the right one -- `Data_Raw.protocol` is "the original enumeration
of this protocol", and 80 is what the payload arrived as -- so the assertion is
updated rather than the fix reverted. The protochain still reads `Raw`, since a
plain int has no name to render.

The comment justifying the forward was too broad, though, and is now measured
rather than asserted. It claimed an unregistered code keeps its name generally.
True on SCTP (an unknown PPID gives `SCTP:Unassigned_4243` and `protocol=4243`,
which is why registering NGAP would otherwise have made a failed parse *less*
informative than leaving PPID 60 alone) and on IP, but false on TCP, where
`Transport._decode_next_layer` resolves ports through `__proto__` and reaches Raw
without an alias, so an unknown port already reports `None`. So the failure path
is now uniform across layers while the unknown-code paths remain inconsistent
with each other -- filed as #418.

Full suite, the command CI's Integration leg runs: 806 passed, 17 skipped.

* ngap: correct the procedure count, and say that PPID 66 does not decode

Four review findings on #417, all of them right.

The "not 51 hand-written procedures" heading in `ngap.py` and `ngap.rst` was
wrong: `ProcedureCode` has 81 members, and 51 was a bad number from the brief
that the enums themselves already corrected. Now 81 in both, which is the same
character count, so the RST underline is unaffected.

The other two are accurate-but-misleading-by-omission. `SCTP`'s class docstring
said both PPIDs are registered to NGAP without saying that only 60 can decode: a
PPID 66 payload is an NGAP PDU inside a DTLS record, and with no DTLS
implementation those bytes are never aligned PER, so 66 degrades to `Raw` on
every capture rather than only when `pycrate` is absent. The module docstring and
the docs page already said so; the class docstring, which is where someone
reading `SCTP.__proto__` looks, did not.

`test_ppid_dispatch_hook`'s docstring had the same gap, and its assertion is
deliberately no stronger than "both dispatch" -- now stated, so the test is not
read as claiming both decode.

56 tests pass across the SCTP and NGAP files.

* ngap: drop Criticality.get's unused default, and report one error type

Copilot is right on #417: `Criticality.get` declared and documented a `default`
it never used. An unknown name raised `KeyError`, an unknown value `ValueError`
via `_missing_`, and `default` reached neither path.

The behaviour is intended -- `Criticality` is an ASN.1 `ENUMERATED` with no
extension marker, so a fourth value is unencodable and a lookup for one is a bug,
which is why `_missing_` raises deliberately. It is the parameter that was wrong,
not the closedness, so the parameter is gone. No caller passed it: all five call
sites use one argument.

Its siblings keep theirs, and the docstring now says why rather than leaving the
difference to be rediscovered -- `ProcedureCode` and `ProtocolIE` extend through
`extend_enum` for a code a newer specification names, which is right for a
registry that grows and impossible for one that cannot.

Also made the unknown-name path raise `ValueError` like the unknown-value path,
so the two ways of getting this wrong no longer report differently.

Verified: signature carries no `default`, valid int/str/member lookups unchanged,
both unknown forms now `ValueError`. 22 NGAP tests and the SCTP file pass.
@JarryShaw
JarryShaw deleted the docs/prune-pep-and-fix-toc branch September 17, 2026 01:06
@JarryShaw JarryShaw added the docs Pull requests that change documentation only (docs: subject prefix) label Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Pull requests that change documentation only (docs: subject prefix)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants