mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00

A ton of work remaining here, pushing so I can preview things rendered. Here's the [latest rendered documentation](https://astral-sh.github.io/uv/).
144 lines
6.5 KiB
Markdown
144 lines
6.5 KiB
Markdown
# 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:
|
|
|
|
```text
|
|
flask>=2.0.0
|
|
```
|
|
|
|
Running `uv pip compile requirements.in` would produce the following `requirements.txt` file:
|
|
|
|
```text
|
|
# 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:
|
|
|
|
```text
|
|
# 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:
|
|
|
|
1. If the package is a direct dependency, and its version markers include a pre-release specifier
|
|
(e.g., `flask>=2.0.0rc1`).
|
|
1. 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](https://pubgrub-rs-guide.netlify.app/limitations/prerelease_versions)
|
|
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"](./PIP_COMPATIBILITY.md#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](https://github.com/astral-sh/uv/issues/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](https://www.rfc-editor.org/rfc/rfc3339.html) 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`](https://peps.python.org/pep-0700/).
|
|
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.
|