Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 41 additions & 78 deletions docs/concepts/projects/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,41 +196,6 @@ To target this environment, you'd export `UV_PROJECT_ENVIRONMENT=/usr/local`.
environment. The `--active` flag can be used to opt-in to respecting `VIRTUAL_ENV`. The
`--no-active` flag can be used to silence the warning.

## Limited resolution environments

If your project supports a more limited set of platforms or Python versions, you can constrain the
set of solved platforms via the `environments` setting, which accepts a list of PEP 508 environment
markers. For example, to constrain the lockfile to macOS and Linux, and exclude Windows:

```toml title="pyproject.toml"
[tool.uv]
environments = [
"sys_platform == 'darwin'",
"sys_platform == 'linux'",
]
```

See the [resolution documentation](../resolution.md#limited-resolution-environments) for more.

## Required environments

If your project _must_ support a specific platform or Python version, you can mark that platform as
required via the `required-environments` setting. For example, to require that the project supports
Intel macOS:

```toml title="pyproject.toml"
[tool.uv]
required-environments = [
"sys_platform == 'darwin' and platform_machine == 'x86_64'",
]
```

The `required-environments` setting is only relevant for packages that do not publish a source
distribution (like PyTorch), as such packages can _only_ be installed on environments covered by the
set of pre-built binary distributions (wheels) published by that package.

See the [resolution documentation](../resolution.md#required-environments) for more.

## Build isolation

By default, uv builds all packages in isolated virtual environments, as per
Expand Down Expand Up @@ -401,70 +366,68 @@ in the deployed environment without a dependency on the originating source code.

## Conflicting dependencies

uv requires that all optional dependencies ("extras") declared by the project are compatible with
each other and resolves all optional dependencies together when creating the lockfile.
uv requires resolves all project dependencies together, including optional dependencies ("extras")
and dependency groups. If dependencies declared in one section are not compatible with those in
another section, uv will fail to resolve the requirements of the project with an error.

If optional dependencies declared in one extra are not compatible with those in another extra, uv
will fail to resolve the requirements of the project with an error.

To work around this, uv supports declaring conflicting extras. For example, consider two sets of
optional dependencies that conflict with one another:
uv supports explicit declaration of conflicting dependency groups. For example, to declare that the
`optional-dependency` groups `extra1` and `extra2` are incompatible:

```toml title="pyproject.toml"
[project.optional-dependencies]
extra1 = ["numpy==2.1.2"]
extra2 = ["numpy==2.0.0"]
```

If you run `uv lock` with the above dependencies, resolution will fail:

```console
$ uv lock
x No solution found when resolving dependencies:
`-> Because myproject[extra2] depends on numpy==2.0.0 and myproject[extra1] depends on numpy==2.1.2, we can conclude that myproject[extra1] and
myproject[extra2] are incompatible.
And because your project requires myproject[extra1] and myproject[extra2], we can conclude that your projects's requirements are unsatisfiable.
[tool.uv]
conflicts = [
[
{ extra = "extra1" },
{ extra = "extra2" },
],
]
```

But if you specify that `extra1` and `extra2` are conflicting, uv will resolve them separately.
Specify conflicts in the `tool.uv` section:
Or, to declare the development dependency groups `group1` and `group2` incompatible:

```toml title="pyproject.toml"
[tool.uv]
conflicts = [
[
{ extra = "extra1" },
{ extra = "extra2" },
{ group = "group1" },
{ group = "group2" },
],
]
```

Now, running `uv lock` will succeed. Note though, that now you cannot install both `extra1` and
`extra2` at the same time:
See the [resolution documentation](../resolution.md#conflicting-dependencies) for more.

```console
$ uv sync --extra extra1 --extra extra2
Resolved 3 packages in 14ms
error: extra `extra1`, extra `extra2` are incompatible with the declared conflicts: {`myproject[extra1]`, `myproject[extra2]`}
## Limited resolution environments

If your project supports a more limited set of platforms or Python versions, you can constrain the
set of solved platforms via the `environments` setting, which accepts a list of PEP 508 environment
markers. For example, to constrain the lockfile to macOS and Linux, and exclude Windows:

```toml title="pyproject.toml"
[tool.uv]
environments = [
"sys_platform == 'darwin'",
"sys_platform == 'linux'",
]
```

This error occurs because installing both `extra1` and `extra2` would result in installing two
different versions of a package into the same environment.
See the [resolution documentation](../resolution.md#limited-resolution-environments) for more.

The above strategy for dealing with conflicting extras also works with dependency groups:
## Required environments

```toml title="pyproject.toml"
[dependency-groups]
group1 = ["numpy==2.1.2"]
group2 = ["numpy==2.0.0"]
If your project _must_ support a specific platform or Python version, you can mark that platform as
required via the `required-environments` setting. For example, to require that the project supports
Intel macOS:

```toml title="pyproject.toml"
[tool.uv]
conflicts = [
[
{ group = "group1" },
{ group = "group2" },
],
required-environments = [
"sys_platform == 'darwin' and platform_machine == 'x86_64'",
]
```

The only difference with conflicting extras is that you need to use `group` instead of `extra`.
The `required-environments` setting is only relevant for packages that do not publish a source
distribution (like PyTorch), as such packages can _only_ be installed on environments covered by the
set of pre-built binary distributions (wheels) published by that package.

See the [resolution documentation](../resolution.md#required-environments) for more.
81 changes: 76 additions & 5 deletions docs/concepts/resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,77 @@ though only `name`, `version`, `requires-dist`, `requires-python`, and `provides
uv. The `version` field is also considered optional. If omitted, the metadata will be used for all
versions of the specified package.

## Conflicting dependencies

uv requires that all optional dependencies ("extras") declared by the project are compatible with
each other and resolves all optional dependencies together when creating the lockfile.

If optional dependencies declared in one extra are not compatible with those in another extra, uv
will fail to resolve the requirements of the project with an error.

To work around this, uv supports declaring conflicting extras. For example, consider two sets of
optional dependencies that conflict with one another:

```toml title="pyproject.toml"
[project.optional-dependencies]
extra1 = ["numpy==2.1.2"]
extra2 = ["numpy==2.0.0"]
```

If you run `uv lock` with the above dependencies, resolution will fail:

```console
$ uv lock
x No solution found when resolving dependencies:
`-> Because myproject[extra2] depends on numpy==2.0.0 and myproject[extra1] depends on numpy==2.1.2, we can conclude that myproject[extra1] and
myproject[extra2] are incompatible.
And because your project requires myproject[extra1] and myproject[extra2], we can conclude that your projects's requirements are unsatisfiable.
```

But if you specify that `extra1` and `extra2` are conflicting, uv will resolve them separately.
Specify conflicts in the `tool.uv` section:

```toml title="pyproject.toml"
[tool.uv]
conflicts = [
[
{ extra = "extra1" },
{ extra = "extra2" },
],
]
```

Now, running `uv lock` will succeed. Note though, that now you cannot install both `extra1` and
`extra2` at the same time:

```console
$ uv sync --extra extra1 --extra extra2
Resolved 3 packages in 14ms
error: extra `extra1`, extra `extra2` are incompatible with the declared conflicts: {`myproject[extra1]`, `myproject[extra2]`}
```

This error occurs because installing both `extra1` and `extra2` would result in installing two
different versions of a package into the same environment.

The above strategy for dealing with conflicting extras also works with dependency groups:

```toml title="pyproject.toml"
[dependency-groups]
group1 = ["numpy==2.1.2"]
group2 = ["numpy==2.0.0"]

[tool.uv]
conflicts = [
[
{ group = "group1" },
{ group = "group2" },
],
]
```

The only difference from conflicting extras is that you need to use the `group` key instead of
`extra`.

## Lower bounds

By default, `uv add` adds lower bounds to dependencies and, when using uv to manage projects, uv
Expand Down Expand Up @@ -513,11 +584,6 @@ reading and extracting archives in the following formats:
- lzma tarball (`.tar.lzma`)
- zip (`.zip`)

## Learn more

For more details about the internals of the resolver, see the
[resolver reference](../reference/resolver-internals.md) documentation.

## Lockfile versioning

The `uv.lock` file uses a versioned schema. The schema version is included in the `version` field of
Expand All @@ -539,3 +605,8 @@ other words, lockfiles may only be rejected across minor releases.
The `revision` field of the lockfile is used to track backwards compatible changes to the lockfile.
For example, adding a new field to distributions. Changes to the revision will not cause older
versions of uv to error.

## Learn more

For more details about the internals of the resolver, see the
[resolver reference](../reference/resolver-internals.md) documentation.
Loading