Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/QA.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ jobs:

- name: Check stubs annotations
run: inv check-stub-annotations

- name: Check build deps are in sync
run: inv check-build-deps-sync
22 changes: 22 additions & 0 deletions .github/workflows/Tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,28 @@ jobs:
- name: Run the tests
run: inv coverage --args "-vvv"

build-floor-versions: # validate that the floor of our build deps ranges still works
runs-on: ubuntu-26.04

steps:
- uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.11"

- name: Install floor versions of build dependencies
run: |
pip install -U pip packaging
pip install $(python3 scripts/floor_versions.py)

- name: Install project against floor versions (no build isolation)
run: pip install -e .[test,scripts] --no-build-isolation

- name: Run the tests
run: inv coverage --args "-vvv"

build-docs: # validate that doc can be built
runs-on: ubuntu-26.04

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Relax `setuptools`/`wheel`/`cython` build dependency pins to `>=floor,<next major>` ranges instead of exact versions, easing downstream packaging; `delocate` stays exactly pinned (#288)

## [4.0.0] - 2026-10-29

### Added
Expand Down
23 changes: 17 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
[build-system]
requires = [
"setuptools == 84.2.1",
"wheel == 0.48.0",
"cython == 3.3.0",
# this list must stay byte-for-byte identical to [project.optional-dependencies].build
# below. PEP 518 requires build-system.requires to be static TOML, parsed before any
# project/build-backend code runs, so it cannot reference the `build` extra (or vice
# versa): the duplication is unavoidable and is checked by
# scripts/check_build_deps_sync.py (run in CI).
#
# floor versions below are read by scripts/floor_versions.py and
# validated by the "build-floor-versions" CI job; when bumping, only move
# the floor if actually required, not on every bump
"setuptools >=84.2.1,<85",
"wheel >=0.48.0,<0.49",
"cython >=3.3.0,<3.4",
# https://github.com/pypa/cibuildwheel/blob/v2.22/cibuildwheel/resources/constraints.txt
"delocate == 0.13.0 ; platform_system=='Windows'",
]
Expand Down Expand Up @@ -69,9 +78,11 @@ test = [
"libzim[build]",
]
build = [
"setuptools == 84.2.1",
"wheel == 0.48.0",
"cython == 3.3.0",
# must stay byte-for-byte identical to [build-system].requires above; see the
# comment there
"setuptools >=84.2.1,<85",
"wheel >=0.48.0,<0.49",
"cython >=3.3.0,<3.4",
"delocate == 0.13.0 ; platform_system=='Windows'",
]
docs = [
Expand Down
33 changes: 33 additions & 0 deletions scripts/check_build_deps_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
"""Check that build-system.requires and the `build` extra stay in sync.

PEP 518 requires build-system.requires to be static TOML, parsed before any
project or build-backend code runs, so it cannot reference
project.optional-dependencies.build (or vice-versa): the two lists must be
duplicated by hand. This script is the drift check for that duplication.
"""

import sys
import tomllib
from pathlib import Path


def main() -> int:
pyproject = tomllib.loads(Path("pyproject.toml").read_text())
build_system_requires = pyproject["build-system"]["requires"]
build_extra = pyproject["project"]["optional-dependencies"]["build"]

if build_system_requires == build_extra:
print("build-system.requires and the `build` extra are in sync") # noqa: T201
return 0

print( # noqa: T201
"error: build-system.requires and the `build` extra have drifted apart"
)
print(f" build-system.requires = {build_system_requires}") # noqa: T201
print(f" optional-dependencies.build = {build_extra}") # noqa: T201
return 1


if __name__ == "__main__":
sys.exit(main())
28 changes: 28 additions & 0 deletions scripts/floor_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""Print `name==version` pins for the floor of build-system.requires in pyproject.toml.

Used by CI to install and test against the lowest versions we claim to
support, without hardcoding those versions a second time in the workflow.
"""

import tomllib
from pathlib import Path

from packaging.requirements import Requirement

pyproject = tomllib.loads(Path("pyproject.toml").read_text())

pins = []
for req in pyproject["build-system"]["requires"]:
requirement = Requirement(req)
if requirement.marker and not requirement.marker.evaluate():
continue
# exact pins (e.g. delocate) have nothing to float, skip them
floor = next(
(spec.version for spec in requirement.specifier if spec.operator == ">="),
None,
)
if floor is not None:
pins.append(f"{requirement.name}=={floor}")

print(" ".join(pins)) # noqa: T201
7 changes: 7 additions & 0 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,19 @@ def check_stub_annotations(ctx: Context):
ctx.run("python -m scripts.check_stub_annotations", pty=use_pty)


@task
def check_build_deps_sync(ctx: Context):
"""check that build-system.requires and the build extra are in sync"""
ctx.run("python -m scripts.check_build_deps_sync", pty=use_pty)


@task(optional=["args"], help={"args": "check tools (pyright) additional arguments"})
def checkall(ctx: Context, args: str = ""):
"""check static types and type stubs"""
check_pyright(ctx, args)
check_stubtest(ctx)
check_stub_annotations(ctx)
check_build_deps_sync(ctx)


@task(optional=["args"], help={"args": "black additional arguments"})
Expand Down
Loading