fix(viewer): measure the 3d scene fill from mplot3d and add CI on Python 3.8-3.14 - #8
Merged
Conversation
…tself, so the viewer sizes its window correctly on matplotlib 3.7 as well
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Make GitHub run pyLEAFS's tests automatically on every push and every pull request, so nothing lands untested. Add .github/workflows/tests.yml that installs the library exactly the way the repository's published instructions describe (pip install -e '.[test]', using the declared test extra rather than installing pytest by hand), runs pytest, and does so across the full range of Python versions the repository CLAIMS to support - 3.8 through 3.14, read from requires-python, the classifiers, and the README - rather than a convenient subset. A second job builds the manual with the docs extra (pip install -e '.[docs]'; make -C docs html) so a broken documentation build is caught too. The workflow is deliberately boring: only actions/checkout and actions/setup-python, a plain version matrix with fail-fast disabled, and no caching cleverness or third-party actions.
While verifying the matrix, Python 3.8 turned out to genuinely fail: 3.8 caps matplotlib at 3.7.5, and test_viewer_3d_fill_is_the_true_worst_case_over_all_view_angles asserted against hard-coded fill constants (0.9526, 1.0302) that only hold on matplotlib 3.8+. matplotlib 3.8 multiplied its normalised 3d box aspect by an extra 25/24, so the same world box covers more of a 3d axes there than on 3.7; under the default perspective projection that enlargement is not a plain rescale, so both the width and the height fractions grow, and by slightly different amounts (3.7.5 measures 0.91276, 0.98890). The captain's explicit decision was to KEEP Python 3.8 supported and fix the cause rather than drop the version or loosen the test, and authorised expanding scope for exactly that. So pyLEAFS/viewer.py now derives those two numbers instead of hard-coding them: _scene_fill_3d() measures mplot3d's own projection of a unit box at the two closed-form worst view angles on a throwaway off-screen figure and caches the result, which is correct on every matplotlib version. The value is a property of mplot3d alone - verified independent of world shape, figure size, and axes rectangle - so measuring it once off-screen is legitimate. The test now compares the swept maximum against that measurement instead of a literal, and because the sweep repeats the identical worst-case projection it lands on the measured value to within pixel-transform rounding, so the no-overflow assertion carries a 1e-9 epsilon while the 5e-4 'nothing is lost' tolerance is unchanged; the test was not weakened into meaninglessness. Verified locally: the full suite (38 tests) passes on 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14, including a cross-check with matplotlib 3.7.5 pinned on 3.9, and the docs build succeeds. The published install instructions themselves proved correct on a clean environment, so no instruction fix was needed. Scope is deliberately limited to the workflow plus the viewer/test fix the 3.8 decision forced.
What Changed
pyLEAFS/viewer.pynow derives the 3d scene fill instead of hard-coding it:_scene_fill_3d()measures mplot3d's own projection of a unit box at the two closed-form worst view angles on a throwaway off-screen figure, cached withfunctools.lru_cache. The old_SCENE_FILL_3D = (0.9526, 1.0302)constants only held on matplotlib 3.8+, which scaled the normalised 3d box aspect by an extra 25/24, so the 3d viewer window is now sized correctly on matplotlib 3.7 as well - the version Python 3.8 is capped at.tests/test_pyLEAFS.pycompares the swept maximum against that measurement rather than the literals; the no-overflow assertion carries a 1e-9 epsilon because the sweep repeats the identical worst-case projection, while the 5e-4 "nothing is lost" tolerance is unchanged..github/workflows/tests.ymlrunspip install -e '.[test]'thenpyteston every push and pull request across Python 3.8-3.14 (fail-fast: false, pinned toubuntu-24.04since no 3.8/3.9 builds exist for newer images), plus a second job that builds the manual withpip install -e '.[docs]'andmake -C docs html. Onlyactions/checkoutandactions/setup-python, no caching,permissions: contents: read.docs/MakefiledefaultsSPHINXOPTSto-Wso Sphinx warnings fail that build.Risk Assessment
✅ Low: All four previously agreed fixes were applied exactly as instructed and verified against the runner-image and python-versions manifests, the Sphinx config, and the test import, leaving a well-bounded change that satisfies every stated acceptance criterion with no outstanding findings.
Testing
I ran the new workflow's two jobs locally, step for step, on fresh checkouts:
pip install -e '.[test]'+pytestpasses 38 tests on all seven Python versions the repository claims (3.8 through 3.14, with matplotlib resolving to 3.7.5 on 3.8), plus a matplotlib-3.7.5-pinned cross-check on 3.9, and the docs job builds the manual with-Wproven active by making a toctree warning fail the build. I confirmed the Python 3.8 fix addresses a real failure rather than a hypothetical one - the base commit fails the fill test on matplotlib 3.7.5 with exactly the reported values while the target commit passes - and captured before/after screenshots of the 3d viewer showing the world box going from 95.2% x 96.0% of its reserved window area to 99.2% x 100.0% on 3.7.5, with modern matplotlib unaffected. I also verified the workflow structure against requires-python, the classifiers and the README, checked that the ubuntu-24.04 pin is what makes 3.8/3.9 resolvable at all, confirmed the measured fill is configuration-independent to 1e-16, and mutation-tested the assertions to show the 1e-9 epsilon did not weaken them. Everything passed; the worktree is clean and temporary environments were removed./var/folders/ym/vnmfjh5n7dn2vdzdgy7zrbjh0000gn/T/no-mistakes-evidence/01M0D5TVKGS5R5XC1DPXK32H2J/viewer-mpl375-before-after.png)/var/folders/ym/vnmfjh5n7dn2vdzdgy7zrbjh0000gn/T/no-mistakes-evidence/01M0D5TVKGS5R5XC1DPXK32H2J/viewer-mpl375-after-annotated.png)/var/folders/ym/vnmfjh5n7dn2vdzdgy7zrbjh0000gn/T/no-mistakes-evidence/01M0D5TVKGS5R5XC1DPXK32H2J/viewer-mpl375-before-annotated.png)/var/folders/ym/vnmfjh5n7dn2vdzdgy7zrbjh0000gn/T/no-mistakes-evidence/01M0D5TVKGS5R5XC1DPXK32H2J/viewer-mpl311-after-annotated.png)/var/folders/ym/vnmfjh5n7dn2vdzdgy7zrbjh0000gn/T/no-mistakes-evidence/01M0D5TVKGS5R5XC1DPXK32H2J/viewer-mpl375-after-plain.png)Evidence: Local transcript of both workflow jobs across the full matrix
----- tests (python-version: 3.8) ----- python 3.8.20 | matplotlib 3.7.5 | numpy 1.24.4 | pytest 8.3.5 38 passed in 39.23s ----- tests (python-version: 3.9) ----- python 3.9.25 | matplotlib 3.9.4 | numpy 2.0.2 | pytest 8.4.2 38 passed, 14 warnings in 46.01s ----- tests (python-version: 3.10) ----- python 3.10.21 | matplotlib 3.10.9 | numpy 2.2.6 | pytest 9.1.1 38 passed in 46.05s ----- tests (python-version: 3.11) ----- python 3.11.16 | matplotlib 3.11.1 | numpy 2.4.6 | pytest 9.1.1 38 passed in 44.93s ----- tests (python-version: 3.12) ----- python 3.12.14 | matplotlib 3.11.1 | numpy 2.5.2 | pytest 9.1.1 38 passed in 46.02s ----- tests (python-version: 3.13) ----- python 3.13.15 | matplotlib 3.11.1 | numpy 2.5.2 | pytest 9.1.1 38 passed in 46.42s ----- tests (python-version: 3.14) ----- python 3.14.7 | matplotlib 3.11.1 | numpy 2.5.2 | pytest 9.1.1 38 passed in 45.57s ----- cross-check: python 3.9 with matplotlib pinned to 3.7.5 ----- 38 passed, 11 warnings in 31.03s ----- docs job (3.14, pip install -e '.[docs]'; make -C docs html) ----- Running Sphinx v9.1.0 build succeeded. ----- docs: -W is in effect and does fail a warning ----- $ make -C docs -n html -> sphinx-build -M html "." "_build" -W with a page left out of the toctree: build finished with problems, 1 warning (with warnings treated as errors). make: *** [html] Error 1 ----- regression check: base commit 5c9d512 on python 3.8 / matplotlib 3.7.5 ----- FAILED test_viewer_3d_fill_is_the_true_worst_case_over_all_view_angles np.allclose(array([0.9127639, 0.9888995]), (0.9526, 1.0302), atol=0.0005) -> False same test at f2bf5a4 on the same interpreter: passed (fill measured as 0.91276 x 0.98890)Evidence: setup-python manifest: 3.8/3.9 have no build newer than ubuntu-24.04
Evidence: Measured fill is a property of mplot3d alone (36 configurations, two matplotlib versions)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 4 issues found → auto-fixed ✅
.github/workflows/tests.yml:9- The matrix job pinsruns-on: ubuntu-latest, but actions/python-versions publishes no ubuntu-26.04 builds for Python 3.8 or 3.9 (manifest coverage: 3.8/3.9 -> 16.04-24.04; 3.10+ -> up to 26.04), and neither will be added since both are EOL upstream. ubuntu-26.04 images already exist in preview and the runner-images README documents a gradual-latestmigration to the newest stable image. When that migration reaches ubuntu-latest, the "3.8" and "3.9" legs fail at the setup-python step with "Version 3.8 with arch x64 not found" - silently losing exactly the 3.8 coverage this change expanded scope to preserve (the viewer/test fix exists only because 3.8 must stay tested). Suggested fix: pin the matrix job toruns-on: ubuntu-24.04(the docs job on 3.14 can stay on ubuntu-latest). This is a runner-policy call rather than a mechanical fix, so it is flagged for the author rather than changed here..github/workflows/tests.yml:7- Nopermissions:block, so both jobs receive the repository's default GITHUB_TOKEN scope (write-all on repos created before the default changed). Neither job needs anything beyond reading the checkout. Addingpermissions:\n contents: readat the workflow level costs nothing and keeps the workflow as boring as intended - no new actions, no caching..github/workflows/tests.yml:32-make -C docs htmlinvokes sphinx-build without-W, and docs/Makefile passesSPHINXOPTSthrough empty by default, so Sphinx warnings (broken cross-references, missing toctree entries, autodoc import warnings) exit 0 and pass CI. The job therefore catches hard build failures - the majority case - but not silent documentation rot.SPHINXOPTS=-W make -C docs htmlwould catch both; whether the manual should be warning-clean is the author's call, hence flagged rather than changed.pyLEAFS/viewer.py:56- The module-level_scene_fill_3d_cachesentinel plus theglobalstatement andis Noneguard hand-roll a memoisation thatfunctools.lru_cache(maxsize=None)on_scene_fill_3d()provides directly (available on Python 3.8;functools.cacheis 3.9+, so it cannot be used given the 3.8 floor). That removes the module global, theglobaldeclaration, and one level of indentation from the body while keeping identical measure-once semantics.🔧 Fix: Pin CI runner, add permissions, -W docs, lru_cache memo
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
Local mirror of the workflowtestsjob on a fresh checkout per version:python$V -m venv,python -m pip install --upgrade pip,pip install -e '.[test]',pytestfor 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14 - 38 passed on eachCross-checkpip install 'matplotlib==3.7.5'on Python 3.9 thenpytest- 38 passedRegression proof: base commit 5c9d512 on Python 3.8 / matplotlib 3.7.5,pytest -k fill-test_viewer_3d_fill_is_the_true_worst_case_over_all_view_anglesFAILS (0.9127639, 0.9888995 vs hard-coded 0.9526, 1.0302); same test at f2bf5a4 passesLocal mirror of the workflowdocsjob on Python 3.14:pip install -e '.[docs]'thenmake -C docs html- build succeededmake -C docs -n htmlshowssphinx-build -M html "." "_build" -W; adding a page outside the toctree gives1 warning (with warnings treated as errors)andmake: *** [html] Error 1Parsed.github/workflows/tests.ymlwith PyYAML: triggers push+pull_request,fail-fast: false, matrix 3.8-3.14, only actions/checkout@v4 and actions/setup-python@v5, no cache config,permissions: contents: readCross-checked the matrix againstrequires-python, the Programming Language classifiers and the README "tested on 3.8 through 3.14" claim - all agreeChecked the actions/python-versions manifest: linux builds of 3.8/3.9 exist for ubuntu-24.04 but not 26.04, so the runner pin is required for those matrix entriesIndependence probe of_scene_fill_3d()over 36 combinations of world shape x figure size/dpi x axes rectangle on matplotlib 3.7.5 and 3.11.1 - max deviation 4.44e-16Mutation check: scaling the measured fill by 1.01 and 0.99 in a throwaway copy makestest_viewer_3d_fill_is_the_true_worst_case_over_all_view_angles(and, when too small,test_viewer_3d_box_stays_in_the_window_at_every_view_angle) fail; unmutated they passManual render of the 3d Viewer at the worst-case view angle on matplotlib 3.7.5 (before and after) and 3.11.1 (after), annotated with the reserved scene rectangle and the drawn world box.github/workflows/tests.yml:1- Judgment call, left unresolved on purpose: the repository now has CI (tests on 3.8-3.14 plus a docs build on every push and PR) but no document mentions it. Nothing existing became stale - README and docs/getting_started.rst already claim 3.8-3.14 support and publish exactly the commands the workflow runs - so there was no stale fact to fix. The conventional home would be a status badge in README.md or a CONTRIBUTING.md, but both are additions (one a new documentation surface) rather than staleness repairs, so they were deliberately not made here. Worth a follow-up if the author wants CI status visible.✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.