uv: expose `conflicts` in `tool.uv` in `pyproject.toml`

This commit is contained in:
Andrew Gallant 2024-11-13 14:45:47 -05:00 committed by Andrew Gallant
parent c0440e93cf
commit c68e0d624e
3 changed files with 138 additions and 7 deletions

View File

@ -460,7 +460,7 @@ pub struct ToolUv {
)] )]
pub environments: Option<SupportedEnvironments>, pub environments: Option<SupportedEnvironments>,
/// Conflicting extras may be declared here. /// Conflicting extras or groups may be declared here.
/// ///
/// It's useful to declare conflicting extras when the extras have mutually /// It's useful to declare conflicting extras when the extras have mutually
/// incompatible dependencies. For example, extra `foo` might depend on /// incompatible dependencies. For example, extra `foo` might depend on
@ -481,12 +481,8 @@ pub struct ToolUv {
/// fail. /// fail.
#[cfg_attr( #[cfg_attr(
feature = "schemars", feature = "schemars",
// Skipped for now while we iterate on this feature. schemars(description = "A list sets of conflicting groups or extras.")
schemars(skip, description = "A list sets of conflicting groups or extras.")
)] )]
/*
This is commented out temporarily while we finalize its
functionality and naming. This avoids it showing up in docs.
#[option( #[option(
default = r#"[]"#, default = r#"[]"#,
value_type = "list[list[dict]]", value_type = "list[list[dict]]",
@ -500,9 +496,16 @@ pub struct ToolUv {
{ extra = "test2" }, { extra = "test2" },
] ]
] ]
# Or, to declare conflicting groups:
conflicts = [
[
{ group = "test1" },
{ group = "test2" },
]
]
"# "#
)] )]
*/
pub conflicts: Option<SchemaConflicts>, pub conflicts: Option<SchemaConflicts>,
} }

View File

@ -1,4 +1,55 @@
## Project metadata ## Project metadata
### [`conflicts`](#conflicts) {: #conflicts }
Conflicting extras or groups may be declared here.
It's useful to declare conflicting extras when the extras have mutually
incompatible dependencies. For example, extra `foo` might depend on
`numpy==2.0.0` while extra `bar` might depend on `numpy==2.1.0`. These
extras cannot be activated at the same time. This usually isn't a
problem for pip-style workflows, but when using uv project support
with universal resolution, it will try to produce a resolution that
satisfies both extras simultaneously.
When this happens, resolution will fail, because one cannot install
both `numpy 2.0.0` and `numpy 2.1.0` into the same environment.
To work around this, you may specify `foo` and `bar` as conflicting
extras. When doing universal resolution in project mode, these extras
will get their own "forks" distinct from one another in order to permit
conflicting dependencies. In exchange, if one tries to install from the
lock file with both conflicting extras activated, installation will
fail.
**Default value**: `[]`
**Type**: `list[list[dict]]`
**Example usage**:
```toml title="pyproject.toml"
[tool.uv]
# Require that `package[test1]` and `package[test2]`
# requirements are resolved in different forks so that they
# cannot conflict with one another.
conflicts = [
[
{ extra = "test1" },
{ extra = "test2" },
]
]
# Or, to declare conflicting groups:
conflicts = [
[
{ group = "test1" },
{ group = "test2" },
]
]
```
---
### [`constraint-dependencies`](#constraint-dependencies) {: #constraint-dependencies } ### [`constraint-dependencies`](#constraint-dependencies) {: #constraint-dependencies }
Constraints to apply when resolving the project's dependencies. Constraints to apply when resolving the project's dependencies.

77
uv.schema.json generated
View File

@ -76,6 +76,17 @@
} }
] ]
}, },
"conflicts": {
"description": "A list sets of conflicting groups or extras.",
"anyOf": [
{
"$ref": "#/definitions/SchemaConflicts"
},
{
"type": "null"
}
]
},
"constraint-dependencies": { "constraint-dependencies": {
"description": "PEP 508-style requirements, e.g., `ruff==0.5.0`, or `ruff @ https://...`.", "description": "PEP 508-style requirements, e.g., `ruff==0.5.0`, or `ruff @ https://...`.",
"type": [ "type": [
@ -566,6 +577,35 @@
"$ref": "#/definitions/ConfigSettingValue" "$ref": "#/definitions/ConfigSettingValue"
} }
}, },
"ConflictPackage": {
"description": "The actual conflicting data for a package.\n\nThat is, either an extra or a group name.",
"oneOf": [
{
"type": "object",
"required": [
"Extra"
],
"properties": {
"Extra": {
"$ref": "#/definitions/ExtraName"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"Group"
],
"properties": {
"Group": {
"$ref": "#/definitions/GroupName"
}
},
"additionalProperties": false
}
]
},
"ExcludeNewer": { "ExcludeNewer": {
"description": "Exclude distributions uploaded after the given timestamp.\n\nAccepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same format (e.g., `2006-12-02`).", "description": "Exclude distributions uploaded after the given timestamp.\n\nAccepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and local dates in the same format (e.g., `2006-12-02`).",
"type": "string", "type": "string",
@ -1316,6 +1356,43 @@
} }
] ]
}, },
"SchemaConflictItem": {
"description": "Like [`ConflictItem`], but for deserialization in `pyproject.toml`.\n\nThe schema format is different from the in-memory format. Specifically, the schema format does not allow specifying the package name (or will make it optional in the future), where as the in-memory format needs the package name.",
"type": "object",
"required": [
"conflict"
],
"properties": {
"conflict": {
"$ref": "#/definitions/ConflictPackage"
},
"package": {
"anyOf": [
{
"$ref": "#/definitions/PackageName"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"SchemaConflictSet": {
"description": "Like [`ConflictSet`], but for deserialization in `pyproject.toml`.\n\nThe schema format is different from the in-memory format. Specifically, the schema format does not allow specifying the package name (or will make it optional in the future), where as the in-memory format needs the package name.",
"type": "array",
"items": {
"$ref": "#/definitions/SchemaConflictItem"
}
},
"SchemaConflicts": {
"description": "Like [`Conflicts`], but for deserialization in `pyproject.toml`.\n\nThe schema format is different from the in-memory format. Specifically, the schema format does not allow specifying the package name (or will make it optional in the future), where as the in-memory format needs the package name.\n\nN.B. `Conflicts` is still used for (de)serialization. Specifically, in the lock file, where the package name is required.",
"type": "array",
"items": {
"$ref": "#/definitions/SchemaConflictSet"
}
},
"Source": { "Source": {
"description": "A `tool.uv.sources` value.", "description": "A `tool.uv.sources` value.",
"anyOf": [ "anyOf": [