diff --git a/docs/concepts/projects/config.md b/docs/concepts/projects/config.md index f9d33ed90..8efb667a1 100644 --- a/docs/concepts/projects/config.md +++ b/docs/concepts/projects/config.md @@ -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 @@ -401,33 +366,12 @@ 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: - -```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: +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" [tool.uv] @@ -439,25 +383,9 @@ conflicts = [ ] ``` -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: +Or, to declare the development dependency groups `group1` and `group2` incompatible: ```toml title="pyproject.toml" -[dependency-groups] -group1 = ["numpy==2.1.2"] -group2 = ["numpy==2.0.0"] - [tool.uv] conflicts = [ [ @@ -467,4 +395,39 @@ conflicts = [ ] ``` -The only difference with conflicting extras is that you need to use `group` instead of `extra`. +See the [resolution documentation](../resolution.md#conflicting-dependencies) for more. + +## 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. diff --git a/docs/concepts/resolution.md b/docs/concepts/resolution.md index e857e7b1d..278289ea9 100644 --- a/docs/concepts/resolution.md +++ b/docs/concepts/resolution.md @@ -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 @@ -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 @@ -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.