mirror of https://github.com/astral-sh/uv
Add support for reading PEP 735 dependency groups (#8104)
Part of #8090 As a basic first step, we parse these groups defined in `pyproject.toml` files.
This commit is contained in:
parent
d2cd09bbd7
commit
3c9d783e09
|
|
@ -44,6 +44,8 @@ pub struct PyProjectToml {
|
|||
pub project: Option<Project>,
|
||||
/// Tool-specific metadata.
|
||||
pub tool: Option<Tool>,
|
||||
/// Non-project dependency groups, as defined in PEP 735.
|
||||
pub dependency_groups: Option<BTreeMap<ExtraName, Vec<String>>>,
|
||||
/// The raw unserialized document.
|
||||
#[serde(skip)]
|
||||
pub raw: String,
|
||||
|
|
@ -1141,6 +1143,8 @@ pub enum DependencyType {
|
|||
Dev,
|
||||
/// A dependency in `project.optional-dependencies.{0}`.
|
||||
Optional(ExtraName),
|
||||
/// A dependency in `dependency-groups.{0}`.
|
||||
Group(ExtraName),
|
||||
}
|
||||
|
||||
/// <https://github.com/serde-rs/serde/issues/1316#issue-332908452>
|
||||
|
|
|
|||
|
|
@ -712,6 +712,22 @@ impl PyProjectTomlMut {
|
|||
}
|
||||
}
|
||||
|
||||
// Check `dependency-groups`.
|
||||
if let Some(groups) = self.doc.get("dependency-groups").and_then(Item::as_table) {
|
||||
for (group, dependencies) in groups {
|
||||
let Some(dependencies) = dependencies.as_array() else {
|
||||
continue;
|
||||
};
|
||||
let Ok(group) = ExtraName::new(group.to_string()) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !find_dependencies(name, marker, dependencies).is_empty() {
|
||||
types.push(DependencyType::Group(group));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check `tool.uv.dev-dependencies`.
|
||||
if let Some(dev_dependencies) = self
|
||||
.doc
|
||||
|
|
@ -719,7 +735,7 @@ impl PyProjectTomlMut {
|
|||
.and_then(Item::as_table)
|
||||
.and_then(|tool| tool.get("uv"))
|
||||
.and_then(Item::as_table)
|
||||
.and_then(|tool| tool.get("dev-dependencies"))
|
||||
.and_then(|uv| uv.get("dev-dependencies"))
|
||||
.and_then(Item::as_array)
|
||||
{
|
||||
if !find_dependencies(name, marker, dev_dependencies).is_empty() {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
use std::env;
|
||||
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use assert_fs::fixture::ChildPath;
|
||||
use assert_fs::prelude::*;
|
||||
use insta::assert_json_snapshot;
|
||||
|
||||
use uv_pep508::ExtraName;
|
||||
|
||||
use crate::pyproject::PyProjectToml;
|
||||
use crate::workspace::{DiscoveryOptions, ProjectWorkspace};
|
||||
|
||||
async fn workspace_test(folder: &str) -> (ProjectWorkspace, String) {
|
||||
|
|
@ -76,7 +79,8 @@ async fn albatross_in_example() {
|
|||
],
|
||||
"optional-dependencies": null
|
||||
},
|
||||
"tool": null
|
||||
"tool": null,
|
||||
"dependency-groups": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -128,7 +132,8 @@ async fn albatross_project_in_excluded() {
|
|||
],
|
||||
"optional-dependencies": null
|
||||
},
|
||||
"tool": null
|
||||
"tool": null,
|
||||
"dependency-groups": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -237,7 +242,8 @@ async fn albatross_root_workspace() {
|
|||
"override-dependencies": null,
|
||||
"constraint-dependencies": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependency-groups": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -326,7 +332,8 @@ async fn albatross_virtual_workspace() {
|
|||
"override-dependencies": null,
|
||||
"constraint-dependencies": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependency-groups": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -377,7 +384,8 @@ async fn albatross_just_project() {
|
|||
],
|
||||
"optional-dependencies": null
|
||||
},
|
||||
"tool": null
|
||||
"tool": null,
|
||||
"dependency-groups": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -528,7 +536,8 @@ async fn exclude_package() -> Result<()> {
|
|||
"override-dependencies": null,
|
||||
"constraint-dependencies": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependency-groups": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -629,7 +638,8 @@ async fn exclude_package() -> Result<()> {
|
|||
"override-dependencies": null,
|
||||
"constraint-dependencies": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependency-groups": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -743,7 +753,8 @@ async fn exclude_package() -> Result<()> {
|
|||
"override-dependencies": null,
|
||||
"constraint-dependencies": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependency-groups": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -831,7 +842,8 @@ async fn exclude_package() -> Result<()> {
|
|||
"override-dependencies": null,
|
||||
"constraint-dependencies": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependency-groups": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -840,3 +852,21 @@ async fn exclude_package() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_dependency_groups() {
|
||||
let toml = r#"
|
||||
[dependency-groups]
|
||||
test = ["a"]
|
||||
"#;
|
||||
|
||||
let result =
|
||||
PyProjectToml::from_string(toml.to_string()).expect("Deserialization should succeed");
|
||||
let groups = result
|
||||
.dependency_groups
|
||||
.expect("`dependency-groups` should be present");
|
||||
let test = groups
|
||||
.get(&ExtraName::from_str("test").unwrap())
|
||||
.expect("Group `test` should be present");
|
||||
assert_eq!(test, &["a".to_string()]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@ pub(crate) async fn add(
|
|||
bail!("Project is missing a `[project]` table; add a `[project]` table to use optional dependencies, or run `{}` instead", "uv add --dev".green())
|
||||
}
|
||||
DependencyType::Dev => (),
|
||||
DependencyType::Group(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -469,6 +470,7 @@ pub(crate) async fn add(
|
|||
DependencyType::Optional(ref group) => {
|
||||
toml.add_optional_dependency(group, &requirement, source.as_ref())?
|
||||
}
|
||||
DependencyType::Group(_) => todo!("adding dependencies to groups is not yet supported"),
|
||||
};
|
||||
|
||||
// If the edit was inserted before the end of the list, update the existing edits.
|
||||
|
|
@ -743,6 +745,9 @@ async fn lock_and_sync(
|
|||
DependencyType::Optional(ref group) => {
|
||||
toml.set_optional_dependency_minimum_version(group, *index, minimum)?;
|
||||
}
|
||||
DependencyType::Group(_) => {
|
||||
todo!("adding dependencies to groups is not yet supported")
|
||||
}
|
||||
}
|
||||
|
||||
modified = true;
|
||||
|
|
@ -817,6 +822,7 @@ async fn lock_and_sync(
|
|||
let dev = DevMode::Exclude;
|
||||
(extras, dev)
|
||||
}
|
||||
DependencyType::Group(_) => todo!("adding dependencies to groups is not yet supported"),
|
||||
};
|
||||
|
||||
project::sync::do_sync(
|
||||
|
|
|
|||
|
|
@ -122,6 +122,9 @@ pub(crate) async fn remove(
|
|||
);
|
||||
}
|
||||
}
|
||||
DependencyType::Group(_) => {
|
||||
todo!("removing dependencies from groups is not yet supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,6 +253,9 @@ fn warn_if_present(name: &PackageName, pyproject: &PyProjectTomlMut) {
|
|||
"`{name}` is an optional dependency; try calling `uv remove --optional {group}`",
|
||||
);
|
||||
}
|
||||
DependencyType::Group(_) => {
|
||||
// TODO(zanieb): Once we support `remove --group`, add a warning here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -849,8 +849,8 @@ impl AddSettings {
|
|||
python,
|
||||
} = args;
|
||||
|
||||
let dependency_type = if let Some(group) = optional {
|
||||
DependencyType::Optional(group)
|
||||
let dependency_type = if let Some(extra) = optional {
|
||||
DependencyType::Optional(extra)
|
||||
} else if dev {
|
||||
DependencyType::Dev
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue