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 inuv-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-sourcesskips[tool.uv.sources]overrides. --exclude-newer. Filters candidates by upload time. Implemented viauv-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
- crates/uv-resolver — the deep dive.
- crates/uv-distribution — the metadata fetcher.
- crates/uv-pep508 — marker arithmetic.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.