Skip to content

ci: pin the declared dependency floors exactly in the floors job - #139

Open
lesnik512 wants to merge 2 commits into
mainfrom
ci/floors-readback
Open

lesnik512 wants to merge 2 commits into
mainfrom
ci/floors-readback

Conversation

@lesnik512

@lesnik512 lesnik512 commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

The floors job now installs each declared floor exactly, so a floor with no wheel fails the resolve instead of passing on a higher version.

The gap

--only-binary PyYAML doesn't make a floor with no wheel fail. It makes that version ineligible, and --resolution lowest-direct then climbs to the lowest version that has a wheel, then passes. This is how PyYAML>=6 resolved 6.0.1 and 6.0.2 on 3.12 and 3.13 while the job stayed green (#135). uv has no option that turns the climb into an error. Its maintainers treat skipping versions without wheels as intended (astral-sh/uv#16186).

The fix

scripts/floor_constraints.py reads [project.dependencies] and every [project.optional-dependencies] extra from pyproject.toml and prints one exact pin per declared floor. A lower bound of >=, ~= or == becomes ==, and each marker is kept verbatim so uv evaluates it for the interpreter being tested:

PyYAML==6; python_version < "3.12"
PyYAML==6.0.1; python_version == "3.12"
PyYAML==6.0.2; python_version == "3.13"
PyYAML==6.0.3; python_version >= "3.14"

The floors job passes this file as --constraints together with the existing --only-binary PyYAML. A dependency with no lower bound fails the generator, because an exact pin can't be derived for it.

This follows what larger projects do: sunpy and jwst (minimum_dependencies), napari (tox-min-req), scikit-image and scikit-learn all generate exact minimum pins rather than relying on a lowest resolution. None of those I checked also enforces wheel-only.

packaging and tomli join the dev group because the tests import them. tomli rather than tomllib keeps the script importable on 3.10 without a version-gated import. The core package stays zero-dependency.

Verified locally

I ran the CI steps with Linux wheels (--python-platform x86_64-manylinux_2_28):

case result
3.10 to 3.14 as declared installs 6.0, 6.0, 6.0.1, 6.0.2, 6.0.3: each declared floor exactly
3.12 with the floor planted back to PyYAML>=6 No solution found ... pyyaml==6.0 has no usable wheels, exit 1

just lint-ci passes, just test-ci passes 1482 tests at 100% coverage, and the generator's tests pass on 3.10.

Known limits

  • Overlapping markers. Two declarations of one dependency are merged only when their marker text is identical; the higher floor wins. Different markers that overlap for one interpreter, such as a core a>=1 and an extra's a>=2; python_version >= "3.12", would give two contradictory pins. Nothing in this repo does that.
  • uv sync repos. uv sync takes no constraints flag. Repos that install with uv sync --no-install-project would have to move to uv pip install, or set [tool.uv] constraint-dependencies, before they can use this.

@lesnik512 lesnik512 changed the title ci: fail the floors job when a floor resolves above its declaration ci: pin the declared dependency floors exactly in the floors job Sep 25, 2026

This branch has not been deployed

No deployments
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.

1 participant