Visitar URL original
feat: constraints mode for testing the oldest supported versions in CI by omar-radwan7 · Pull Request #46 · scientific-python/spec0-action · GitHub
Skip to content

feat: constraints mode for testing the oldest supported versions in CI - #46

Closed
omar-radwan7 wants to merge 1 commit into
scientific-python:mainfrom
omar-radwan7:constraints-mode
Closed

omar-radwan7 wants to merge 1 commit into
scientific-python:mainfrom
omar-radwan7:constraints-mode

Conversation

@omar-radwan7

Copy link
Copy Markdown
Contributor

Constraints mode for testing the oldest supported versions in CI

The action can already raise the lower bounds in pyproject.toml. This adds a second mode that helps projects check those lower bounds: mode: constraints writes a constraints file with exact SPEC 0 minimum versions, so a CI job can install and test the oldest supported combination. It does not touch the project file or open a PR.

Motivation: shap wants to replace a hand-maintained numpy==2.0 pin in its CI with floors taken from the SPEC 0 schedule (shap/shap#5263). That issue suggests extending this action instead of writing a separate script, so other projects can use it too. Made during the GC.OS hackathon in Ulm.

- uses: scientific-python/spec0-action@<commit>
  id: spec0
  with:
    mode: constraints
    packages: numpy scipy pandas scikit-learn matplotlib python
- uses: actions/setup-python@v6
  with:
    python-version: ${{ steps.spec0.outputs.python_version }}
- run: pip install -c constraints-spec0.txt . && pytest

Changes

  • spec0_action/constraints.py: computes the floors on a reference date by applying all schedule entries up to that date in order (each entry only lists the packages that change), and formats them as exact pins. With >= the installer would pick the newest release, so the minimum would never be tested. Python is left out of the file and reported as an output instead.
  • run_spec0_constraints.py: CLI with --packages, --output and --reference-date. Writes constraints_file, versions (JSON) and python_version to $GITHUB_OUTPUT.
  • action.yaml: new inputs mode, packages, constraints_file, reference_date and the three outputs. In constraints mode the checkout, git setup, update and PR steps are skipped. update stays the default, so existing users are not affected.
  • pyproject.toml: write-constraints pixi task.
  • Readme section and a new job in test_action.yaml that runs the action in constraints mode and checks the file and outputs.

The run fails with a message naming the packages if the schedule has no floor for them on the reference date, and on invalid schedule data, instead of falling back to the newest version.

Testing

  • 19 new tests in tests/test_constraints.py (floors across entries, start date boundaries, unsorted schedule, missing floors, invalid schedules, CLI outputs). Full suite: 101 passed.
  • pre-commit passes.
  • Checked with the current schedule: numpy 2.3.0, scipy 1.15.0, pandas 2.3.0, scikit-learn 1.7.0, matplotlib 3.11.0, ipython 8.32.0, Python 3.13. I then installed shap with that constraints file on Python 3.13 and ran its test suite: 386 passed, 0 failed.
  • Ran the action for real in a shap CI job on my fork: it generated the pins, set up Python 3.13 from the python_version output, installed shap at those versions and the tests passed (387 passed, 0 failed): https://github.com/omar-radwan7/shap/actions/runs/41460204617/job/125326992471

AI disclosure

Written with Claude Code. I chose the task and the approach with the shap maintainers at the hackathon. Claude wrote the code and tests and ran them. I reviewed the changes before opening this PR.

Generated with Claude Code

https://claude.ai/code/session_01Q7ywk9uaZ3gLUcBP7UysV3

Add mode: constraints, which writes a constraints file with exact SPEC 0
minimum version pins instead of updating the project file and opening a
PR. Floors are computed from all schedule entries up to a reference date.
Missing floors and invalid schedule data fail with a clear message.
Outputs: constraints_file, versions (JSON) and python_version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7ywk9uaZ3gLUcBP7UysV3
@nabobalis

nabobalis commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Thank you for the PR @omar-radwan7.

I guess I am confused. You want to just run the floor version of dependencies on the CI?

@omar-radwan7

omar-radwan7 commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Yes. The action already raises the minimum versions, but nothing tests that the project still works with them. This mode writes the exact minimum versions so a CI job can install and test them. shap does this by hand today with numpy==2.0 (shap/shap#5263). Example run: https://github.com/omar-radwan7/shap/actions/runs/41460204617/job/125326992471

@nabobalis

nabobalis commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

I guess my answer is that you should use existing tooling either:

  • uv pip install --resolution lowest-direct
  • pip-compile
  • minimum-dependencies

The last one is what I use myself to check this.

Would these not be a better fit than adding a new method to this action? Or do I misunderstand something?

@omar-radwan7

Copy link
Copy Markdown
Contributor Author

You're right, thanks. I tried uv pip install --resolution lowest-direct on shap and it fails today because shap does not declare lower bounds for scipy, pandas and scikit-learn (it tries to build scipy 0.8.0). So the real fix is on the shap side: declare the floors, which the update mode of this action already does, and then test them with lowest-direct. No new mode needed here.

Would a short readme section on testing the floors in CI with --resolution lowest-direct after an update be useful? Otherwise I'm happy to close this.

@omar-radwan7

Copy link
Copy Markdown
Contributor Author

Thanks for the advice. I followed it: shap now uses create_pr: false plus uv pip install --resolution lowest-direct (shap/shap#5290), and #47 adds a short readme section for that. Closing this one.

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