Skip to content

Update CI: drop EOL jobs, test Python 3.11-3.15, add timeouts - #578

Merged
tompollard merged 1 commit into
MIT-LCP:mainfrom
cbrnr:ci-update
Oct 27, 2026
Merged

tompollard merged 1 commit into
MIT-LCP:mainfrom
cbrnr:ci-update

Conversation

@cbrnr

@cbrnr cbrnr commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

The test workflow has been failing for reasons unrelated to the code. This PR fixes the workflows and brings them up to date. It is the first of a series of smaller PRs that modernize the project's tooling (the next ones will raise the minimum Python version, switch the build backend and replace black/pylint with Ruff).

Changes

  • Remove the Debian 11 i386 job. Debian 11 is end-of-life, and its security repository on deb.debian.org no longer serves the packages it lists, so apt-get install fails with 404s.
  • Test on Python 3.11-3.15 and drop the numpy==1.26.4 job (NumPy 1.26 is the last release that supports Python 3.9). uv run now gets --python, so every matrix entry actually runs with its own interpreter (previously the interpreter was installed, but not selected).
  • Add a 45 minute timeout to the test job. Slow PhysioNet downloads made jobs hang until GitHub cancelled them after 6 hours.
  • Update actions and the Read the Docs image. actions/checkout, astral-sh/setup-uv and the RTD build image (Ubuntu 20.04 is gone) are updated. The docs workflow sets up Python 3.12 explicitly. Dependabot keeps GitHub Actions up to date from now on.
  • Replace the stale Poetry instructions in DEVELOPING.md with the uv equivalent.

Notes

  • requires-python still says >= 3.9. The next PR raises it to 3.11 to match what CI tests.
  • Some tests fail when PhysioNet is slow or unreachable (the failures in the previous CI run were ChunkedEncodingError/ReadTimeout from physionet.org). This PR does not change that, it only makes sure such a job fails after 45 minutes instead of 6 hours. A later PR makes these tests skip instead of fail.

Testing

I could not run the workflows themselves, but I checked that all YAML files parse, that uv run --python 3.11 and --python 3.15 select the requested interpreter, that the docs build with docs/requirements.txt on Python 3.12, and that the existing test suite on 3.11 and 3.15 only fails in the 17 tests that need PhysioNet when offline.

The test workflow was failing for reasons unrelated to the code.

- Remove the Debian 11 i386 job: Debian 11 is end-of-life and its security
  repository on deb.debian.org no longer serves the packages it lists, so
  `apt-get install` fails with 404s.
- Test on Python 3.11-3.15 and drop the numpy==1.26.4 job (NumPy 1.26 is
  the last release that supports Python 3.9). Pass --python to uv run so
  that every matrix entry actually runs with its own interpreter.
- Add a 45 minute timeout to the test job. Slow PhysioNet downloads made
  jobs hang until GitHub cancelled them after 6 hours.
- Update actions/checkout, astral-sh/setup-uv and the Read the Docs build
  image, and set up Python explicitly in the docs workflow. Add Dependabot
  to keep GitHub Actions up to date.
- Replace the stale Poetry instructions in DEVELOPING.md with the uv
  equivalent.
@cbrnr

cbrnr commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

As already expected, jobs fail due to PhysioNet being unreachable. I would still recommend to merge this PR, I'll submit a follow-up PR which skips all tests that rely on the network (PhysioNet in particular) next.

@tompollard

Copy link
Copy Markdown
Member

Thanks for looking at this @cbrnr. We're working on the PhysioNet slowness/unavailability. In the process of migrating the platform to new infrastructure.

@tompollard tompollard left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @cbrnr, looks good to me

@tompollard
tompollard merged commit c9467bc into MIT-LCP:main Oct 27, 2026
7 of 17 checks passed
@cbrnr

cbrnr commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @tompollard! I hope everything will be fine and PhysioNet will be available again!

@cbrnr
cbrnr deleted the ci-update branch October 27, 2026 17:35
cbrnr added a commit that referenced this pull request Oct 27, 2026
This is the second PR of a series that modernizes the project's tooling
(see #578 for the first one, which updated the CI workflows and tests
Python 3.11-3.15).

## Changes

- **Require Python >= 3.11.** Python 3.9 is end-of-life and 3.10 will be
soon, and CI no longer tests them. `black`'s target version is updated
accordingly.
- **Raise the minimum versions of some dependencies.** `soundfile`
(0.10.0 -> 0.12.1), `matplotlib` (3.2.2 -> 3.8.0) and `requests` (2.8.1
-> 2.31.0) had lower bounds that are much older than anything we test.
The development dependencies `pytest` (8.0.0) and `pytest-xdist` (3.5.0)
are raised as well. The minimum versions of `numpy`, `scipy`, `pandas`,
`fsspec` and `aiohttp` are unchanged.
- **Add a "minimum versions" job.** It runs the tests on Python 3.11
with the lowest allowed versions of all direct dependencies (`uv run
--resolution lowest-direct`), so a lower bound cannot silently become
unusable.
- **Add a changelog entry.**

## Notes

- The new job runs the whole test suite, so it is affected by the same
slow or unreachable PhysioNet downloads as the other jobs (it has the
same 45 minute timeout). A later PR will make the PhysioNet tests skip
instead of fail.

## Testing

I resolved the lowest-direct environment locally on Python 3.11 (numpy
1.26.4, scipy 1.13.0, pandas 2.2.3, soundfile 0.12.1, matplotlib 3.8.0,
requests 2.31.0, fsspec 2023.10.0, aiohttp 3.10.11, pytest 8.0.0,
pytest-xdist 3.5.0). With PhysioNet blocked, the test suite there passes
except for the same 17 tests that need PhysioNet, and `black --check`
passes with the new target version.
tompollard added a commit that referenced this pull request Oct 27, 2026
Third PR of the tooling series (after #578 and #580). The project
already uses uv for package management, so this switches the build
backend to `uv_build` as well.

## Changes

- **Build backend:** `uv_build >= 0.13.0, < 0.14` replaces `hatchling`.
The package lives in the project root (not in `src/`), so `module-root =
""` is set.
- **Static version:** `uv_build` does not support dynamic versions, so
the version is now only specified in `pyproject.toml` (the release
instructions in `DEVELOPING.md` are updated). `wfdb.__version__` is read
from the installed package metadata, and it is `"unknown"` if `wfdb` is
imported from a source tree that has not been installed. The
documentation reads the version from `pyproject.toml`.
- **License:** `uv_build` does not include the license file
automatically, so `license-files = ["LICENSE"]` is set. I also switched
to the SPDX expression `license = "MIT"` (the old table form is
deprecated).
- **Changelog entry.**

## What changes in the distributions

- **Wheel:** the same files as before, the metadata is the same except
for the license field (`License-Expression: MIT` instead of `License:
MIT License`).
- **sdist:** it now only contains the `wfdb` package, `LICENSE`,
`README.md` and `pyproject.toml` (plus `PKG-INFO`). The hatchling sdist
also contained `docs/`, `.github/`, `DEVELOPING.md`, `pylintrc` and some
dotfiles (tests, sample data and the demo files were already excluded).
I think that is fine, but let me know if you would like to keep some of
these in the sdist.

## Testing

- Built the sdist and wheel with the latest `uv_build` (0.13.6) and with
the lowest allowed version (0.13.0), and compared the result with the
hatchling build (see above).
- Installed the wheel: `wfdb.__version__` is `4.3.1`. In an editable
install it is `4.3.1`, and importing from an uninstalled source tree
gives `unknown`.
- The docs build as before (same warnings) and show version 4.3.1.
- The test suite behaves as before (only the 17 tests that need
PhysioNet fail when offline), and `black --check` passes.
tompollard added a commit that referenced this pull request Oct 27, 2026
Fourth PR of the tooling series (after #578, #580 and #581). It replaces
`black` (formatting) and `pylint` (which was not run anywhere, and had a
560 line config) with [Ruff](https://docs.astral.sh/ruff/), which does
both.

The PR consists of four commits, and I suggest reviewing them one by
one:

1. **Replace black and pylint with Ruff:** the actual configuration. The
`dev` extra now contains `ruff >= 0.13.0` (the first version that agrees
with the rules below) instead of `black` and `pylint`, the style
workflow runs `ruff format --check` and `ruff check`, `pylintrc` is
removed, and the developer docs and the changelog are updated.
2. **Fix a test that was never run:** `tests/test_multi_record.py`
defines `test_contained_combined_ranges_variable_layout` twice, so the
first definition was shadowed and never ran (Ruff reports this as F811).
I renamed the second test. Both pass.
3. **Apply ruff formatting and safe lint fixes:** purely mechanical (34
files). You can reproduce it with `ruff check --fix . && ruff format .`
on the first two commits. It contains the formatting, plus the safe
autofixes of the enabled rules: sorted imports, f-strings instead of
`str.format()`, `list[...]`/`X | None` instead of `typing` aliases,
`class X:` instead of `class X(object):`, and unused imports.
4. **Ignore the reformatting commit in git blame:** adds
`.git-blame-ignore-revs` with commit 3 (GitHub uses this file
automatically; locally, run `git config blame.ignoreRevsFile
.git-blame-ignore-revs`, which is documented in `DEVELOPING.md`). Note
that the hash only stays valid if the commits of this PR are preserved,
i.e. with a merge commit (as for the previous PRs), not when squashing
or rebasing.

## Configuration

- **Line length:** Ruff's default of 88 instead of black's 80 (as
configured so far).
- **Rules:** `E`, `F`, `W` (pycodestyle/pyflakes), `I` (isort), `UP`
(pyupgrade) and `B` (bugbear). `E501` is ignored because the formatter
takes care of line lengths, and `F401` is ignored in `__init__.py` files
(re-exports).
- **Existing violations:** I did not want to change code behavior in
this PR, so the rules whose fixes are not safe are ignored for now
(about 90 occurrences in total): `E711`, `E721`, `E722`, `E741`, `F841`,
`UP031`, `B006`, `B007`, `B020`, `B904`, `B905`. They can be enabled one
by one in follow-up PRs.
- Jupyter notebooks are not checked (black did not check them either).

## Testing

- `ruff check .` and `ruff format --check .` pass (also with the oldest
allowed Ruff, 0.13.0), and all modules still import.
- The test suite behaves as before on Python 3.11 with the lowest
allowed dependency versions and on 3.15 with the latest (65 passed, and
only the 17 tests that need PhysioNet fail when offline; the test that
was shadowed before is now among the passing ones).
- With the ignore file, `git blame` attributes lines changed by commit 3
to their original commits.
tompollard added a commit that referenced this pull request Oct 28, 2026
#589)

This is the last part of the cleanup that started with #578, #580, #581,
#582 and #588. It builds the documentation without warnings, applies one
docstring style everywhere, and moves the package to a `src` layout.
Each commit can be reviewed on its own.

1. **Fix all Sphinx documentation warnings.** The docs build reported 94
warnings. They are fixed, and the docs workflow now builds with `-W
--keep-going` so they stay fixed.
- Duplicate object descriptions: repeated `automodule` directives and
the classes documented on both the `wfdb` and `io` pages are marked
`:no-index:`, so each object is indexed once.
- `numpydoc_class_members_toctree = False`, because the autosummary
stubs it links to do not exist.
- Too short title underlines, and reStructuredText errors in docstrings
(lists, literal blocks for the EDF/MATLAB/WAV format descriptions and
tables, doctest continuation lines).
- The `:no-index:` option needs Sphinx 7.2, so the pinned Sphinx version
of the `docs` group goes from 7.0.0 to 7.2.6.
2. **Wrap docstrings and comments at 88 columns**, the same width as the
code, and enable W505 in Ruff so it stays that way. This is a mechanical
change, and the commit is listed in `.git-blame-ignore-revs`.
3. **Start docstrings on the opening quote line with a one-line
summary.** Summaries that were wrapped over several lines or too long
for one line are shortened, and the rest of the text moves into the
description below. No code changes: comparing the ASTs with docstrings
removed shows no difference. A few docstrings were reworded to fit, and
the descriptions of `csv_to_wfdb` and `csv2ann` (which described reading
a WFDB header) now match what the functions do. Also listed in
`.git-blame-ignore-revs`.
4. **Move the package to `src/wfdb`.** This is the default module root
of `uv_build`, so the `module-root` setting is removed. The tests now
always run against the installed package, and `docs/conf.py` adds `src`
to `sys.path`. Files are moved without changes, so `git blame` and `git
log --follow` keep working. The wheel and the sdist build as expected.
5. **Ignore the two reformatting commits in git blame**, and add a note
about the docstring style to `DEVELOPING.md`. The hashes stay valid with
a merge commit, as in #582.
6. **Add `AGENTS.md` and `CLAUDE.md`** with short guidance for coding
agents (commands and conventions). `CLAUDE.md` only points to
`AGENTS.md`.

Checked locally: Ruff lint and format, the docs build with `-W
--keep-going`, and the tests that work offline on Python 3.11 with the
lowest dependencies and on 3.15.

@tompollard this concludes the tooling and infrastructure refresh. Any
remaining changes are minor, such as fixing the ignored Ruff rules,
which can be done later in follow-up PRs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants