Skip to content

ci(python): declare the version tiers and the latest version in one file (#1660) - #1677

Merged
JarryShaw merged 4 commits into
mainfrom
ci/1660-python-version-tiers-file
Oct 14, 2026
Merged

JarryShaw merged 4 commits into
mainfrom
ci/1660-python-version-tiers-file

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Please follow the guide below

  • You will be asked some questions, please read them carefully and answer honestly

  • Put an x into all the boxes [ ] relevant to your pull request (like that [x])

  • Use Preview tab to see how your pull request will actually look like

  • Searched for similar pull requests

  • Followed the coding style (make pylint, make mypy, make isort)

  • make test passes, and a test case covers the change -- tests cover it; only the touched tests/project modules were run locally, the full suite is CI's

  • Added a changelog entry under docs/source/changelog/ and regenerated CHANGELOG.md, if the change is user-visible -- N/A — centralised in docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620) #657

What is the purpose of your pull request?

Tick the commit type your subject line carries.

  • fix — corrects a defect
  • feat — adds a feature
  • perf — changes performance, not behaviour
  • refactor — changes neither behaviour nor performance
  • test — tests only
  • docs — documentation only
  • ci — workflows or build tooling
  • release — bumps the version or rolls up a distribution
  • chore — anything else

Description of your pull request and other information

Part of #1660 (the scheduled monitor follows); folds in #1661.

  • One file. .github/python-versions.toml gives each CPython and PyPy version one line: its tier and, where ubuntu-latest cannot provide it, its runner. It names latest (3.15) and lists the three pins kept off the file, each with its reason in a comment: both conda != '3.15' checks and pypcap-parity's APT_CACHE_SAVE. TOML, for comments: stdlib tomllib, and the repo's existing config format.
  • Workflows read it. util/python_versions.py runs on the runner's own python3: tomllib needs 3.11, and every image that runs it has 3.12 or newer (runner-images READMEs at 2c421200c). Every matrix, engine-tests' interpreter lists and the release completeness counts now come from its outputs, and so does every single-interpreter job's version.
  • Names. No job or required-check name changes, except Gate (full suite, Python 3.14), which becomes 3.15. No required check reads it.
  • Moved to 3.15 (ci: the gates and the full suite run on Python 3.14, not the newest supported version #1661): gate, the coverage and sdist legs, unittest-ordering, changelog, project-tests, version_check, docs, the vendor and conda crons, CI Timing, and lint.
  • Lint on 3.15. At 0bc157660, on setup-python's own 3.14.8 and 3.15.0 builds, pylint 4.1.3 (astroid 4.3.4, both latest on PyPI) reports the same messages, 515 C / 71 E / 4970 W, with E1101 at 12 on both. Only R0401 cyclic-import differs, and it differs between runs on one interpreter too. bandit, mypy and vermin print identical reports. The crash and the 35 E1101 recorded earlier came from faking a missing _decimal with sys.modules['_decimal'] = None. That reproduces them on 3.14.8 as well. setup-python's 3.15.0 has no _decimal, and it does not crash.
  • Tests. tests/project/test_python_versions_1660.py: the file reproduces 1024109fc's matrices except the coverage leg; no workflow pins a version the file does not list; the classifiers, README, SECURITY.md, docs and bug template agree with it. The fix(conda): the Python 3.10 conda build fails since soupsieve 3 was pinned #1633 and ci: a failed KTLO leg turns main and pull requests red; report it as a warning and exit 0 #1672 tier tests now read the file. Tests that read it skip on the KTLO legs (3.8-3.10), which have no tomllib. Each new test fails on origin/main and passes here. actionlint 1.7.12 reports the same 28 findings as on main.
  • Ruleset. A tier change is now a one-line edit plus a ruleset 23497679 update, as the workflows page says. One exception: a new pypi KTLO leg still needs its own ktlo_* output, and the tests name it.

@JarryShaw JarryShaw added ci Pull requests that change CI or workflow configuration (ci: subject prefix) review: good-to-go Cross-review at the current head says ready; CI state is separate labels Oct 13, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at a52c2b4bd. A Sonnet cross-review checked this Part A of #1660, from an Opus worker. I re-ran the new tier test, the 1633 tier test, the release-gate test and the queue test: 20, 26, 120 and 37 passed.

What changed. .github/python-versions.json holds one line per version (tier and runner), plus latest and the exemptions with their reasons. util/python_versions.py, standard library only, turns it into matrices, and every matrix and single-interpreter job reads it through fromJSON.

What the reviewer checked. It ran its own expander over all seven matrix jobs: the legs, names, runs-on and order are identical to main. The only change is the move to latest, with coverage now on the 3.15 leg. The required-check names match ruleset 23497679 exactly. The job adds no write permission, and the KTLO mechanics are unchanged. All eight mutations it tried were caught by the tests.

Exempt, each with its reason: lint stays on 3.14, because pylint/astroid crashes on dpkt.py under setup-python's 3.15, which has no C _decimal. Also exempt: conda's != '3.15', engine-tests' KTLO 3.10, and pypcap-parity's 3.11 cache key.

Unverified until CI runs: GitHub's include expansion order, and the sparse checkout under workflow_call. Each workflow also gains a short serial python-versions job in front of its legs.

Conflicts expected with #1672, which is in progress and touches the same workflows. Part B, the monitor, follows as its own PR.

@JarryShaw JarryShaw moved this to In review in PyPCAPKit Oct 13, 2026
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Coverage: 89.46% (unit tier, Python 3.14, 775d6c736, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 3 20 3 95.16%
pcapkit/const 18858 954 2366 868 91.06%
pcapkit/corekit 2366 67 760 35 96.48%
pcapkit/dumpkit 275 3 100 3 98.40%
pcapkit/foundation 3033 152 1076 35 94.23%
pcapkit/interface 138 7 52 5 93.68%
pcapkit/protocols 17166 226 4580 190 98.07%
pcapkit/toolkit 1416 279 584 5 77.80%
pcapkit/utilities 494 4 146 4 98.75%
pcapkit/vendor 4417 2342 1010 157 43.38%

Per-file detail: the coverage-html artifact of this run.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Can we use JSON with comments format for the definition file? Or maybe let's use YAML if it’s a better choice. If we don't wanna introduce new dependencies, config format (ini) would probably be an alternative.

Also, I'd prefer we keep all single-instance workflows on the latest Python versions, like linting, release, CI timing etc., unless it's incompatible like the PyPCAP parity tests.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Recorded, two items.

Format: TOML, unless you'd rather another. It carries comments, Python reads it with the standard library (tomllib, 3.11+), and the repo already keeps its config in TOML (pyproject.toml). By contrast:

  • JSON with comments has no stdlib parser.
  • YAML would need PyYAML.
  • INI cannot nest the per-version tables without flattening them.
    A worker is converting .github/python-versions.json to .github/python-versions.toml, moving the // note and the exemption reasons into real comments.

Single-interpreter jobs on the latest Python. Release, CI timing, docs, coverage, the crons and the gate already move to 3.15 in this PR, and pypcap-parity keeps 3.11, as you said. Lint is the one held back, because it is measured incompatible: on 3.15, pylint 4.1.3 / astroid 4.3.4 crashes on pcapkit/toolkit/dpkt.py (F0002), and its E1101 count moves from 12 to 35. bandit, mypy and vermin agree on both versions. Options:

  1. Lint on 3.15, with only pylint held on 3.14 until astroid handles 3.15 (recommended).
  2. Lint entirely on 3.15, accepting pylint's crash and the changed counts.
  3. Keep lint on 3.14, as this PR does now.

@JarryShaw
JarryShaw force-pushed the ci/1660-python-version-tiers-file branch from a52c2b4 to c605ea3 Compare October 13, 2026 21:37
JarryShaw added a commit that referenced this pull request Oct 13, 2026
…1660)

- Review on #1677 asked for a tier file that holds comments.
  .github/python-versions.json becomes .github/python-versions.toml with
  the same data, one inline table per version, so a tier move is still a
  one-line diff. The "//" note and each exemption's reason are comments;
  an exemption has no `reason` key.
- util/python_versions.py and util/ci_timing.py read it with tomllib
  (3.11+). Every job that runs the script on the runner's own python3 is
  on ubuntu-latest or macos-latest, whose python3 is 3.12 or newer. No
  dependency is added.
- Tests that read the file skip without tomllib, as on the KTLO legs
  (3.8-3.10). New tests: the file is TOML and each exemption has a comment,
  each runner's python3 has tomllib, the skips hold without it, and the
  workflows page cites each version's line.
- Sparse checkouts, comments, docs and line citations follow the rename.

New tests fail on the parent and pass here; old and new script outputs are
identical; actionlint findings unchanged.
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at c605ea3a7. Round 2 passed a Sonnet check. I re-ran the 1660, 1672, queue and 1633 tests: 24, 10, 38 and 26 passed.

Round 2.

  • Format. .github/python-versions.toml replaces the JSON file. Each version is one inline-table line, so a tier move is still a one-line diff. The old // note and every exemption reason are now real comments.
  • Equivalence. The TOML loads to exactly the JSON's data, and the script prints byte-identical outputs from both.
  • tomllib. It is available everywhere the script runs, per the pinned runner-images READMEs: ubuntu-latest 24.04 has Python 3.12.3, 26.04 has 3.14.4, and macos-latest has 3.14.7. The tests/project modules that read the file skip below 3.11, on the KTLO legs, with a stated reason; nothing new is depended on.
  • Rebase. Rebased onto 68a57a9c2, keeping ci: a failed KTLO leg turns main and pull requests red; report it as a warning and exit 0 #1672's KTLO mechanics in all six conflicting files.

Still open: lint's version, the question above; this PR keeps lint on 3.14 until you choose. One date is inconsistent: the runner's own notice says ubuntu-latest moves to 26.04 from 2026-10-19, while the README says November 2026.

@JarryShaw

Copy link
Copy Markdown
Owner Author

what's wrong with asteroids? and yea, everything else lint on 3.15 then

@JarryShaw

Copy link
Copy Markdown
Owner Author

Recorded: everything except pylint runs lint on 3.15. Before settling for that, a worker is trying newer pylint/astroid releases so that pylint can move too.

What's wrong with astroid. The crash (F0002 astroid-error, AttributeError: 'NoneType' object has no attribute '__name__') happens only on setup-python's 3.15 build, which ships without the C _decimal module and falls back to _pydecimal. A 3.15 built with C _decimal lints pcapkit/toolkit/dpkt.py cleanly. The E1101 count moving from 12 to 35 points the same way: astroid infers decimal differently when it is pure Python. The worker is pinning down the exact astroid code path, and checking whether a released or development astroid fixes it.

If a version works, lint moves to 3.15 entirely. Otherwise this PR ships option 1, with only pylint held on 3.14 and the reason recorded in the tier file.

@JarryShaw

Copy link
Copy Markdown
Owner Author

GOOD TO GO carries to 627fa2527. Your update-branch merge of 7aeeaf804 is the clean automatic merge: its tree 4947e260 equals git merge-tree's. Round 3 (lint on 3.15) is still in progress, and will rebase onto main when it lands.

…ile (#1660)

- Python versions were spelt out in every matrix, so a tier change meant a
  sweep of many files. .github/python-versions.json now gives each CPython
  and PyPy version one line with its tier and, where ubuntu-latest cannot
  provide it, its runner; it names `latest` and lists the pins kept off it.
- util/python_versions.py reads it with the runner's own python3. Every
  matrix (test, integration, engine-tests, pypcap-parity, compatibility,
  pypi, conda, conda-dist) and every single-interpreter job's version comes
  from its outputs; the release completeness checks count its tags. Job and
  required-check names are unchanged.
- The single-interpreter jobs move to `latest`, 3.15 (#1661): the gate, now
  `Gate (full suite, Python 3.15)`, the coverage and sdist legs, ordering,
  docs, crons, CI Timing. lint.yml stays on 3.14, an exemption in the file.
- New tests/project/test_python_versions_1660.py: the file reproduces main's
  matrices bar the coverage leg, no workflow pins a version the file does not
  list, and the classifiers, docs and comments agree with it. The tier tests,
  #1672's KTLO tests included, read the file; the workflows page says a tier
  change is a one-line edit plus the ruleset.

New tests fail on origin/main and pass here; actionlint findings unchanged.
…1660)

- Review on #1677 asked for a tier file that holds comments.
  .github/python-versions.json becomes .github/python-versions.toml with
  the same data, one inline table per version, so a tier move is still a
  one-line diff. The "//" note and each exemption's reason are comments;
  an exemption has no `reason` key.
- util/python_versions.py and util/ci_timing.py read it with tomllib
  (3.11+). Every job that runs the script on the runner's own python3 is
  on ubuntu-latest or macos-latest, whose python3 is 3.12 or newer. No
  dependency is added.
- Tests that read the file skip without tomllib, as on the KTLO legs
  (3.8-3.10). New tests: the file is TOML and each exemption has a comment,
  each runner's python3 has tomllib, the skips hold without it, and the
  workflows page cites each version's line.
- Sparse checkouts, comments, docs and line citations follow the rename.

New tests fail on the parent and pass here; old and new script outputs are
identical; actionlint findings unchanged.
- lint.yml reads `latest` from .github/python-versions.toml like every other
  single-interpreter job; the file drops its lint exemption.
- On setup-python's 3.14.8 and 3.15.0 builds, pylint 4.1.3 (astroid 4.3.4)
  reports the same messages, and bandit, mypy and vermin the same reports.
  The 3.15 crash recorded earlier came from faking a missing `_decimal` with
  `sys.modules['_decimal'] = None`, which crashes 3.14.8 identically.
- Tests: lint joins the single-interpreter jobs that must run `latest`, and
  its pin at 1024109 joins the scan's known pins.
- CONTRIBUTING and the workflows page no longer name lint's 3.14.

tests/project: 265 passed, 1 skipped; actionlint 1.7.12: 28 findings, as on main.
@JarryShaw

Copy link
Copy Markdown
Owner Author

The red Python 3.15 check is a test defect in this PR, not a regression. Job 115567676540 shows 1 failed, 4274 passed. The failing test is test_without_tomllib_the_tests_that_read_the_file_skip (tests/project/test_python_versions_1660.py:512): its probe interpreter asserts 'tomllib' not in sys.modules at startup.

On the coverage leg, which this PR moves to 3.15, .github/coverage.toml's patch = subprocess starts coverage in every child Python process. Coverage then reads its TOML config, which imports tomllib before the probe's first line runs. No other leg measures coverage, so only this one fails.

The fix is to block the import rather than assert it is absent: sys.modules['tomllib'] = None. It goes into the next push, together with round 3 (lint on 3.15), which is still running.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Correction: pylint works on 3.15, and the crash I reported was our own artifact.

To mimic setup-python's 3.15, which has no C _decimal, earlier rounds set sys.modules['_decimal'] = None. That marks the module as blocked, not absent, and astroid treats a None entry as a loaded module (manager.py:234 → raw_building.py:591), which crashed it. The same trick crashes 3.14 too.

Round 3 measured setup-python's real 3.15.0 tarball, sha256-checked, in an ubuntu:24.04 container. pylint 4.1.3 / astroid 4.3.4 run cleanly with E1101 at 12, the same message set as 3.14, and bandit, mypy and vermin are unchanged. So lint moves entirely to 3.15, and the exemption is gone. A Sonnet check reproduced all of this.

That push also carries the fix for the tomllib probe test, which is being finished now.

…ded (#1660)

- The coverage leg failed test_without_tomllib_the_tests_that_read_the_file_skip:
  `patch = subprocess` in .github/coverage.toml starts coverage in every
  child, and coverage reads its TOML config, so the probe interpreter
  already held tomllib and its `assert 'tomllib' not in sys.modules` raised.
- The probe now drops tomllib and its submodules from sys.modules and sets
  the entry to None, so any import raises ModuleNotFoundError, an
  ImportError, loaded or not. The meta-path finder and the assert go; the
  two scripts' expected error is the new message.

Fails before and passes after under `coverage run --rcfile=.github/coverage.toml`
on 3.14.8 and 3.15.0; the file passes in full (24 passed, 142 subtests), plain
and under coverage; the test passes on 3.8 and 3.10.
@JarryShaw
JarryShaw force-pushed the ci/1660-python-version-tiers-file branch from 627fa25 to 775d6c7 Compare October 13, 2026 22:57
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at 775d6c736. It is rebased onto main at f20d0a600, which includes #1684. Rounds 3 and 4 each passed a Sonnet check. I re-ran test_python_versions_1660.py under coverage run --rcfile=.github/coverage.toml, as the coverage leg runs it: 24 passed. The 1633, 1672 and queue tests gave 74 passed.

  • Round 3, lint on 3.15. lint.yml reads latest, and the lint exemption is dropped (see the correction above). Three pins remain exempt: conda's != '3.15', engine-tests' KTLO 3.10 and pypcap-parity's 3.11 cache key.
  • Round 4, the red Python 3.15 check. The no-tomllib probe now removes any loaded tomllib and blocks it with sys.modules['tomllib'] = None, instead of asserting it is absent.
    • It fails on round 3 under coverage, on 3.14.8 and on 3.15.0, and passes after the fix.
    • Five deliberate breaks of the skip paths are each caught.

This merges cleanly with #1686 and #1688.

@JarryShaw
JarryShaw merged commit 727c0fc into main Oct 14, 2026
49 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in PyPCAPKit Oct 14, 2026
@JarryShaw
JarryShaw deleted the ci/1660-python-version-tiers-file branch October 14, 2026 01:11
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Pull requests that change CI or workflow configuration (ci: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant