diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index fd050c0e10..831882f33d 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -5,8 +5,8 @@ # the web UI for no gain. # # The security link exists because ``SECURITY.md`` asks reporters not to open a -# public issue for anything exploitable, and until now the chooser offered no way -# to comply -- the only visible route was the public bug template. +# public issue for anything exploitable, and without it the chooser would offer +# no way to comply -- the only visible route would be the public bug template. blank_issues_enabled: true diff --git a/.github/coverage.toml b/.github/coverage.toml index 5322ca9ba7..6c0671255a 100644 --- a/.github/coverage.toml +++ b/.github/coverage.toml @@ -1,7 +1,7 @@ # Coverage configuration for the one coverage-measured leg of unit-tests.yml # (`test`, Python 3.15), passed with `--rcfile`. Coverage reads a single config # file, so this repeats pyproject.toml's [tool.coverage.*] settings and adds the -# three below on top. tests/project/test_coverage_rcfile.py asserts every +# four below on top. tests/project/test_coverage_rcfile.py asserts every # pyproject setting is repeated here unchanged. # # parallel -- each process writes its own .coverage.* file, merged afterwards diff --git a/.github/python-versions.toml b/.github/python-versions.toml index e4923f8701..fafc50472f 100644 --- a/.github/python-versions.toml +++ b/.github/python-versions.toml @@ -5,8 +5,8 @@ # and proposes each change, and the ruleset 23497679 edit it needs, as a PR. # # Tiers: supported (blocking), ktlo (end-of-life, non-blocking), pre-release -# (non-blocking; none listed today) and experimental (non-blocking, a workflow -# of their own; see below). `runner` is the image a version needs when +# (non-blocking; python_versions.py refuses it) and experimental (non-blocking, a +# workflow of their own; see below). `runner` is the image a version needs when # ubuntu-latest cannot provide it. `latest` is the newest supported CPython, # which the single-interpreter jobs run on. See docs/source/contributing/workflows.rst. @@ -62,8 +62,8 @@ job = "conda" where = "Conda environment creation and activation: with.python-version" value = "3.15" -# As for create-release.yml's conda job: conda-forge's conda has no py315 build -# yet. +# As for create-release.yml's conda job: conda-forge's conda has no py315 +# build. [[exemptions]] workflow = "cron-conda.yml" job = "conda-dist" diff --git a/.github/release.yml b/.github/release.yml index 8e775494f4..a9bd48c2e6 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -11,12 +11,12 @@ # matching on the `type(scope):` subject prefix this project actually uses, so # the type a pull request announces in its title cannot reach this file. # -# A labelling programme has since put a type label on every merged pull request -# (zero exceptions, and every new one too), so the old survey this file cited -- -# 40 pull requests, mostly untyped, hence a bare Dependencies/catch-all split -- -# is stale, and a category per label is now possible. Sampling 334 merged pull -# requests (`gh pr list --state merged --limit 400`) gives the frequency below; -# zero carry no type label: +# Merged pull requests carry a type label, set by hand, which is what makes a +# category per label possible -- an earlier survey of 40 pull requests, +# mostly untyped, supported only a bare Dependencies/catch-all split. +# +# Sampling 334 merged pull requests (`gh pr list --state merged --limit 400`) +# gives the frequency below; zero carry no type label: # # fix 124, dependencies 97, breaking 50, ci 41, test 31, docs 28, feat 12, # release 10, chore 9, perf 7, const 2, refactor 2 @@ -67,8 +67,8 @@ # of everything else on a first read. This file takes the former; flip its # position in the list below if that judgement is wrong. # -# `*` catches whatever matches none of the categories above -- currently -# nothing, since every merged pull request carries a type label, but it stays +# `*` catches whatever matches none of the categories above -- nothing in the +# sample, since every pull request in it carries a type label, but it stays # last regardless so an unlabelled future pull request still lands somewhere # instead of silently vanishing from the notes. # diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 92e9708c81..e8b2e2c217 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -22,7 +22,7 @@ jobs: # against a commit the Unit Tests workflow is already testing, and it bought # nothing: CodeQL reads the source, so its findings do not depend on the test # suite passing. Worse, it meant a failing test suppressed the security - # analysis for that commit. Static analysis now runs on its own. + # analysis for that commit. Static analysis runs on its own. analyze: name: Analyze runs-on: ubuntu-latest diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 2d37271c31..528582d535 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -22,7 +22,7 @@ name: Create Release # #1705: a group holds one pending run and cancels it when another joins, so a # run whose Vendor Update did not conclude ``success`` -- one ``version_check`` # skips, so it releases nothing -- is keyed on its own ``github.run_id``. Keyed on -# the ref, such a run, 38499295798, displaced the pending 38499150441. Now only a +# the ref, such a run, 38499295798, displaced the pending 38499150441. So only a # run that may release can wait in ``main``'s group or displace the one waiting # there, evaluating ``main``'s newer tip instead. A tag push keeps its tag's group. # GitHub documents the one pending run, and ``github.run_id`` as unique per run: @@ -32,8 +32,8 @@ concurrency: cancel-in-progress: false # Every job below with an effect outside this run declares a deployment -# ``environment:``, but as of #887 only one of them -- ``github-release`` -- -# carries a required reviewer. Four jobs still get four environments rather than +# ``environment:``, but only one of them -- ``github-release`` -- carries a +# required reviewer (#887). Four jobs still get four environments rather than # one shared ``release``, for credential and blast-radius separation, not for # approval count: # @@ -43,7 +43,7 @@ concurrency: # pypi -> pypi an upload to PyPI, and to TestPyPI # conda -> anaconda an upload to anaconda.org/jarryshaw # -# Approving the GitHub Release is now the release's *only* approval. +# Approving the GitHub Release is the release's *only* approval. # ``tag`` and ``pypi`` both carry ``needs: [ github, ... ]``, and ``conda`` needs # ``tag`` and ``github`` too, so nothing downstream of ``github`` can start before # that approval lands, and rejecting it leaves nothing tagged and nothing @@ -52,7 +52,7 @@ concurrency: # # The other three environments keep their names -- ``pypi``'s matters because # PyPI trusted publishing matches its OIDC claim against it -- and keep their -# secret scoping, they just no longer hold a required reviewer of their own. Do +# secret scoping, they just hold no required reviewer of their own. Do # not add one back to any of them without re-examining the ``needs:`` chain: a # reviewer on ``conda-tag`` with its old ``needs: [ version_check ]`` is exactly # the bug #887 fixed, because it let the commit-to-``main`` and ``conda-*`` tag @@ -100,7 +100,7 @@ concurrency: # ``workflow_run``-triggered attempt reads a tag that already exists and skips # every job, reporting green. ``github`` still uses that guard for its own # artefact -- the tag it checks is the one it is about to create -- but ``tag``, -# ``pypi`` and ``conda`` now each read their own evidence instead: ``tag`` checks +# ``pypi`` and ``conda`` each read their own evidence instead: ``tag`` checks # for the ``conda-+0`` tag it creates, ``pypi`` checks PyPI's file count # for the version against the count expected, and ``conda`` both checks Anaconda's # aggregate count as a job-level cost saver and, per leg, whether that leg's own @@ -114,7 +114,7 @@ concurrency: # ``tag``), and GitHub Actions skips a job whose ``needs:`` included a job that # itself skipped, *before* that job's own ``if:`` is even evaluated -- unless the # ``if:`` calls a status-check function itself. ``github`` legitimately skips -# whenever its own tag already exists, which is exactly the retry case this fix is +# whenever its own tag already exists, which is exactly the retry case #888 is # for, so ``tag``, ``pypi`` and ``conda`` all reach for the same escape hatch # ``unit-tests``'s callers already use for the identical problem -- # ``cron-vendor.yml``, ``deploy-pages.yml`` and ``cron-conda.yml`` all open with @@ -149,7 +149,7 @@ concurrency: # is going to be published. Every successful Vendor Update triggers this # workflow, and most of them leave ``__version__`` alone, so the gate used to # run on commits Unit Tests had already passed only for all four jobs below to -# skip (eight times on 2026-10-05 alone). ``unit-tests`` now runs when the ref +# skip (eight times on 2026-10-05 alone). ``unit-tests`` runs when the ref # is a ``v*`` tag or *any* of the four evidence outputs is not ``'true'``, # which covers every case in which ``github``, ``tag``, ``pypi`` or ``conda`` # could run: each of them runs only on a ``v*`` ref or on its own evidence @@ -157,7 +157,7 @@ concurrency: # an evidence output nothing set runs the gate rather than skipping it, which # can cost a run but never a release. # -# Moving the gate off the front of the chain means it no longer gates by +# Moving the gate off the front of the chain means it does not gate by # position, so every publishing job names it instead: ``github`` lists it in # ``needs:`` and keeps the implicit ``success()`` (its ``if:`` calls no # status-check function), and ``tag``, ``pypi`` and ``conda`` -- whose @@ -221,12 +221,12 @@ jobs: # read leaves it unset, and ``version_check`` runs as before. # # The concurrency group above cannot see this: it reads only the event, and - # that Vendor Update concluded ``success``, so the run still joins ``main``'s - # group and can still displace a run pending there, as before. It used to - # evaluate ``main``'s newer tip in its place; it now releases nothing, so the - # displaced run's release waits for the next run on ``main`` -- the tip's own - # Vendor Update if the tip's Unit Tests run has not completed yet, else the - # next push's or Saturday's. + # that Vendor Update concluded ``success``, so the run joins + # ``main``'s group and can displace a run pending there. It releases + # nothing in that run's place, so the displaced run's release waits for + # the next run on ``main``: the tip's own Vendor Update if the tip's + # Unit Tests run has not completed yet, else the next push's or + # Saturday's. vendor_update: name: Triggering Vendor Update runs-on: ubuntu-latest @@ -446,10 +446,10 @@ jobs: # Python 3.14 -- every earlier interpreter (3.9 through 3.13, # measured directly) raises `IndentationError: unexpected indent` # on the leading whitespace this YAML block scalar's own - # indentation would otherwise force onto the source. This step - # runs before `actions/setup-python@v7`, so `python3` here is - # whatever the runner image ships (3.12 on ubuntu-latest as of - # writing), not 3.14. Feeding one line per `printf` argument + # indentation would otherwise force onto the source. This job's + # setup-python step installs `latest` first, but the snippet does + # not lean on that; the conda job's check runs before its own. + # Feeding one line per `printf` argument # keeps each line's YAML indentation off the actual Python source # `python3 -` reads from stdin, so nothing is indented that # Python did not itself put there. @@ -786,7 +786,7 @@ jobs: runs-on: ubuntu-latest # Pushes a commit to ``main`` and the ``conda-*`` tag. ``permissions: {}`` # does not constrain it -- the write goes through the deploy key and PAT below, - # not the run's token. It carried its own required reviewer until #887; now it + # not the run's token. It has no required reviewer of its own (#887), and # depends on ``github`` rather than on ``version_check`` alone, precisely so # the single ``github-release`` approval still gates it -- without that # ``needs:``, this job would start the moment ``version_check`` finished, @@ -954,7 +954,7 @@ jobs: # ``id-token: write`` that *enables* trusted publishing was re-added below, so # the block read as disabled as a unit when only its gate still was. #887 # removed this environment's own required reviewer; it is gated only - # transitively now, through ``needs: [ github, unit-tests, version_check ]`` + # transitively, through ``needs: [ github, unit-tests, version_check ]`` # below, and the name still matters because PyPI's trusted-publishing claim # match is keyed on it. # @@ -991,7 +991,7 @@ jobs: # axis, whose wheels version_check counts. The KTLO tier -- end-of-life, # still built and published but never blocking -- is added by # ``include``, without the versions that need an older runner than this - # job's ``ubuntu-latest``: Python 3.10 language level today. Each step + # job's ``ubuntu-latest``, leaving the 3.10 language level. Each step # that can fail on one carries `continue-on-error: ${{ matrix.ktlo == # true }}`, the step-level form, so ``needs.pypi.result`` cannot see it # -- see the same choice in unit-tests.yml's ``test`` job. #1672: each @@ -1190,7 +1190,7 @@ jobs: # Uploads to anaconda.org/jarryshaw with ``ANACONDA_TOKEN``. This job never had # an ``environment:`` at all, not even a commented one, so it was the half of # #641 that was not written down as a thing to turn on. #887 later removed - # this environment's own required reviewer; gating is transitive now, through + # this environment's own required reviewer; gating is transitive, through # ``needs: [ tag, github, unit-tests, version_check ]`` below. See the note # above ``jobs:``. # diff --git a/.github/workflows/cron-conda.yml b/.github/workflows/cron-conda.yml index ec1f23f92f..e2625b7215 100644 --- a/.github/workflows/cron-conda.yml +++ b/.github/workflows/cron-conda.yml @@ -38,7 +38,7 @@ concurrency: cancel-in-progress: false jobs: - # Only earns its runners on the schedule trigger now: `Unit Tests` has no + # Only earns its runners on the schedule trigger: `Unit Tests` has no # schedule trigger of its own, so Saturday's cron run is the one case left # where nothing else has already verified this tree. `workflow_run` carries # its own verdict instead -- see `conda-update` below. @@ -95,7 +95,7 @@ jobs: # skip. `!cancelled()` rather than `always()` so that cancelling the run # still cancels the update; a gate that actually failed still blocks. # - # The `workflow_run` clause is the real gate for a push to main now: it + # The `workflow_run` clause is the real gate for a push to main: it # reads the verdict `Unit Tests` already reached instead of re-running it, # and `verdict` says whether that verdict covers the commit checked out # below (#1643). `.event == 'push'` and `.head_branch == 'main'` @@ -162,7 +162,7 @@ jobs: # which raises unless that distribution is installed here -- and that # file names `dpkt`, `scapy`, `pyshark`, `requests` and `beautifulsoup4` # with no marker. #910 narrowed `all` to core addons only, so those five - # now come from `dev` instead; without it this step would install + # come from `dev` instead; without it this step would install # cleanly and the next one would crash on the first missing name. - name: Install and Setup run: | @@ -219,7 +219,7 @@ jobs: # drops it as "previously applied", while `files_changed` still reads # 'true'. Conda Update 38325136710 and 38325153882 were such a pair, both # on 2e941bbb7 and raised 2 s after two different `Unit Tests` runs, for - # 9083163f5 and for 01151b13f, completed; `verdict` now refuses both. + # 9083163f5 and for 01151b13f, completed; `verdict` refuses both. # Asking about that file rather than "is a commit left to push" also # covers a rebase that keeps part of the commit but drops its bump: that # remainder would land under a build already tagged, so it is not pushed, diff --git a/.github/workflows/cron-vendor.yml b/.github/workflows/cron-vendor.yml index efed66cb20..a7d5f79d9c 100644 --- a/.github/workflows/cron-vendor.yml +++ b/.github/workflows/cron-vendor.yml @@ -15,7 +15,7 @@ on: workflows: ["Unit Tests"] types: [completed] -# Least privilege by default now that `workflow_run` is in the trigger list -- +# Least privilege by default, since `workflow_run` is in the trigger list -- # that event can be raised by a pull request's `Unit Tests` run completing, so # the ambient token this workflow gets should default to nothing rather than # whatever the repository's default is. Both jobs below already declare their @@ -44,7 +44,7 @@ concurrency: cancel-in-progress: false jobs: - # Only earns its runners on the schedule trigger now: `Unit Tests` has no + # Only earns its runners on the schedule trigger: `Unit Tests` has no # schedule trigger of its own, so Saturday's cron run is the one case left # where nothing else has already verified this tree. `workflow_run` carries # its own verdict instead -- see `vendor-update` below. @@ -100,7 +100,7 @@ jobs: # skip. `!cancelled()` rather than `always()` so that cancelling the run # still cancels the update; a gate that actually failed still blocks. # - # The `workflow_run` clause is the real gate for a push to main now: it + # The `workflow_run` clause is the real gate for a push to main: it # reads the verdict `Unit Tests` already reached instead of re-running it, # and `verdict` says whether that verdict covers the commit checked out # below (#1643). `.event == 'push'` and `.head_branch == 'main'` @@ -203,7 +203,7 @@ jobs: # would already have failed to create the log file at all -- # `cat` on a missing file returns 1 under `-e` and would abort the # whole step right here, exactly the "one dead crawler kills every - # good regeneration" failure mode this PR exists to remove. + # good regeneration" failure mode this handling exists to remove. cat "$RUNNER_TEMP/vendor-stderr.log" >&2 || true if [ "$vendor_exit" -ne 0 ]; then diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index b4795a8119..8250c8833c 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -42,7 +42,7 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: - # Only earns its runners on the schedule trigger now: `Unit Tests` has no + # Only earns its runners on the schedule trigger: `Unit Tests` has no # schedule trigger of its own, so Saturday's cron run is the one case left # where nothing else has already verified this tree, and a gate is still # needed. Pull requests never published anyway, and `workflow_run` carries @@ -94,12 +94,12 @@ jobs: deploy-pages: needs: [ unit-tests, verdict ] # `needs` cannot itself be conditional, so a skipped gate (pull requests, - # and now `workflow_run`) has to be accepted explicitly -- by default this + # and `workflow_run`) has to be accepted explicitly -- by default this # job would inherit the skip. `!cancelled()` rather than `always()` so that # cancelling the run still cancels the docs build; a gate that actually # failed still blocks. # - # The `workflow_run` clause is the real gate for a push to main now: it + # The `workflow_run` clause is the real gate for a push to main: it # reads the verdict `Unit Tests` already reached instead of re-running it, # and `verdict` says whether that verdict covers the commit checked out # below (#1643). `.event == 'push'` and `.head_branch == 'main'` diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6a6c05624d..8aab094c9a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -47,16 +47,15 @@ name: "Lint" # 6039/6040/6043/6046/6056. Combined: R 642-662, total 6039-6059 # [1a852698b]. The floor matches the pre-#767 range's own corrected floor # exactly (642 both ways) because it is the same tree state either side of -# that diff -- the range only looks lower now because three of its messages -# have been deleted, not because the code changed. Already superseded the +# that diff -- the range only looks lower because three of its messages were +# deleted, not because the code changed. Already superseded the # same day it was measured, too: a cross-review measured `R0801 # duplicate-code` alone moving 400 -> 573 on `merge(f31e5114f, 83b58ebda)` -- # this branch merged onto the `main` it was rebased against before #768 -# landed there -- taking R to 823 and the total to 6220 on that merge. Still -# true of `main` itself today, since this PR touches no `pcapkit/` file, but -# named for the tree it was actually measured on rather than asserted of -# `main` directly. This range is a moving target to re-measure, never a -# constant to defend. +# landed there -- taking R to 823 and the total to 6220 on that merge. That +# figure is named for the tree it was actually measured on rather than +# asserted of `main` directly. This range is a moving target to re-measure, +# never a constant to defend. # # The pin above moved from 932cb48d1 to 1a852698b because `pcapkit/` is not # actually unchanged between them, despite what an earlier revision of this @@ -65,7 +64,7 @@ name: "Lint" # `pcapkit/vendor/reg/apptype/apptype.py`; tree 7270ad50 -> 2b9ac808), #758's # port-validation logic among them. E, W and C read identically either side of # that diff -- 90/4765/542 both times -- but 1a852698b is the tree actually -# re-measured for this line, so that is what it is pinned to now, rather than +# re-measured for this line, so that is what it is pinned to, rather than # repeating a byte-identity claim that does not hold. # # Four of `PYLINT_FLAGS`' `--disable=` entries named checks pylint has @@ -77,8 +76,8 @@ name: "Lint" # was as unrecognised as if the check had been removed (-> `W0012 # unknown-option-value`). Same shape of breakage as `emptystring` below, a # different cause. All four are dropped either way -- loading the fifth plugin -# to enable `eq-without-hash` properly would find nothing today (`W1641` is 0 -# over `pcapkit/`), so there is nothing lost in dropping it instead. +# to enable `eq-without-hash` properly found nothing when measured (`W1641` +# was 0 over `pcapkit/`), so there is nothing lost in dropping it instead. # # `--load-plugins=` also named `pylint.extensions.emptystring`, which no # longer exists in the installed pylint (-> `E0013 bad-plugin-value` every @@ -100,7 +99,7 @@ name: "Lint" # the same run -- the figures above are both the raw and the file-scoped # count, for E, W and C alike. # -# None of the four is clean, so none can block today: a job that is red the day +# None of the four is clean, so none can block: a job that is red the day # it lands teaches everyone to scroll past red, which costs more than the checks # are worth. Advisory steps keep the numbers visible (each writes its count to # the run summary) without that damage, and promoting one to blocking is a matter @@ -109,8 +108,8 @@ name: "Lint" # Promotion order, cheapest first: # * bandit -- 8 findings, and 7 are B104 "binding to all interfaces" on # `address: ... = '0.0.0.0'` *default arguments* in MH/MIP option builders, -# which bind no socket. This package already uses `# nosec` (16 findings are -# suppressed that way today), so triaging 8 comments is the whole job. +# which bind no socket. This package already uses `# nosec` (16 findings +# were suppressed that way when measured), so triaging 8 is the whole job. # * mypy -- 24 of the 112 are `unused-ignore`, i.e. `# type: ignore` comments # that are no longer needed. Deleting those is risk-free and cuts a fifth. # * vermin -- blocked on a decision, not on cleanup: see the step below. @@ -126,7 +125,7 @@ name: "Lint" on: # Deliberately *not* `push: [main]`, which is the trigger the other validation # workflows here use. Those gate something: a failing unit-test or CodeQL run - # on main is a commit someone must look at. These steps gate nothing yet, so a + # on main is a commit someone must look at. These steps gate nothing, so a # second advisory run per merge -- on a commit that already got one on its pull # request -- would buy no information while competing for runners that are # visibly oversubscribed (pull requests on this repository have been sitting at @@ -246,15 +245,15 @@ jobs: exit $status - # `vermin-ci`, not `vermin`: the `vermin` target redirects into temp/ and - # hands the file to an editor, so its exit status is the editor's and a run - # that found violations still succeeds. Same flags, via $(VERMIN_FLAGS). + # `vermin-ci`, not `vermin`: the `vermin` target redirects the report into + # temp/, so a run that found violations stops at that line with the report + # in a file rather than in this log. Same flags, via $(VERMIN_FLAGS). # # This one needs a decision rather than a cleanup before it can block, and # the decision is not CI's to make. vermin.ini sets `targets = 3.6` while # the code's real floor is 3.11 -- `enum.StrEnum` and - # `enum.show_flag_values` are 3.11 members, and 86 modules import - # `typing_extensions`. pyproject.toml's `requires-python` still claims + # `enum.show_flag_values` are 3.11 members, and 93 modules import + # `typing_extensions`. pyproject.toml's `requires-python` claims # `>=3.6, <4`, so the declared floor and the actual one are five releases # apart. Whichever way that is resolved -- raise `requires-python`, or # raise `targets` to match it -- belongs in its own change. diff --git a/.github/workflows/python-compatibility.yml b/.github/workflows/python-compatibility.yml index d421a7d0e7..0e0f31f4a5 100644 --- a/.github/workflows/python-compatibility.yml +++ b/.github/workflows/python-compatibility.yml @@ -45,8 +45,8 @@ jobs: run: python3 util/python_versions.py --github-output compatibility: - # Named "Python ${{ matrix.python-version }}" until now, which collided - # exactly with the Unit Tests matrix job of the same name: `gh pr checks` + # Not named "Python ${{ matrix.python-version }}": that name collided + # exactly with the Unit Tests matrix job of the same name, and `gh pr checks` # showed two rows reading `Python 3.10`, one a 13-second import smoke test # and one a three-minute test run, with nothing to tell them apart. # @@ -94,8 +94,8 @@ jobs: python -m pip install -U pip setuptools wheel python -m pip install -e . - # One annotation saying why, so a failed leg -- 3.7's, today, where the - # editable install succeeds and 41 modules do not compile -- is legible + # One annotation saying why, so a failed leg -- such as 3.7's, where the + # editable install succeeds and the sources do not compile -- is legible # from the run summary without opening the log. ANNOTATION_LEVEL makes it # a warning on a KTLO leg (#1672). Each title escapes `,` as `%2C`: the # runner splits a command's properties on `,`, so the KTLO leg's title diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index dc61157ba1..f99b1abe7a 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -163,7 +163,7 @@ jobs: # this job rather than added to it -- see either job's own comment for # why. # - # #1633: a KTLO leg that cannot install -- 3.7 today -- fails fast and + # #1633: a KTLO leg that cannot install -- such as 3.7 -- fails fast and # once: one annotation names pip's last ERROR line (its title's `,` as # `%2C`, as the runner splits properties on `,`), and every later step # needs `steps.install.outcome == 'success'`, so it is skipped rather @@ -422,7 +422,7 @@ jobs: # file as `os`: 3.8 and 3.9 need ubuntu-24.04, as setup-python has no Ubuntu # 26.04 build of either and `ubuntu-latest` moves to 26.04 from 2026-10-19. # 3.7 needs ubuntu-22.04, the newest image setup-python builds it for - # (3.7.17). It cannot install today -- the `test` extra's `pytest>=8` needs + # (3.7.17). It cannot install -- the `test` extra's `pytest>=8` needs # 3.8, and the sources use syntax 3.7 cannot parse -- so the leg stops at # its install step, with one annotation naming pip's reason (see that step). # ubuntu-22.04 is deprecated: brownouts from 2027-03-23, retirement on @@ -561,7 +561,7 @@ jobs: # # A failure to compute the selection intentionally does not fall back to # a bare `pytest -q` -- that would silently re-introduce the whole-suite - # duplication this change exists to remove, with nothing in the log to + # duplication this selection exists to remove, with nothing in the log to # say so. `--dist load` is pinned explicitly; see the `test` job above # for why. # @@ -701,8 +701,8 @@ jobs: # #1633 splits the interpreters into two tiers, read from # .github/python-versions.toml by `python-versions` (#1660). The supported # cells share one step, and any one of them failing fails the job. The KTLO - # cells (3.10 today) are end-of-life, still tested on every run, never - # blocking. They are a step of their own with step-level `continue-on-error: + # cells (3.10 alone; see below) are end-of-life, still tested on every run, + # never blocking. They are a step of their own with step-level `continue-on-error: # true` -- the treatment #845 gave the then-prerelease 3.15 cell -- so a KTLO # failure is reported as warnings and a job-summary row (#1672) while the # job, and `required-checks`, which reads its result, stay green. A version @@ -1443,7 +1443,7 @@ jobs: # # GitHub issue #1029: the protocols cell measured 30.4 minutes on a CI # runner against this job's 45-minute cap (#1029 measured the next slowest - # leg at 6.2 minutes on CI), so it is now two cells: `protocols/internet` and the rest of + # leg at 6.2 minutes on CI), so it is two cells: `protocols/internet` and the rest of # tests/protocols (its other five subdirectories and the modules sitting # directly in it), the latter expressed as `protocols` with # `--exclude protocols/internet`. run_unittest_leg.py takes a sub-path and @@ -1471,7 +1471,7 @@ jobs: # # What sharding costs: modules in different cells no longer share a process, # so pollution from one to the other is invisible again. Counts below were - # derived with leg_modules() on main at 30714cd51 plus this change: 51 + # derived with leg_modules() on main at 30714cd51 plus #1029's split: 51 # modules in tests/protocols (12 under internet, 39 in the rest, of which 10 # sit directly in tests/protocols). "Pairs" are unordered, C(n,2), since a # single serial run realises each pair in one order. Before: 1275 pairs @@ -1486,7 +1486,7 @@ jobs: # surfaced an instance of the very defect this job exists to catch (issue # #985): a stale VendorRuntimeWarning generation made assertWarnsRegex fail # in tests/vendor/test_vendor_snapshot_restore_unit.py. That was fixed by - # re-resolving the class fresh per test, so the directory is now a leg (297 + # re-resolving the class fresh per test, so the directory is a leg (297 # tests in the leg, 118 of them tests/vendor's own, measured on this venv). # # Two directories are deliberately absent from ``leg``, for reasons that @@ -1536,7 +1536,7 @@ jobs: # rather than inventing a new number (`engine-tests` is 30, but runs # under xdist, which this job deliberately does not). 45 did stop fitting # comfortably (#1029: protocols at 30.4 minutes on CI), so `protocols` is - # now split into sub-legs, as foreseen -- see the matrix comment above. The + # split into sub-legs, as foreseen -- see the matrix comment above. The # cap is left at 45: CI measured the slowest cell, protocols/internet, at # 8.2m (run 37341197811), so even a slowdown like #1029's window leaves it # far inside the cap: that window's runs were cancelled at 45.3m or more, @@ -1792,7 +1792,7 @@ jobs: # above -- this is the full, unfiltered suite, so it is the one place # where skipping the dpkt-gated tests would be most misleading. crypto, # cli and NGAP join it for the same reason and cover the three of #738's - # remaining gates this change fixes, since an unfiltered `pytest` run (no + # remaining gates they were added for, since an unfiltered `pytest` run (no # --ignore, no tier selection) reaches every one of them regardless of # which tier they live in -- unlike the `test` and `integration` jobs # above, which each cover only the subset their own selection reaches. @@ -1876,7 +1876,7 @@ jobs: # with needs for required checks that depend on other jobs." # * `inputs.gate-only != true` mirrors the jobs this needs: below. # They are all skipped by their own `if:` whenever this workflow is - # called with `gate-only: true` (from create-release.yml), so without + # called with `gate-only: true` (as every caller does), so without # this guard every needs.*.result below would read `skipped` on that # path and this job would report a spurious failure -- on a release # call, not a PR, which is not where ruleset 23497679 applies anyway. @@ -1885,14 +1885,14 @@ jobs: # instead of leaving that to be re-discovered from the four jobs' own # logs. # - # Note for whoever adds a second caller of this reusable workflow: under + # Note for whoever adds a caller of this reusable workflow: under # `workflow_call`, GitHub prefixes a called workflow's check names with the # calling job's own name (` / Required checks passed`), which # would not match a ruleset entry naming just `Required checks passed`. - # Harmless today -- `create-release.yml`'s only call passes - # `gate-only: true`, which skips this job via the guard above before that - # renaming would ever matter -- but a future caller that does NOT set - # `gate-only: true` would produce a differently-named, unrequired check. + # Harmless for the four callers -- create-release, cron-conda, cron-vendor and + # deploy-pages each pass `gate-only: true`, which skips this job via the guard + # above before that renaming would ever matter -- but a caller that does NOT + # set `gate-only: true` would produce a differently-named, unrequired check. # # Deliberately NOT `needs: gate`: `gate` only ever runs when this workflow # is called with `gate-only: true` and is otherwise skipped by its own diff --git a/CITATION.cff b/CITATION.cff index d108fa6469..72b7f43ac0 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -11,7 +11,7 @@ # move with ``pcapkit.__version__`` in ``pcapkit/__init__.py`` and the matching entry # under ``docs/source/changelog/`` when a release goes out. # -# No ``doi`` and no ``orcid``: neither exists for this project or its author today, +# No ``doi`` and no ``orcid``: neither exists for this project or its author, # and both fields are checked formats rather than free text, so an invented value # would validate and still be wrong. They are omitted rather than guessed. cff-version: 1.2.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5dfef6eac2..734129331b 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -130,7 +130,7 @@ Two mechanical points that decide real cases: One invisible shape: the `pcapkit/const/` enums carry a `_missing_` fallback that resolves an unregistered value rather than raising as a plain `enum` would. This is deliberate and is the extensibility behaviour of the whole `pcapkit.const` package, so it is documented once on -the package's landing page rather than on each of the 121 registries that implement it. +the package's landing page rather than on each of the registries that implement it. `docs/source/conf.py` already names `_missing_` in `autodoc_default_options['exclude-members']`, so a per-class directive would argue with the project's own configuration. diff --git a/MANIFEST.in b/MANIFEST.in index c78d8d2c2d..b1aff2a5ff 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -46,9 +46,8 @@ prune test # entry from the listing, and nothing else changes. # # These two are kept anyway, as an explicit statement of intent that does not -# depend on a setuptools default staying where it is. That is a different and much -# weaker reason than the one this comment used to give, which is the point of -# rewriting it rather than deleting it. +# depend on a setuptools default staying where it is -- a much weaker reason than +# being load-bearing, and the only one. include README.md include LICENSE @@ -65,7 +64,8 @@ include LICENSE # half of the attribution at the one point nobody can see the repository. It # also lets ``RepositoryCitationTests`` in # ``tests/project/test_bump_version.py`` run against an unpacked sdist, where -# today it skips itself with "not shipped in the source distribution". +# without this line it skips itself with "not shipped in the source +# distribution". include CITATION.cff # The changelog needs two lines of its own, and neither is redundant. @@ -79,9 +79,7 @@ include CHANGELOG.md # distribution would describe one release and no history. ``prune docs`` above # drops the rest, which is where the history lives, so re-add just that one # directory afterwards -- the same shape as ``include CHANGELOG.md`` above, and for -# the same reason. (This used to name ``include README.md`` instead, on the -# mistaken belief that that line was load-bearing; see the note above it.) Each of -# those files is a self-contained reStructuredText entry +# the same reason. Each of those files is a self-contained reStructuredText entry # that renders standalone; the Sphinx index beside them is deliberately *not* # shipped, because it is a ``toctree`` that only Sphinx can read. recursive-include docs/source/changelog *.rst diff --git a/Makefile b/Makefile index 33608b608b..1ed79d3bc0 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ export PIPENV_CACHE_DIR ?= $(CURDIR)/.pipenv-cache export PIP_CACHE_DIR ?= $(CURDIR)/.pip-cache export all_proxy= -# No recipe below currently needs a bash-only feature -- the last one, brace +# No recipe below needs a bash-only feature -- the last one, brace # expansion in the `isort:` target, went with #765's `find`-based fix (see # that target and tests/project/test_isort_clean.py for why: a later revision # of that same fix leaned on brace expansion plus a bash new enough for @@ -13,7 +13,7 @@ export all_proxy= # reliance can go wrong beyond just needing bash at all). The pin stays # anyway: one known shell to write recipes against, rather than whatever # `/bin/sh` happens to be on a given machine, is worth keeping even with -# nothing bash-specific relying on it today, and it costs nothing to take +# nothing bash-specific relying on it, and it costs nothing to take # from PATH rather than a fixed prefix, as Homebrew, Linuxbrew and system # installs all put it somewhere different. SHELL := $(shell command -v bash 2>/dev/null || echo /bin/bash) @@ -95,13 +95,14 @@ coverage: samples pipenv run coverage run -m pytest -q pipenv run coverage report -# The engine speed table in docs/source/index.rst -- every supported Python -# version, one image each, measured in containers so the host does not affect the +# The engine speed table in docs/source/index.rst -- every Python version +# examples/benchmark/python-images.txt marks `default`, one image each, +# measured in containers so the host does not affect the # result. Needs docker, and nothing else: deliberately not run through pipenv, # since the whole point is that the measuring environments are the pinned ones # inside the images rather than whatever is installed here. # -# Expect around two hours at the defaults: five interpreters, seven environments, +# Expect around two hours at the defaults: five interpreters, seven engines, # and almost all of the measuring time is pyshark, which spawns a tshark process # per extraction. Cut it with --engines, --pythons, or --quick. bench: @@ -146,8 +147,9 @@ RUN ?= pipenv run # Flag sets are variables rather than literals for the same reason -- `vermin` # needs two recipes (see below) and would otherwise carry two copies of its # flags. Note the `pcapkit` before the flags as well as after: that is how this -# recipe has always read, and vermin de-duplicates the paths (it reports 496 -# files analyzed either way), so it is preserved verbatim rather than tidied. +# recipe has always read, and vermin de-duplicates the paths (it reports the +# same number of files analyzed either way), so it is preserved verbatim rather +# than tidied. VERMIN_FLAGS = --backport argparse --backport enum --backport importlib --backport ipaddress --backport typing --backport typing_extensions --no-parse-comments --eval-annotations -vv # `pylint.extensions.emptystring` no longer exists (#767): the check it provided, # `compare-to-empty-string`, was folded into the core `refactoring` checker at @@ -172,9 +174,10 @@ vermin: $(RUN) vermin pcapkit $(VERMIN_FLAGS) pcapkit > temp/vermin.txt command -v code >/dev/null && code temp/vermin.txt || cat temp/vermin.txt -# What CI runs. The `vermin` target above redirects into temp/ and then hands the -# file to an editor, so its exit status is the editor's (or `cat`'s) and a run -# that found violations still succeeds -- fine at a desk, useless as a check. +# What CI runs. The `vermin` target above redirects the report into temp/ and +# only then hands the file to an editor, so a run that found violations stops +# Make at the redirect, with the report left unread in the file and the line +# that would have opened it never reached -- useless as a log. # This one writes to stdout and lets vermin's failure propagate, which is what # `targets = 3.6` in vermin.ini is for: vermin exits non-zero when the target is # not met, and the code's real floor is 3.11. diff --git a/README.md b/README.md index fbbd44687f..7692e94e3a 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,8 @@ Reassembly, TCP flow tracing and the engine are keyword arguments to the same ca >>> scapy = pcapkit.extract('in.pcap', nofile=True, engine='scapy') >>> reasm = pcapkit.extract('in.pcap', nofile=True, reassembly=True, ipv6=True) >>> flows = pcapkit.extract('in.pcap', nofile=True, trace=True, tcp=True) ->>> len(flows.trace) -3 +>>> len(flows.trace.tcp) +2 ``` More examples, including the command line interface, are in diff --git a/examples/benchmark/README.rst b/examples/benchmark/README.rst index b94e343ed4..7899f3b0c3 100644 --- a/examples/benchmark/README.rst +++ b/examples/benchmark/README.rst @@ -66,7 +66,7 @@ neither can be worked around from inside ``pcapkit``: that will not compile against the 3.12+ C API; ``pypcapfile`` 0.12.0 imports ``imp``, removed in 3.12; ``pyshark`` 0.6 calls ``asyncio.get_event_loop_policy().get_event_loop()``, which raises from 3.14. - **That is now a fact the table reports rather than a constraint on the run**: each + **That is a fact the table reports rather than a constraint on the run**: each engine's ceiling shows up as ``--`` cells with the reason, in the columns past it. The ``default`` engine -- ``pcapkit``'s own parser, also spelled ``pcapkit`` -- is @@ -492,7 +492,7 @@ baseline, that machine drift cancels, that repeats are paired one to one rather averaged first, that the environments stitch together correctly, that an unmeasured engine keeps its reason, that the overlap marking neither over- nor under-claims, and that the emitted reStructuredText parses under **plain docutils** with no warnings and -uses no Sphinx-only roles. That last one is now a conservative choice rather than a +uses no Sphinx-only roles. That last one is a conservative choice rather than a hard requirement: the snippets land in ``docs/source/index.rst``, which Sphinx renders, so a ``:mod:`` role would survive there. Keeping them docutils-clean is what lets a table be pasted into any reStructuredText a plain docutils reader will see -- diff --git a/examples/benchmark/report.py b/examples/benchmark/report.py index a098460c87..cc49414e8c 100644 --- a/examples/benchmark/report.py +++ b/examples/benchmark/report.py @@ -1003,7 +1003,7 @@ def render_versions_rst(rows: 'Sequence[Row]', documents: 'Sequence[dict[str, An lines.append('') if emulated: - # `run.sh` composes this sentence itself today, which makes it safe today. It + # `run.sh` composes this sentence itself, which on its own would make it safe. It # still arrives through `--emulated` on the command line, so it is outside text by # every test that matters, and the one channel of it left unescaped would be the # one nobody thought about. diff --git a/examples/generators/dispatch.py b/examples/generators/dispatch.py index e955c0a47a..903578867c 100644 --- a/examples/generators/dispatch.py +++ b/examples/generators/dispatch.py @@ -3,7 +3,7 @@ :file:`pcapkit/foundation/registry/protocols.py` documents the registries that decide *which* :class:`~pcapkit.protocols.protocol.Protocol` subclass parses the -next layer -- 38 entries across seven tables. Nothing enumerated them: several +next layer -- 47 entries across eight tables. Nothing enumerated them: several test modules reference ``__proto__``, and :file:`tests/protocols/test_registry_runtime.py` checks that a table entry *resolves* to the right class object, but that is a weaker property than the one @@ -51,15 +51,12 @@ so a mismatch between the two is reported as what it is rather than folded into "dispatch failed". -Four of the 38 entries are known to degrade to :class:`Raw` today, for reasons +Two of the 47 entries are known to degrade to :class:`Raw`, for reasons recorded in :data:`KNOWN_DEGRADED` rather than fixed here -- this module reports -defects, it does not carry workarounds for them. Two are -:class:`~pcapkit.protocols.internet.ipx.IPX` (GitHub issue #492: ``Socket(0)`` -is not a valid member, and :class:`IPX() ` -cannot even be constructed with no arguments, let alone parsed); the other two -are both :class:`~pcapkit.protocols.transport.sctp.SCTP` payload protocol -identifiers reaching :class:`~pcapkit.protocols.application.ngap.NGAP`. Neither -is a defect: building a well-formed PER-encoded ``NGAP-PDU`` is out of scope for +defects, it does not carry workarounds for them. Both are +:class:`~pcapkit.protocols.transport.sctp.SCTP` payload protocol identifiers +reaching :class:`~pcapkit.protocols.application.ngap.NGAP`, and neither is a +defect: building a well-formed PER-encoded ``NGAP-PDU`` is out of scope for a minimal dispatch probe, so both use the same placeholder payload :meth:`~tests.protocols.transport.test_sctp_unit.SCTPUnitTests.test_ppid_dispatch_hook` already does, and for the same documented reason -- one PPID is DTLS-wrapped, @@ -666,7 +663,7 @@ def _internet_enum() -> 'Any': 'internet/SCTP': ('pcapkit.protocols.transport.sctp', 'SCTP'), 'internet/OSPFIGP': ('pcapkit.protocols.application.ospf', 'OSPF'), # #904: Shim6 (140) previously had no entry at all, and the default - # factory made it resolve to Raw. It is now registered directly at + # factory made it resolve to Raw. It is registered directly at # IPv6_Ext, which parses the RFC 6564 ยง4 generic layout it has # never had a dedicated dissector for -- a deliberate addition, not a # regression. @@ -898,7 +895,7 @@ def outcomes(families: 'Optional[tuple[Family, ...]]' = None) -> 'list[Outcome]' return [probe(case) for case in cases(families)] -#: Codes known to degrade to :class:`~pcapkit.protocols.misc.raw.Raw` today, +#: Codes known to degrade to :class:`~pcapkit.protocols.misc.raw.Raw`, #: with the defect that causes it. Recorded, not fixed -- GitHub issue #496 is #: a test-harness change and none of these classes is this module's to touch. #: diff --git a/examples/generators/endian.py b/examples/generators/endian.py index 1f91be66cb..d8e5812c2e 100644 --- a/examples/generators/endian.py +++ b/examples/generators/endian.py @@ -12,7 +12,7 @@ That is how GitHub issue #605 survived: ``Frame.unpack`` seeded the byte order under the key ``bytesorder`` where ``byteorder_callback`` -- then in -:file:`pcapkit/protocols/schema/misc/pcap/frame.py`, now in +:file:`pcapkit/protocols/schema/misc/pcap/frame.py`, since moved to :file:`pcapkit/corekit/fields/byteorder.py` -- reads ``byteorder``, so the lookup always missed and always fell back to :data:`sys.byteorder`. Reading a big-endian capture then byte-swapped ``ts_sec``, ``ts_usec``, ``incl_len`` and diff --git a/examples/generators/legacy.py b/examples/generators/legacy.py index 4913736ad3..84c08aeb8a 100644 --- a/examples/generators/legacy.py +++ b/examples/generators/legacy.py @@ -86,7 +86,7 @@ indexed from the start of the direction's data, so with any real initial sequence number every slice started far beyond the end of the buffer, every fragment came out empty, and ``if data:`` discarded the datagram without a word. -That is fixed -- ``submit`` now converts each hole's absolute bounds into +That is fixed -- ``submit`` converts each hole's absolute bounds into offsets into the buffer it is reading, and decides completeness from whether any hole survives that conversion -- and GitHub issue #349 records the whole of it. Regenerating these two fixtures to carry a permanently lost segment as well was diff --git a/examples/generators/options.py b/examples/generators/options.py index 66955c9b3e..c98ba1c682 100644 --- a/examples/generators/options.py +++ b/examples/generators/options.py @@ -529,8 +529,8 @@ def _ipv4_overrides() -> 'dict[Any, dict[str, Any]]': # more than one does, not because one is unrepresentable: it used to be, # a single ``GENSER`` (value 0) making ``_make_opt_sec`` size a # zero-octet bitmap and then index into it for a bare ``IndexError``, and - # #537 fixed that arithmetic. So this is a coverage choice now and no - # longer routes around anything. + # #537 fixed that arithmetic. So this is a coverage choice and routes + # around nothing. OptionNumber.SEC: {'authorities': [ProtectionAuthority.GENSER, ProtectionAuthority.NSA]}, # ``counts=10``, the default, needs 43 option octets, which overflows @@ -1002,8 +1002,7 @@ def _mh_extension_build(code: 'Any', kwargs: 'dict[str, Any]') -> 'Any': #: How many copies of the parameter under test go in one packet. #: -#: One, since #689. This note used to be a history of why the constant was two; -#: it is now the history of why it no longer is. +#: One, since #689. What follows is why it was two, and why it is not. #: #: It was two because of the defect #651 fixed. ``HIP.make`` computes the #: header's ``len`` field as ``total_length // 8 + 4``, which is lossless only @@ -1029,7 +1028,7 @@ def _mh_extension_build(code: 'Any', kwargs: 'dict[str, Any]') -> 'Any': #: which is what moved the one-copy figure from 44 to 46, level with two copies. #: #: Two remained after that, neither helped by a second copy since each failed at -#: two copies as well as at one. Both are closed now: +#: two copies as well as at one. Both are closed: #: #: * ``HOST_ID`` declared four octets for the single 16-bit DI-Type/DI-Length #: word of :rfc:`7401` Section 5.2.9, so it packed two surplus octets -- 18 at @@ -1037,7 +1036,7 @@ def _mh_extension_build(code: 'Any', kwargs: 'dict[str, Any]') -> 'Any': #: conformant record two octets late, taking the curve as ``Algorithm``. #1118 #: made the field two octets. #: * ``HIP_TRANSFORM`` is HIPv1-only -- ``_read_param_hip_transform`` raises for -#: any other version -- and is now built at version 1 through +#: any other version -- and is built at version 1 through #: :data:`HIP_VERSION`, so it round-trips and is no longer recorded. #: #: A third gap here, ``R1_Counter`` (128), was closed separately by #690 and was @@ -1046,7 +1045,7 @@ def _mh_extension_build(code: 'Any', kwargs: 'dict[str, Any]') -> 'Any': #: ``code=`` of the class statement and ``R1CounterParameter`` declared only #: 129 -- even though ``__parameter__``'s ``_read_param_*``/``_make_param_*`` #: entries already existed for both codes, as two hand-written dict entries, -#: not because of any name-normalisation rule. No longer recorded as of #690, +#: not because of any name-normalisation rule. Not recorded since #690, #: which registered ``R1CounterParameter`` for 128 too. #: #: So the pair was routing around nothing by the time #672, #679 and #690 had @@ -1074,8 +1073,7 @@ def _mh_extension_build(code: 'Any', kwargs: 'dict[str, Any]') -> 'Any': #: The single-parameter case is asserted directly, and positively, by #: ``test_a_hip_packet_carrying_one_parameter_round_trips`` in #: :mod:`tests.protocols.test_option_roundtrip_unit`, which is also where the -#: two-copy shape is still exercised now that this constant no longer produces -#: it. +#: two-copy shape is exercised, since this constant does not produce it. HIP_COPIES = 1 @@ -1109,7 +1107,7 @@ def _hip_overrides() -> 'dict[Any, dict[str, Any]]': # ``aabbccdd`` turned that silence into ``type 0 padding not zeroed: # aabbccdd``. With a non-zero counter the fixture can no longer conceal # a mis-stride here, whether or not the width is right -- which is the - # reason to keep this override now that #672 has corrected the width. + # reason to keep this override, #672 having corrected the width. Parameter.R1_COUNTER: {'counter': 0xaabbccdd}, Parameter.R1_Counter: {'counter': 0xaabbccdd}, } diff --git a/examples/generators/pcapng.py b/examples/generators/pcapng.py index 288c7ecb4b..88d962a502 100644 --- a/examples/generators/pcapng.py +++ b/examples/generators/pcapng.py @@ -50,11 +50,11 @@ Block types deliberately left out --------------------------------- -Two block types that used to raise on spec-conformant input now parse, so they -are absent from these fixtures only because the fixtures have not been extended -to carry them -- not because pcapkit cannot read them: +Two block types once raised on spec-conformant input and parse since their +fixes, so they are absent from these fixtures only because the fixtures have not +been extended to carry them -- not because pcapkit cannot read them: -* **Custom Block** (``0x00000BAD``/``0x40000BAD``) -- **parses now.** Its +* **Custom Block** (``0x00000BAD``/``0x40000BAD``) -- **parses.** Its padding was computed as ``(4 - pkt['data'] % 4) % 4``, i.e. on the :class:`bytes` object rather than on its length, so any custom block raised ``TypeError: not all arguments converted during bytes formatting``. The @@ -62,26 +62,26 @@ its custom data, so ``data`` already spans the custom data, its padding *and* the block's options, which is as much as a reader that does not own the private enterprise number can tell apart (GitHub issue #341). -* **Packet Block** (obsolete, ``0x00000002``) -- **parses now.** Its +* **Packet Block** (obsolete, ``0x00000002``) -- **parses.** Its ``interface_id`` and ``drop_count`` were declared ``UInt32Field`` although Appendix A of draft-ietf-opsawg-pcapng (Figure 19) packs both into one 32-bit word -- which is the layout the ``options`` length's 32-octet overhead already - assumed. Both are ``UInt16Field`` now, and the reader resolves the link type + assumed. Both are ``UInt16Field``, and the reader resolves the link type from the block's own ``interface_id`` rather than through the ``linktype`` property, which needs a ``self._info`` that does not exist yet while the block is still being read and so failed with ``AttributeError: 'PCAPNG' object has no attribute '_info'`` (GitHub issue #345). The block MUST NOT appear in new files, so a fixture carrying one would be documenting the past. -Two further constructs used to be avoided rather than covered, and both are -**fixed** now, so the fixtures carry them as regression cover: +Two further constructs were once avoided rather than covered; both are +**fixed**, so the fixtures carry them as regression cover: * **``if_IPv6addr``** -- ``IPv6InterfaceField.post_process`` in ``pcapkit/corekit/fields/ipaddress.py`` read the trailing prefix-length octet as ``int(value[16:])``, i.e. it parsed a raw byte as an ASCII decimal string, so a ``/64`` prefix raised ``ValueError`` and only the ten lengths 48-57 parsed at all -- each of them to the wrong value (GitHub issue #346). Every - interface built by :func:`_interface_profile` now carries the option, using + interface built by :func:`_interface_profile` carries the option, using the ``/64`` address from section 4.2 of the format specification. * **Simple Packet Block** in a multi-interface section -- the engine raised ``FormatError: PCAP-NG: [SPB] invalid section with 2 interfaces`` for any @@ -89,7 +89,7 @@ format specification (section 4.4 of draft-ietf-opsawg-pcapng) permits: it says only that such a block then refers to the interface described by the first Interface Description Block, not that it is invalid. The check is for - *zero* interfaces now (GitHub issue #347), so ``test.pcapng`` carries its + *zero* interfaces (GitHub issue #347), so ``test.pcapng`` carries its simple packet block in the two-interface first section, which is the case that used to be rejected. @@ -97,7 +97,7 @@ raised, and a fixture that covers the code path is what will catch a future crash there. All three were option-area sizing errors in ``pcapkit/protocols/schema/misc/pcapng.py``, all three were confirmed by -bisecting one block type at a time, and all three are now **fixed** -- so the +bisecting one block type at a time, and all three are **fixed** -- so the fixtures that reached them are regression cover rather than known-bad input: * **Interface Statistics Block** -- ``options`` was sized ``length - 20`` though @@ -106,13 +106,13 @@ save it, because the field still consumed its declared width. Symptom, on every capture containing one including the downloaded ``many_interfaces.pcapng``: ``packet length < 0: -8`` and ``[Block 5] block - length mismatch: N != 0``. Now ``length - 24`` (GitHub issue #342). + length mismatch: N != 0``. It is ``length - 24`` (GitHub issue #342). * **Name Resolution Block options** -- ``options`` was sized ``__option_padding__ - 4``, which over-ran whenever ``ns_*`` options were present. Symptom: ``[Block 4] block length mismatch: 60 != 314``. Not a wrong constant: ``Schema.unpack`` advances the file by each field's *declared* length, so the octets the option field was trying to size had already been - consumed by ``records``. An ``OptionField`` now hands the remainder it did not + consumed by ``records``. An ``OptionField`` hands the remainder it did not parse back to the file -- the rewind ``ForwardMatchField`` already did -- which is what makes ``__option_padding__`` mean the same thing to the field that reads it as to the field that reported it, and the NRB's ``options`` is sized @@ -123,10 +123,10 @@ copied from the IPv4 record where the address is four octets wide; an IPv6 address is sixteen, so the name field over-read by twelve and swallowed the record terminator and the block's options. Symptom: ``packet length < - 0: -29273``, and the block's ``ns_*`` options vanished. Now ``length - 16`` + 0: -29273``, and the block's ``ns_*`` options vanished. It is ``length - 16`` (GitHub issue #343). -``many_interfaces.pcapng`` and ``profile.pcapng`` now extract with neither a +``many_interfaces.pcapng`` and ``profile.pcapng`` extract with neither a block length mismatch nor a negative packet length; before these fixes each reported one of each. @@ -427,7 +427,7 @@ def _journal_entry(message: 'str', realtime_us: 'int') -> 'bytes': a binary field name and raised a bare ``struct.error`` (fixed in `#699 `__, which made a NUL-only line end the entry). Measured on ``ef859f776``: a misaligned - entry now parses with no ``SchemaWarning``. The loop stays only because + entry parses with no ``SchemaWarning``. The loop stays only because removing it regenerates ``test.pcapng`` -- an untracked fixture change, not a prose one; see `#791 `__. @@ -484,8 +484,8 @@ def _interface_profile(writer: '_Blocks', name: 'str', description: 'str', resolution: 'int' = 6) -> 'list[tuple[int, bytes]]': """The full set of ``if_*`` options pcapkit can parse for one interface. - ``if_IPv6addr`` used to be the one omission, because pcapkit raised on it - rather than because it did not belong here; it is included now that the + ``if_IPv6addr`` was once the one omission, because pcapkit raised on it + rather than because it did not belong here; it is included since the prefix-length octet is read correctly -- see the module docstring. Args: diff --git a/examples/legacy_smoke/_engine_support.py b/examples/legacy_smoke/_engine_support.py index 17edec6807..fe3b7399de 100644 --- a/examples/legacy_smoke/_engine_support.py +++ b/examples/legacy_smoke/_engine_support.py @@ -86,7 +86,7 @@ def unavailable(exc: 'Exception') -> 'str | None': f'which Python {version} no longer provides (pyshark bug, not pcapkit)') # The prerelease pcap-ct/libpcap wheels are platform-neutral, but their - # bundled configuration currently asks macOS to load Linux's libc.so.6. + # bundled configuration asks macOS to load Linux's libc.so.6. # That makes the optional engine unavailable on this host, not a failure in # the extraction being benchmarked. if isinstance(exc, OSError) and 'libc.so.6' in str(exc): diff --git a/pyproject.toml b/pyproject.toml index aeebb82214..e60b88a32f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ license = { text="BSD 3-Clause License" } # 3.7.16 SyntaxError from source, as expected -- this is what the # ``bpc-*`` conversion exists for # -# **The conversion path has an upstream bug today.** ``bpc-poseur`` 0.4.3.post1 +# **The conversion path has an upstream bug.** ``bpc-poseur`` 0.4.3.post1 # crashes on any positional-only parameter that is on a *method* rather than a # plain function -- ``AttributeError: 'Name' object has no attribute 'name'`` at # ``poseur.py:744`` in ``_process_classdef``, which does ``cls_ctx=name.name`` @@ -225,7 +225,7 @@ PyPCAPFile = [ "pypcapfile; python_version < '3.12'" ] # pcapkit.vendor.ngap's two crawlers (#880) can run, and ``requests``/ # ``beautifulsoup4`` for the seven crawlers that import them at module scope # (c.f. the ``test`` extra's own comment below for which, and #507). #910 -# narrowed ``all`` to core addons only, so this extra now names all three +# narrowed ``all`` to core addons only, so this extra names all three # itself rather than leaning on ``all`` to also carry them -- ``pycrate`` # happens to be a core addon in its own right post-#910 (see the ``all`` # extra's comment below), but ``requests``/``beautifulsoup4`` are not, having @@ -357,7 +357,7 @@ test = [ # setuptools specific [tool.setuptools] -zip-safe = false # We use __file__ in pcapkit/__init__.py, therefore Scapy isn't zip safe +zip-safe = false # We use __file__ in pcapkit/__init__.py, therefore pcapkit isn't zip safe [tool.setuptools.packages.find] include = [ diff --git a/util/bump_version.py b/util/bump_version.py index bc587c679d..b343f186e9 100644 --- a/util/bump_version.py +++ b/util/bump_version.py @@ -70,8 +70,7 @@ begin with ``version``. Each field keeps whatever quoting it already had. The file as committed spells one -of each -- ``version: 1.5.0b4`` bare and ``date-released: '2026-09-20'`` quoted, -the latter deliberately, so that a validator reads a string rather than casting it +of each -- ``version`` bare and ``date-released`` quoted, the latter deliberately, so that a validator reads a string rather than casting it to a YAML date object. Preserving the style rather than normalising it keeps the automated commit's diff down to the value that actually moved, and leaves the choice of style where it belongs. An unquoted version is safe here because every @@ -114,12 +113,11 @@ What is deliberately not changed -------------------------------- -:file:`conda/build` is still written relative to the working directory, as it -always was, rather than relative to the repository root the way :data:`CITATION` -is. The two disagree, and the root-relative form is the better of them -- it is -what :file:`util/changelog_md.py` documents and does -- but moving -:file:`conda/build` is a change in behaviour for every existing caller and -belongs in its own review rather than riding along with this one. +:file:`conda/build` is written relative to the working directory, rather than +relative to the repository root the way :data:`CITATION` is. The two disagree, +and the root-relative form is the better of them -- it is what +:file:`util/changelog_md.py` documents and does -- but moving :file:`conda/build` +is a change in behaviour for every existing caller, so it is left as it is. """