
To enforce the 100 character line limit in markdown files introduced in https://github.com/astral-sh/uv/pull/5635, and to automate the formatting of markdown files, i've added prettier and formatted our markdown files with it. I've excluded the changelog and the generated references documentation from this for having too many changes, but we can also include them. I'm not particular on which style we use. My main motivations are (major) not having to reflow markdown files myself anymore and (minor) consistence between all markdown files. I've chosen prettier for similar reason as we chose black, it's a single good style that's automated and shared in the community. I do prefer prettier's style of not breaking inside of a link name though. This PR is in two parts, the first adds prettier to CI and documents using it, while the second actually formats the docs. When merge conflicts arise, we can drop the last commit and regenerate it with `npx prettier --prose-wrap always --write BENCHMARKS.md CONTRIBUTING.md README.md STYLE.md docs/*.md docs/concepts/**/*.md docs/guides/**/*.md docs/pip/**/*.md`. --------- Co-authored-by: Zanie Blue <contact@zanie.dev>
7.3 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.
uv also supports a machine agnostic resolution. uv supports writing multiplatform resolutions in
both a requirements.txt
format and uv-specific (uv.lock
) format.
If using uv's pip compile
, the --universal
flag will generate a resolution that is compatible
with all operating systems, architectures, and Python implementations. In universal mode, the
current Python version (or provided --python-version
) will be treated as a lower bound. For
example, --universal --python-version 3.7
would produce a universal resolution for Python 3.7 and
later.
If using uv's project interface, the machine agnostic resolution will be
used automatically and a uv.lock
file will be created. The lockfile can also be created with an
explicit uv lock
invocation.
uv also supports resolving for specific 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.
!!! note
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.