-
Notifications
You must be signed in to change notification settings - Fork 44
Description
Summary
When the dependency graph contains two versions of the same package — one from a toplevel exact pin and one from a transitive install dependency with a loose specifier — write_constraints_file selects the highest version that satisfies all transitive constraints, even when that differs from the toplevel pin. This produces a constraints.txt that contradicts the toplevel requirements.txt, causing pip to reject the install:
pip install -r requirements.txt -c constraints.txt
# ERROR: conflicting dependencies
This is a practical correctness issue: pip's documented behavior since 20.3 is that "constraints don't override the existing requirements; they simply constrain what versions are visible as input to the resolver." When a constraint conflicts with a requirement, pip refuses to install (pip docs, pip issue #6495). So the output of write_constraints_file must be compatible with the original toplevel pins.
Reproducer
Failing test at https://github.com/wjhrdy/fromager/blob/toplevel-constraint-bug/tests/test_toplevel_local_version.py
Graph structure:
- ROOT →
pkg-a==2.0(toplevel, exact pin) - ROOT →
pkg-b==1.0(toplevel) pkg-b==1.0→pkg-a==2.1(install dep viapkg-a>=1.0)
Both pkg-a==2.0 and pkg-a==2.1 exist in the graph. pkg-a==2.0 satisfies the transitive >=1.0 constraint.
Expected constraint: pkg-a==2.0 (the toplevel pin, which satisfies all consumers)
Actual constraint: pkg-a==2.1 (the highest version, overriding the toplevel pin)
$ pytest tests/test_toplevel_local_version.py -v -s
# NOTE: fromager selected pkg-a==2.1 from: ['2.0', '2.1']
pkg-a==2.1
If requirements.txt has pkg-a==2.0 and constraints.txt has pkg-a==2.1, pip rejects the install.
Mechanism
In write_constraints_file (bootstrap.py), the multi-version resolution iterates reversed(sorted(usable_versions.items())) — highest version first — and picks the first version that satisfies all consumers. This "pick highest" strategy works well when there's no toplevel pin, but when the user has an exact toplevel pin that satisfies all transitive constraints, the constraint writer should respect it rather than override it with a different version.
Real-World Impact
This was discovered in the RHAIIS (Red Hat AI Inference Server) downstream build. The toplevel requirement vllm==0.13.0+rhai10 was pinned in requirements.txt, but a transitive dependency (vllm>=0.10.0) was independently resolved to vllm==0.13.0+rhai9. The constraint writer selected rhai9 (which PEP 440 considers higher due to lexicographic local version comparison), and the container build failed:
ERROR: Cannot install vllm==0.13.0+rhai10 because these package versions have conflicting dependencies.
The user requested vllm==0.13.0+rhai10
The user requested (constraint) vllm==0.13.0+rhai9
Note on test_write_constraints_file_multiples
The existing test test_write_constraints_file_multiples in test_bootstrap.py asserts the current behavior: toplevel b==0.26.1 is overridden by transitive b==0.26.2. This test may need to be updated if the behavior is changed, or it may reflect an intentionally different design choice — in which case the pip compatibility concern should be documented.