uv/docs/resolution.md
Zanie Blue 1ee201da5a
Add structured documentation (#4426)
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/).
2024-06-26 11:28:42 -05:00

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.