
## Summary We want to have consistency between the Ruff and uv documentation for the upcoming release. We don't love the Ruff docs, but we'd rather have consistency and then work towards improving them both, rather than have two very-different documentation sites that both have weaknesses. The setup here is simpler than in Ruff as: (1) we don't yet generate any docs from Rust and (2) we don't try to reuse the README in the uv documentation (which adds a lot of complexity in Ruff). So the change here is mostly a 1-to-1 port to MkDocs. ## Test Plan 
6.5 KiB
Resolution
Resolution strategy
By default, uv follows the standard Python dependency resolution strategy of preferring the
latest compatible version of each package. For example, uv pip install flask>=2.0.0
will
install the latest version of Flask (at time of writing: 3.0.0
).
However, uv's resolution strategy can be configured to support alternative workflows. With
--resolution=lowest
, uv will install the lowest compatible versions for all dependencies,
both direct and transitive. Alternatively, --resolution=lowest-direct
will opt for the
lowest compatible versions for all direct dependencies, while using the latest
compatible versions for all transitive dependencies. This distinction can be particularly useful
for library authors who wish to test against the lowest supported versions of direct dependencies
without restricting the versions of transitive dependencies.
For example, given the following requirements.in
file:
flask>=2.0.0
Running uv pip compile requirements.in
would produce the following requirements.txt
file:
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in
blinker==1.7.0
# via flask
click==8.1.7
# via flask
flask==3.0.0
itsdangerous==2.1.2
# via flask
jinja2==3.1.2
# via flask
markupsafe==2.1.3
# via
# jinja2
# werkzeug
werkzeug==3.0.1
# via flask
However, uv pip compile --resolution=lowest requirements.in
would instead produce:
# This file was autogenerated by uv via the following command:
# uv pip compile requirements.in --resolution=lowest
click==7.1.2
# via flask
flask==2.0.0
itsdangerous==2.0.0
# via flask
jinja2==3.0.0
# via flask
markupsafe==2.0.0
# via jinja2
werkzeug==2.0.0
# via flask
Pre-release handling
By default, uv will accept pre-release versions during dependency resolution in two cases:
- If the package is a direct dependency, and its version markers include a pre-release specifier
(e.g.,
flask>=2.0.0rc1
). - If all published versions of a package are pre-releases.
If dependency resolution fails due to a transitive pre-release, uv will prompt the user to
re-run with --prerelease=allow
, to allow pre-releases for all dependencies.
Alternatively, you can add the transitive dependency to your requirements.in
file with a
pre-release specifier (e.g., flask>=2.0.0rc1
) to opt in to pre-release support for that specific
dependency.
Pre-releases are notoriously difficult to model, and are a frequent source of bugs in other packaging tools. uv's pre-release handling is intentionally limited and intentionally requires user opt-in for pre-releases, to ensure correctness.
For more, see "Pre-release compatibility"
Dependency overrides
Historically, pip
has supported "constraints" (-c constraints.txt
), which allows users to
narrow the set of acceptable versions for a given package.
uv supports constraints, but also takes this concept further by allowing users to override the
acceptable versions of a package across the dependency tree via overrides (--override overrides.txt
).
In short, overrides allow the user to lie to the resolver by overriding the declared dependencies of a package. Overrides are a useful last resort for cases in which the user knows that a dependency is compatible with a newer version of a package than the package declares, but the package has not yet been updated to declare that compatibility.
For example, if a transitive dependency declares pydantic>=1.0,<2.0
, but the user knows that
the package is compatible with pydantic>=2.0
, the user can override the declared dependency
with pydantic>=2.0,<3
to allow the resolver to continue.
While constraints are purely additive, and thus cannot expand the set of acceptable versions for a package, overrides can expand the set of acceptable versions for a package, providing an escape hatch for erroneous upper version bounds.
Multi-platform resolution
By default, uv's pip-compile
command produces a resolution that's known to be compatible with
the current platform and Python version. Unlike Poetry and PDM, uv does not yet produce a
machine-agnostic lockfile (#2679).
However, uv does support resolving for alternate platforms and Python versions via the
--python-platform
and --python-version
command line arguments.
For example, if you're running uv on macOS, but want to resolve for Linux, you can run
uv pip compile --python-platform=linux requirements.in
to produce a manylinux2014
-compatible
resolution.
Similarly, if you're running uv on Python 3.9, but want to resolve for Python 3.8, you can run
uv pip compile --python-version=3.8 requirements.in
to produce a Python 3.8-compatible resolution.
The --python-platform
and --python-version
arguments can be combined to produce a resolution for
a specific platform and Python version, enabling users to generate multiple lockfiles for
different environments from a single machine.
N.B. Python's environment markers expose far more information about the current machine
than can be expressed by a simple --python-platform
argument. For example, the platform_version
marker
on macOS includes the time at which the kernel was built, which can (in theory) be encoded in
package requirements. uv's resolver makes a best-effort attempt to generate a resolution that is
compatible with any machine running on the target --python-platform
, which should be sufficient for
most use cases, but may lose fidelity for complex package and platform combinations.
Time-restricted reproducible resolutions
uv supports an --exclude-newer
option to limit resolution to distributions published before a specific
date, allowing reproduction of installations regardless of new package releases. The date may be specified
as an RFC 3339 timestamp (e.g., 2006-12-02T02:07:43Z
) or
UTC date in the same format (e.g., 2006-12-02
).
Note the package index must support the upload-time
field as specified in PEP 700
.
If the field is not present for a given distribution, the distribution will be treated as unavailable.
To ensure reproducibility, messages for unsatisfiable resolutions will not mention that distributions were excluded
due to the --exclude-newer
flag — newer distributions will be treated as if they do not exist.