Open-Source Wikis

/

uv

/

Features

/

Dependency resolution

astral-sh/uv

Dependency resolution

Dependency resolution is the heart of uv. This page traces what happens between "the user ran uv lock" and "we wrote a uv.lock to disk."

The big picture

sequenceDiagram
    participant CLI as uv lock
    participant Settings
    participant Workspace as uv-workspace
    participant Reqs as uv-requirements
    participant Resolver as uv-resolver
    participant DB as uv-distribution
    participant Client as uv-client / cache

    CLI->>Settings: load options + flags
    Settings->>Workspace: discover workspace
    Workspace-->>CLI: PyProjectToml + members
    CLI->>Reqs: build RequirementsSpecification
    Reqs->>Resolver: input requirements + constraints
    Resolver->>DB: VersionsResponse(name)
    DB->>Client: GET /simple/<name>
    Client-->>DB: Simple HTML / PEP 691 JSON
    DB-->>Resolver: candidate versions
    Resolver->>DB: WheelMetadataResult(name, version)
    DB-->>Resolver: ResolutionMetadata
    Resolver->>Resolver: PubGrub fixed point
    Resolver-->>CLI: ResolutionGraph
    CLI->>CLI: serialize uv.lock (uv-resolver::lock)

The interesting parts are inside the resolver. uv's resolver is a fork of PubGrub with substantial extensions for universal resolution.

Universal resolution

A universal lockfile must be valid for every supported environment — every Python version, every platform — without re-resolving. To achieve this without exploding combinatorially, uv forks:

  • When two environments need different versions of a dependency, uv splits the resolution at that decision point.
  • Each fork carries a marker tree describing the environments it applies to.
  • Forks are tracked in uv-resolver/src/resolver/fork_map.rs, with arithmetic in uv-pep508/src/marker/.

The fork-aware lockfile is uv.lock's [[package]] entries. Each package may have multiple entries with disjoint markers.

Conflicts

Some combinations of extras / dependency-groups can't simultaneously hold (e.g., cuda12 and cpu extras). The resolver supports [tool.uv.conflicts] so the user can declare them explicitly, and produces conflict markers in the lockfile so install-time selection picks the right fork.

Performance levers

  • Indexes. Honoring [[tool.uv.index]] priority decides candidate ordering.
  • Pre-release strategy. --prerelease=allow|if-necessary|disallow|... short-circuits large parts of the search.
  • Source strategy. --no-sources skips [tool.uv.sources] overrides.
  • --exclude-newer. Filters candidates by upload time. Implemented via uv-distribution-types::ExcludeNewer*.

Key files for editing

Symptom Code to read first
Unexpected fork uv-resolver/src/resolver/mod.rs, fork_map.rs
Wrong candidate ordering uv-distribution-types/src/prioritized_distribution.rs
Markers behaving oddly uv-pep508/src/marker/
Lockfile diff noise uv-resolver/src/lock/
Build-time metadata wrong uv-distribution/src/source/

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Dependency resolution – uv wiki | Factory