Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/coverage.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Coverage configuration for the one coverage-measured leg of unit-tests.yml
# (`test`, Python 3.14), passed with `--rcfile`. Coverage reads a single config
# (`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
# pyproject setting is repeated here unchanged.
Expand Down
59 changes: 59 additions & 0 deletions .github/python-versions.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# The Python version tiers (#1660). Every workflow matrix and every
# single-interpreter job's version is read from here through
# util/python_versions.py, and tests/project/test_python_versions_1660.py pins
# the classifiers and docs to it. A tier change is a one-line edit below plus
# the matching edit to ruleset 23497679's Compat contexts.
#
# Tiers: supported (blocking), ktlo (end-of-life, non-blocking) and pre-release
# (non-blocking; none listed today). `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.

latest = "3.15"

[versions.cpython]
"3.7" = { tier = "ktlo", runner = "ubuntu-22.04" }
"3.8" = { tier = "ktlo", runner = "ubuntu-24.04" }
"3.9" = { tier = "ktlo", runner = "ubuntu-24.04" }
"3.10" = { tier = "ktlo" }
"3.11" = { tier = "supported" }
"3.12" = { tier = "supported" }
"3.13" = { tier = "supported" }
"3.14" = { tier = "supported" }
"3.15" = { tier = "supported" }

[versions.pypy]
"pypy3.10" = { tier = "ktlo" }
"pypy3.11" = { tier = "supported" }

# The pins kept off the tiers, each with its reason in the comment above it.
# tests/project/test_python_versions_1660.py fails on a pin not listed here, on
# an entry that matches no pin, and on an entry without a comment.

# The parity matrix's one supported leg saves the apt cache. Both legs install
# one set under one key, and a second saver fails with `Unable to reserve
# cache`.
[[exemptions]]
workflow = "unit-tests.yml"
job = "pypcap-parity"
where = "env.APT_CACHE_SAVE"
value = "3.11"

# conda-forge's conda has no py315 build, so installing 3.15 into base fails
# with `RemoveError: Conda cannot remove itself` (#1633, conda 26.7.2). 3.15
# leaves base on its own Python; `conda build --python` still builds for 3.15.
# Drop this once conda publishes a py315 build.
[[exemptions]]
workflow = "create-release.yml"
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.
[[exemptions]]
workflow = "cron-conda.yml"
job = "conda-dist"
where = "Conda environment creation and activation: with.python-version"
value = "3.15"
8 changes: 7 additions & 1 deletion .github/workflows/ci-timing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ jobs:
steps:
- uses: actions/checkout@v7

# #1660: `latest` from .github/python-versions.toml, read with the
# runner's own python3.
- name: Read the Python versions
id: python-versions
run: python3 util/python_versions.py --github-output

# Nothing is installed: the script is standard library only.
- uses: actions/setup-python@v7
with:
python-version: "3.14"
python-version: ${{ steps.python-versions.outputs.latest }}

# The newest earlier run that uploaded a report, so a week whose run failed
# on a HARD threshold is still next week's baseline. A first run, or one
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/coverage-comment.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Coverage Comment

# Posts the coverage the `test` job's Python 3.14 leg measured in
# Posts the coverage the `test` job's Python 3.15 leg measured in
# unit-tests.yml as a pull-request comment. It runs no tests. The comment cannot
# be posted from unit-tests.yml itself: that file is also a reusable workflow,
# and a called workflow can only narrow the token its caller passes, never widen
Expand Down Expand Up @@ -82,7 +82,7 @@ jobs:
sys.exit(f'invalid test outcome in artifact: {tests!r}')
total = number(data['total'])
status = '' if tests == 'success' else f' -- **tests {tests}**, so this is partial'
print(f"**Coverage: {total:.2f}%**{status} (unit tier, Python 3.14, "
print(f"**Coverage: {total:.2f}%**{status} (unit tier, Python 3.15, "
f"`{os.environ['HEAD_SHA'][:9]}`, Unit Tests run {os.environ['RUN_CONCLUSION']})")
print()
print('| Package | Statements | Missed | Branches | Partial | Cover |')
Expand Down
89 changes: 48 additions & 41 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,16 @@ jobs:
ref: ${{ github.sha }}
fetch-depth: 0

# #1660: .github/python-versions.toml, read with the runner's own python3:
# `latest` for this job, the ``pypi`` and ``conda`` matrices, and the tags
# the two completeness checks below count, all from this run's commit.
- name: Read the Python versions
id: python-versions
run: python3 util/python_versions.py --github-output

- uses: actions/setup-python@v7
with:
python-version: '3.14'
python-version: ${{ steps.python-versions.outputs.latest }}

- name: Install and Setup
run: |
Expand Down Expand Up @@ -256,9 +263,10 @@ jobs:
tag: "conda-${{ steps.get_version.outputs.PCAPKIT_VERSION }}+0"

# PyPI evidence: compare the file count for this version against the 7
# expected -- the 6 supported legs (``pypi``'s ``strategy.matrix.python-version``)
# each producing a wheel, plus the sdist that the "No Source Distribution"
# step keeps only on the 3.14 leg. A release-level 200 only proves *some*
# expected -- the 6 supported legs (``pypi``'s ``python-version`` axis,
# whose wheel tags ``PCAPKIT_PYPI_WHEELS`` lists) each producing a wheel,
# plus the sdist that the "No Source Distribution" step keeps only on the
# 3.15 leg, ``latest``. A release-level 200 only proves *some*
# file landed, not all of them, so presence alone -- ``PCAPKIT_TAG_EXISTS``'s
# mistake, repeated -- is not enough. Only the supported tier's wheels are
# counted (#1633): a KTLO leg's wheel neither completes the release nor,
Expand All @@ -284,10 +292,13 @@ jobs:
id: check_pypi
env:
PCAPKIT_VERSION: ${{ steps.get_version.outputs.PCAPKIT_VERSION }}
PCAPKIT_PYPI_WHEELS: ${{ steps.python-versions.outputs.pypi-wheels }}
run: |
set -euo pipefail

expected=7
# One wheel per supported leg, plus the sdist.
read -r -a wheels <<< "$PCAPKIT_PYPI_WHEELS"
expected=$(( ${#wheels[@]} + 1 ))
# `--retry-connrefused` and the `|| status=000` fallback keep a
# transient pypi.org outage from failing this step outright: under
# `set -e` a bare curl failure here would fail `version_check`,
Expand All @@ -313,10 +324,10 @@ jobs:
# `python3 -` reads from stdin, so nothing is indented that
# Python did not itself put there.
count="$(printf '%s\n' \
"import json" \
"import json, os" \
"with open('/tmp/pypi_release.json') as f:" \
" data = json.load(f)" \
"supported = {'cp311', 'cp312', 'cp313', 'cp314', 'cp315', 'pp311'}" \
"supported = set(os.environ['PCAPKIT_PYPI_WHEELS'].split())" \
"names = [u.get('filename', '') for u in data.get('urls', [])]" \
"wheels = {n.split('-')[2] for n in names if n.endswith('.whl') and n.count('-') == 4}" \
"print(len(wheels & supported) + any(n.endswith('.tar.gz') for n in names))" \
Expand All @@ -343,10 +354,13 @@ jobs:
id: check_conda
env:
PCAPKIT_VERSION: ${{ steps.get_version.outputs.PCAPKIT_VERSION }}
PCAPKIT_CONDA_BUILDS: ${{ steps.python-versions.outputs.conda-builds }}
run: |
set -euo pipefail

expected=10
# Each of ``conda``'s two platforms, for each supported version.
read -r -a builds <<< "$PCAPKIT_CONDA_BUILDS"
expected=$(( 2 * ${#builds[@]} ))
# Same coupling risk, and the same fix, as the PyPI check above.
status="$(curl -s --retry 3 --retry-connrefused \
-o /tmp/anaconda_release.json -w '%{http_code}' \
Expand All @@ -358,11 +372,11 @@ jobs:
# one line per `printf` argument, no line indented in the actual
# source `python3 -` reads.
count="$(printf '%s\n' \
"import json" \
"import json, os" \
"with open('/tmp/anaconda_release.json') as f:" \
" data = json.load(f)" \
"platforms = {'linux-64', 'osx-64'}" \
"py_tags = {'py311', 'py312', 'py313', 'py314', 'py315'}" \
"py_tags = set(os.environ['PCAPKIT_CONDA_BUILDS'].split())" \
"seen = {" \
" (d.get('attrs', {}).get('subdir'), tag)" \
" for d in data.get('distributions', [])" \
Expand Down Expand Up @@ -390,6 +404,10 @@ jobs:
PCAPKIT_PYPI_COMPLETE: ${{ steps.check_pypi.outputs.PCAPKIT_PYPI_COMPLETE }}
PCAPKIT_CONDA_COMPLETE: ${{ steps.check_conda.outputs.PCAPKIT_CONDA_COMPLETE }}
PCAPKIT_CONDA_LABEL: ${{ steps.get_version.outputs.PCAPKIT_CONDA_LABEL }}
# #1660: from .github/python-versions.toml, for ``pypi`` and ``conda``.
PYTHON_LATEST: ${{ steps.python-versions.outputs.latest }}
PYTHON_PYPI: ${{ steps.python-versions.outputs.pypi }}
PYTHON_CONDA: ${{ steps.python-versions.outputs.conda }}

# No ``environment:`` here, and none needed: this is a test gate with no outward
# effect, and a job that calls a reusable workflow may not declare one anyway.
Expand Down Expand Up @@ -797,29 +815,22 @@ jobs:
## one leg failing must not cancel its siblings: each leg still has its own
## wheel to attach to the GitHub Release, and a cancelled leg never does
fail-fast: false
matrix:
# #1633: the supported tier, whose wheels version_check counts.
python-version:
- "3.11"
- "3.12"
- "3.13"
- "3.14"
- "3.15"
- "pypy3.11"
# The KTLO tier: Python 3.10 language level, end-of-life, still built
# and published but never blocking. 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 step after
# setup-python waits on the ``outcome`` of the one it needs, so a KTLO
# leg stops at its first failure, where a supported leg stops, rather
# than building with the image's ``python`` or uploading nothing; and
# ``KTLO summary`` reports each failed step as a warning.
include:
- python-version: "3.10"
ktlo: true
- python-version: "pypy3.10"
ktlo: true
# #1660: CPython and PyPy from .github/python-versions.toml, through
# ``version_check``. #1633: the supported tier is the ``python-version``
# 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
# 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
# step after setup-python waits on the ``outcome`` of the one it needs,
# so a KTLO leg stops at its first failure, where a supported leg stops,
# rather than building with the image's ``python`` or uploading nothing;
# and ``KTLO summary`` reports each failed step as a warning. A KTLO leg
# also needs its own ``ktlo_*`` output above, which
# tests/project/test_python_matrix_tiers_1633.py checks.
matrix: ${{ fromJSON(needs.version_check.outputs.PYTHON_PYPI) }}
steps:
# #1634: this run's commit, not ``ref: v<version>`` -- that tag was
# whatever ``main`` had become by approval time -- and then the check
Expand Down Expand Up @@ -878,7 +889,7 @@ jobs:

- name: No Source Distribution for Python ${{ matrix.python-version }}
id: no_sdist
if: ${{ matrix.python-version != '3.14' && steps.build.outcome == 'success' }}
if: ${{ matrix.python-version != needs.version_check.outputs.PYTHON_LATEST && steps.build.outcome == 'success' }}
continue-on-error: ${{ matrix.ktlo == true }}
run: |
rm dist/*.tar.gz
Expand Down Expand Up @@ -1037,16 +1048,12 @@ jobs:
- macos-latest
- ubuntu-latest
# - windows-latest # skip for now
# #1633: the supported tier only. There is no KTLO 3.10 leg because it
# #1633: the supported tier only, from .github/python-versions.toml
# through ``version_check`` (#1660). There is no KTLO 3.10 leg because it
# can only fail: conda/requirements.txt pins soupsieve==3.0.3
# (Requires-Python >=3.11.5), and util/conda-dist.py pins the newest
# release of every dependency, so that floor only rises.
python-version:
- "3.11"
- "3.12"
- "3.13"
- "3.14"
- "3.15"
python-version: ${{ fromJSON(needs.version_check.outputs.PYTHON_CONDA) }}
steps:
# #1634: this run's commit, not ``ref: conda-<version>+0``, then the check
# that both tags these packages are filed under name it.
Expand Down
19 changes: 11 additions & 8 deletions .github/workflows/cron-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,15 @@ jobs:
persist-credentials: false
fetch-depth: 0

# #1660: `latest` for this job and `conda-dist`'s versions, from
# .github/python-versions.toml, read with the runner's own python3.
- name: Read the Python versions
id: python-versions
run: python3 util/python_versions.py --github-output

- uses: actions/setup-python@v7
with:
python-version: '3.14'
python-version: ${{ steps.python-versions.outputs.latest }}

# `.[all,dev]`, not `.[all]` alone: `util/conda-dist.py` below only
# *refreshes* the version pin of every package already named in
Expand Down Expand Up @@ -302,6 +308,7 @@ jobs:
PCAPKIT_VERSION: ${{ steps.get_version.outputs.PCAPKIT_VERSION }}
PCAPKIT_CONDA_LABEL: ${{ steps.get_version.outputs.PCAPKIT_CONDA_LABEL }}
PCAPKIT_BUILD: ${{ steps.get_version.outputs.PCAPKIT_BUILD }}
PYTHON_CONDA: ${{ steps.python-versions.outputs.conda }}

conda-tag:
# See create-release.yml: "(update)" keeps this apart from the Conda Tag job
Expand Down Expand Up @@ -424,14 +431,10 @@ jobs:
- macos-latest
- ubuntu-latest
# - windows-latest # skip for now
# #1633: the supported tier; create-release.yml's ``conda`` job says why
# #1633: the supported tier, from .github/python-versions.toml through
# `conda-update` (#1660); create-release.yml's ``conda`` job says why
# there is no KTLO 3.10 leg.
python-version:
- "3.11"
- "3.12"
- "3.13"
- "3.14"
- "3.15"
python-version: ${{ fromJSON(needs.conda-update.outputs.PYTHON_CONDA) }}
steps:
- uses: actions/checkout@v7
with:
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/cron-vendor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,15 @@ jobs:
# live credential for anything running in between to find.
persist-credentials: false

# #1660: `latest` from .github/python-versions.toml, read with the
# runner's own python3.
- name: Read the Python versions
id: python-versions
run: python3 util/python_versions.py --github-output

- uses: actions/setup-python@v7
with:
python-version: '3.14'
python-version: ${{ steps.python-versions.outputs.latest }}

- name: Install and Setup
run: |
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/deploy-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,15 @@ jobs:
# question (#1643).
persist-credentials: false

# #1660: `latest` from .github/python-versions.toml, read with the
# runner's own python3.
- name: Read the Python versions
id: python-versions
run: python3 util/python_versions.py --github-output

- uses: actions/setup-python@v7
with:
python-version: '3.14'
python-version: ${{ steps.python-versions.outputs.latest }}

# Builds the Sphinx documentation into `docs/build/html`, which the deploy
# step below publishes. `PCAPKIT_SPHINX` is what makes the
Expand Down
19 changes: 14 additions & 5 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,22 @@ jobs:
# source, and running them several times over would produce several
# copies of the same findings for several times the runner cost.
#
# 3.14 specifically, matching cron-vendor.yml, because it is the version
# the recorded counts above were measured on -- so a count that moves
# means the code moved, not the interpreter. 3.15 is newer, and supported
# since #1633, but moving to it would re-baseline every count above.
# `latest` from .github/python-versions.toml, like every single-interpreter
# job (#1661). Moving off 3.14 changes no count. Measured at 0bc157660
# on setup-python's 3.14.8 and 3.15.0 builds: pylint 4.1.3 (astroid
# 4.3.4) reports the same 515 C, 71 E and 4970 W, message for message;
# only R0401 cyclic-import differs, as it does between two runs on one
# interpreter (see the header). bandit, mypy and vermin print identical
# reports. To test a 3.15 without C `_decimal`, use setup-python's build,
# which has none: faking it with `sys.modules['_decimal'] = None` makes
# astroid crash on pcapkit/toolkit/dpkt.py on 3.14.8 too (#1677).
- name: Read the Python versions
id: python-versions
run: python3 util/python_versions.py --github-output

- uses: actions/setup-python@v7
with:
python-version: '3.14'
python-version: ${{ steps.python-versions.outputs.latest }}

# `.[all,dev]` rather than a bare install: pylint and mypy resolve imports,
# so findings depend on what is importable. #910 narrowed `all` to core
Expand Down
Loading
Loading