Skip to content

write_constraints_file can produce constraints that conflict with toplevel requirements.txt pins #918

@wjhrdy

Description

@wjhrdy

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.0pkg-a==2.1 (install dep via pkg-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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions