Matches the convention used by pyPhotoAlbum and the other projects here:
runs-on: linux/amd64 with a container image from the Gitea registry,
instead of setup-python plus an ad-hoc `pip install pytest pytest-cov
flake8 coverage-badge interrogate` on a self-hosted runner.
pyWebLayout is a library, so the image carries all four interpreters
pyproject.toml claims to support - 3.10, 3.11, 3.12, 3.13 - each in its
own venv at /opt/py<version> with every dependency pre-installed. The
matrix picks one per job. A CI run now downloads nothing, and coverage
widens from 3.10/3.12/3.13 to the full declared range.
Ubuntu marks its system Python externally-managed, so per-interpreter
venvs are used rather than --break-system-packages; that also keeps the
four dependency sets isolated.
Two defects in the existing workflow are fixed while rewriting it:
- pytest runs under continue-on-error so the badge steps still execute,
but nothing afterwards checked its outcome - the job reported green on
a red suite. An explicit gate now fails the job.
- Every matrix leg ran the badge steps and force-pushed the badges
branch, so three jobs raced to publish. Badges and artifacts are now
produced by the 3.13 leg only.
setuptools is pinned below 81 in the image: that release dropped
pkg_resources, which coverage-badge imports at startup, and without the
pin the badge step dies with ModuleNotFoundError. Found by running the
workflow's own commands in the image rather than assuming they work.
Also raises the test Flask server's readiness budget from 5s to 30s.
Making that check raise instead of silently falling through (737cf07)
turned runner load into a hard failure; it showed up as 17 spurious
errors in one containerised run and did not reproduce in three repeats.
The loop still exits as soon as the server answers.
Verified locally: image builds, and tests/ passes 916 on each of 3.10,
3.11, 3.12 and 3.13 inside it. The publishing leg was run end to end -
clean-install dependency check, pytest with coverage, both badges,
coverage summary at 81.5%.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The project carried three sets of packaging metadata — pyproject.toml
[project], setup.cfg [options] and setup.py kwargs — declaring different
dependencies. pyproject.toml wins under any modern backend, so the wheel
was correct, but the other two said "Pillow, numpy" and reading either
gave the wrong answer about what the library needs.
Reduce setup.cfg to its [flake8] section and setup.py to a setup() shim,
each pointing at pyproject.toml.
Correct the authoritative list while consolidating:
- flask was a runtime dependency but is imported only by the fixture HTTP
server in tests. Every user was installing Flask, Jinja2, Werkzeug,
click, itsdangerous and blinker for nothing.
- ebooklib was a runtime dependency and is never imported by the library;
epub_reader.py uses zipfile + xml.etree directly. Only the tests use it,
to build EPUB fixtures.
- requests was declared required but concrete/image.py imports it lazily
and degrades gracefully when absent, so it belongs in an extra.
- requires-python said >=3.6, which cannot be true: the package uses
dataclasses and `from __future__ import annotations`, both 3.7+, and CI
tests 3.10/3.12/3.13.
Runtime install drops from 7 direct dependencies to 4. Adds test,
remote-images and dev extras, and a CI step that installs into an empty
venv with only the runtime deps and imports every subpackage — this class
of defect is only caught by installing what you ship.
Verified: runtime-only install imports all subpackages; `.[test]` runs the
full suite, 853 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>