From 47c0fad378d6fc34cff3b19f2043fe96b48537ad Mon Sep 17 00:00:00 2001 From: Stephen Rosen Date: Wed, 1 Jul 2026 11:22:41 -0500 Subject: [PATCH] Add a check which tests test-mindeps bounds Load `test-mindeps` and `dependencies` out of pyproject data, and compare them to ensure that the specifiers in `test-mindeps` always contain the minimum version listed in `dependencies`. This is a new tox env and runs in CI. It should force bad test-mindeps updates from dependabot to fail. --- .github/workflows/test.yaml | 1 + scripts/ensure_mindeps_match_lower_bounds.py | 49 ++++++++++++++++++++ tox.ini | 8 ++++ 3 files changed, 58 insertions(+) create mode 100644 scripts/ensure_mindeps_match_lower_bounds.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b3da7a077..d91fb5a19 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -62,6 +62,7 @@ jobs: - "mypy-maxpython" - "reference" - "check-min-python-is-tested" + - "check-mindeps-lower-bounds" - "twine-check" - "twine-check-minimum" - "check-sdist" diff --git a/scripts/ensure_mindeps_match_lower_bounds.py b/scripts/ensure_mindeps_match_lower_bounds.py new file mode 100644 index 000000000..ac6804f97 --- /dev/null +++ b/scripts/ensure_mindeps_match_lower_bounds.py @@ -0,0 +1,49 @@ +# run via `tox r -e check-mindeps-lower-bounds` +import pathlib +import re +import sys +import tomllib + +import mddj.api +from packaging.dependency_groups import DependencyGroupResolver +from packaging.requirements import Requirement + +dj = mddj.api.DJ() +REPO_ROOT = pathlib.Path(__file__).parent.parent + +# load 'test-mindeps', filtered only to the directly declared requirements, not the +# includes +with open(REPO_ROOT / "pyproject.toml", "rb") as f: + pyproject = tomllib.load(f) + +group_resolver = DependencyGroupResolver(pyproject["dependency-groups"]) +mindeps = [ + r for r in group_resolver.lookup("test-mindeps") if isinstance(r, Requirement) +] + +dependencies: tuple[str, ...] = dj.read.dependencies() +dependency_lower_bounds = {} +for dep in dependencies: + req = Requirement(dep) + + # extract lower bounds via a regex + # this pattern is imperfect but is good enough for realistic versions + # it won't work on complex specifiers with multiple `>=` expressions, for example + match = re.search(r">=([0-9\.a-z]+)", str(req.specifier)) + if match is None: + continue + + dependency_lower_bounds[req.name] = match.group(1) + +for req in mindeps: + if req.name not in dependency_lower_bounds: + print(f"warning: {req} is in 'test-mindeps' but is not in dependencies") + continue + + true_lower_bound = dependency_lower_bounds[req.name] + if not req.specifier.contains(true_lower_bound): + print( + f"ERROR: 'test-mindeps' lists {req}, which does not contain " + f"the true lower bound, {true_lower_bound!r}" + ) + sys.exit(1) diff --git a/tox.ini b/tox.ini index 5bae52da4..923c8da3f 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,7 @@ [tox] envlist = check-min-python-is-tested + check-mindeps-lower-bounds twine-check{,-minimum} check-sdist{,-minimum} reference @@ -148,6 +149,13 @@ deps = description = Check the Requires-Python metadata against CI config commands = python scripts/ensure_min_python_is_tested.py +[testenv:check-mindeps-lower-bounds] +base = check_metadata_base +# no need for a build backend for this +deps = +description = Check 'test-mindeps' against 'dependencies' +commands = python scripts/ensure_mindeps_match_lower_bounds.py + [testenv:twine-check{,-minimum}] base = check_metadata_base description =