Skip to content

Commit 84cf199

Browse files
committed
Error when --group includes non-existent groups (#8394)
## Summary Part of #8272.
1 parent 7e7618e commit 84cf199

File tree

6 files changed

+96
-0
lines changed

6 files changed

+96
-0
lines changed

crates/uv-configuration/src/dev.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,12 @@ impl DevGroupsSpecification {
163163
(self.dev.is_none() || self.dev.as_ref().is_some_and(DevMode::prod)) && self.groups.prod()
164164
}
165165

166+
/// Returns the flag that was used to request development dependencies.
166167
pub fn dev_mode(&self) -> Option<&DevMode> {
167168
self.dev.as_ref()
168169
}
169170

171+
/// Returns the list of groups to include.
170172
pub fn groups(&self) -> &GroupsSpecification {
171173
&self.groups
172174
}

crates/uv-workspace/src/pyproject.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,11 @@ impl DependencyGroups {
540540
self.0.get(group)
541541
}
542542

543+
/// Returns `true` if the dependency group is in the list.
544+
pub fn contains_key(&self, group: &GroupName) -> bool {
545+
self.0.contains_key(group)
546+
}
547+
543548
/// Returns an iterator over the dependency groups.
544549
pub fn iter(&self) -> impl Iterator<Item = (&GroupName, &Vec<DependencyGroupSpecifier>)> {
545550
self.0.iter()

crates/uv/src/commands/project/export.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@ pub(crate) async fn export(
7070
VirtualProject::discover(project_dir, &DiscoveryOptions::default()).await?
7171
};
7272

73+
// Validate the requested dependency groups.
74+
for group in dev.groups().iter() {
75+
if !project
76+
.pyproject_toml()
77+
.dependency_groups
78+
.as_ref()
79+
.is_some_and(|groups| groups.contains_key(group))
80+
{
81+
return Err(anyhow::anyhow!(
82+
"Group `{group}` is not defined in the project's `dependency-group` table"
83+
));
84+
}
85+
}
86+
7387
let VirtualProject::Project(project) = project else {
7488
return Err(anyhow::anyhow!("Legacy non-project roots are not supported in `uv export`; add a `[project]` table to your `pyproject.toml` to enable exports"));
7589
};

crates/uv/src/commands/project/run.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,20 @@ pub(crate) async fn run(
469469
);
470470
}
471471

472+
// Validate the requested dependency groups.
473+
for group in dev.groups().iter() {
474+
if !project
475+
.pyproject_toml()
476+
.dependency_groups
477+
.as_ref()
478+
.is_some_and(|groups| groups.contains_key(group))
479+
{
480+
return Err(anyhow::anyhow!(
481+
"Group `{group}` is not defined in the project's `dependency-group` table"
482+
));
483+
}
484+
}
485+
472486
let venv = if isolated {
473487
debug!("Creating isolated virtual environment");
474488

crates/uv/src/commands/project/sync.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ pub(crate) async fn sync(
9393
warn_user!("Skipping installation of entry points (`project.scripts`) because this project is not packaged; to install entry points, set `tool.uv.package = true` or define a `build-system`");
9494
}
9595

96+
// Validate the requested dependency groups.
97+
for group in dev.groups().iter() {
98+
if !project
99+
.pyproject_toml()
100+
.dependency_groups
101+
.as_ref()
102+
.is_some_and(|groups| groups.contains_key(group))
103+
{
104+
return Err(anyhow::anyhow!(
105+
"Group `{group}` is not defined in the project's `dependency-group` table"
106+
));
107+
}
108+
}
109+
96110
// Discover or create the virtual environment.
97111
let venv = project::get_or_init_environment(
98112
target.workspace(),

crates/uv/tests/it/sync.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,53 @@ fn sync_dev_group() -> Result<()> {
12261226
Ok(())
12271227
}
12281228

1229+
#[test]
1230+
fn sync_non_existent_group() -> Result<()> {
1231+
let context = TestContext::new("3.12");
1232+
1233+
let pyproject_toml = context.temp_dir.child("pyproject.toml");
1234+
pyproject_toml.write_str(
1235+
r#"
1236+
[project]
1237+
name = "project"
1238+
version = "0.1.0"
1239+
requires-python = ">=3.12"
1240+
dependencies = ["typing-extensions"]
1241+
1242+
[dependency-groups]
1243+
foo = []
1244+
bar = ["requests"]
1245+
"#,
1246+
)?;
1247+
1248+
context.lock().assert().success();
1249+
1250+
// Requesting a non-existent group should fail.
1251+
uv_snapshot!(context.filters(), context.sync().arg("--group").arg("baz"), @r###"
1252+
success: false
1253+
exit_code: 2
1254+
----- stdout -----
1255+
1256+
----- stderr -----
1257+
error: Group `baz` is not defined in the project's `dependency-group` table
1258+
"###);
1259+
1260+
// Requesting an empty group should succeed.
1261+
uv_snapshot!(context.filters(), context.sync().arg("--group").arg("foo"), @r###"
1262+
success: true
1263+
exit_code: 0
1264+
----- stdout -----
1265+
1266+
----- stderr -----
1267+
Resolved 7 packages in [TIME]
1268+
Prepared 1 package in [TIME]
1269+
Installed 1 package in [TIME]
1270+
+ typing-extensions==4.10.0
1271+
"###);
1272+
1273+
Ok(())
1274+
}
1275+
12291276
/// Regression test for <https://github.com/astral-sh/uv/issues/6316>.
12301277
///
12311278
/// Previously, we would read metadata statically from pyproject.toml and write that to `uv.lock`. In

0 commit comments

Comments
 (0)