Skip to content

Commit a0f8359

Browse files
authored
Add conflict detection between --only-group and --extra flags (#15788)
<!-- Thank you for contributing to uv! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? - Does this pull request include references to any relevant issues? --> ## Summary - Added `conflicts_with = "only_group"` to `--extra` arguments in `SyncArgs`, `RunArgs`, and `ExportArgs` - Added tests to verify proper conflict detection and error messages **Before:** The `--extra` flag was silently ignored when used with `--only-group` **After:** Clear error message: `error: the argument '--only-group <ONLY_GROUP>' cannot be used with '--extra <EXTRA>'` fixes: #15676 ## Test Plan - Tests confirm proper error message format when `--only-group` and `--extra` are used together - Verified existing functionality remains unchanged when flags are used independently
1 parent 5f2871e commit a0f8359

File tree

4 files changed

+162
-6
lines changed

4 files changed

+162
-6
lines changed

crates/uv-cli/src/lib.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3077,15 +3077,15 @@ pub struct RunArgs {
30773077
/// Optional dependencies are defined via `project.optional-dependencies` in a `pyproject.toml`.
30783078
///
30793079
/// This option is only available when running in a project.
3080-
#[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)]
3080+
#[arg(long, conflicts_with = "all_extras", conflicts_with = "only_group", value_parser = extra_name_with_clap_error)]
30813081
pub extra: Option<Vec<ExtraName>>,
30823082

30833083
/// Include all optional dependencies.
30843084
///
30853085
/// Optional dependencies are defined via `project.optional-dependencies` in a `pyproject.toml`.
30863086
///
30873087
/// This option is only available when running in a project.
3088-
#[arg(long, conflicts_with = "extra")]
3088+
#[arg(long, conflicts_with = "extra", conflicts_with = "only_group")]
30893089
pub all_extras: bool,
30903090

30913091
/// Exclude the specified optional dependencies, if `--all-extras` is supplied.
@@ -3396,7 +3396,7 @@ pub struct SyncArgs {
33963396
///
33973397
/// Note that all optional dependencies are always included in the resolution; this option only
33983398
/// affects the selection of packages to install.
3399-
#[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)]
3399+
#[arg(long, conflicts_with = "all_extras", conflicts_with = "only_group", value_parser = extra_name_with_clap_error)]
34003400
pub extra: Option<Vec<ExtraName>>,
34013401

34023402
/// Select the output format.
@@ -3410,7 +3410,7 @@ pub struct SyncArgs {
34103410
///
34113411
/// Note that all optional dependencies are always included in the resolution; this option only
34123412
/// affects the selection of packages to install.
3413-
#[arg(long, conflicts_with = "extra")]
3413+
#[arg(long, conflicts_with = "extra", conflicts_with = "only_group")]
34143414
pub all_extras: bool,
34153415

34163416
/// Exclude the specified optional dependencies, if `--all-extras` is supplied.
@@ -4247,11 +4247,11 @@ pub struct ExportArgs {
42474247
/// Include optional dependencies from the specified extra name.
42484248
///
42494249
/// May be provided more than once.
4250-
#[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)]
4250+
#[arg(long, conflicts_with = "all_extras", conflicts_with = "only_group", value_parser = extra_name_with_clap_error)]
42514251
pub extra: Option<Vec<ExtraName>>,
42524252

42534253
/// Include all optional dependencies.
4254-
#[arg(long, conflicts_with = "extra")]
4254+
#[arg(long, conflicts_with = "extra", conflicts_with = "only_group")]
42554255
pub all_extras: bool,
42564256

42574257
/// Exclude the specified optional dependencies, if `--all-extras` is supplied.

crates/uv/tests/it/export.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4467,3 +4467,55 @@ fn no_editable_env_var() -> Result<()> {
44674467

44684468
Ok(())
44694469
}
4470+
4471+
#[test]
4472+
fn export_only_group_and_extra_conflict() -> Result<()> {
4473+
let context = TestContext::new("3.12");
4474+
4475+
let pyproject_toml = context.temp_dir.child("pyproject.toml");
4476+
pyproject_toml.write_str(
4477+
r#"
4478+
[project]
4479+
name = "project"
4480+
version = "0.1.0"
4481+
requires-python = ">=3.12"
4482+
dependencies = []
4483+
4484+
[project.optional-dependencies]
4485+
test = ["pytest"]
4486+
4487+
[dependency-groups]
4488+
dev = ["ruff"]
4489+
"#,
4490+
)?;
4491+
4492+
// Using --only-group and --extra together should error.
4493+
uv_snapshot!(context.filters(), context.export().arg("--only-group").arg("dev").arg("--extra").arg("test"), @r###"
4494+
success: false
4495+
exit_code: 2
4496+
----- stdout -----
4497+
4498+
----- stderr -----
4499+
error: the argument '--only-group <ONLY_GROUP>' cannot be used with '--extra <EXTRA>'
4500+
4501+
Usage: uv export --cache-dir [CACHE_DIR] --only-group <ONLY_GROUP> --exclude-newer <EXCLUDE_NEWER>
4502+
4503+
For more information, try '--help'.
4504+
"###);
4505+
4506+
// Using --only-group and --all-extras together should also error.
4507+
uv_snapshot!(context.filters(), context.export().arg("--only-group").arg("dev").arg("--all-extras"), @r###"
4508+
success: false
4509+
exit_code: 2
4510+
----- stdout -----
4511+
4512+
----- stderr -----
4513+
error: the argument '--only-group <ONLY_GROUP>' cannot be used with '--all-extras'
4514+
4515+
Usage: uv export --cache-dir [CACHE_DIR] --only-group <ONLY_GROUP> --exclude-newer <EXCLUDE_NEWER>
4516+
4517+
For more information, try '--help'.
4518+
"###);
4519+
4520+
Ok(())
4521+
}

crates/uv/tests/it/run.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6039,3 +6039,55 @@ fn isolate_child_environment() -> Result<()> {
60396039

60406040
Ok(())
60416041
}
6042+
6043+
#[test]
6044+
fn run_only_group_and_extra_conflict() -> Result<()> {
6045+
let context = TestContext::new("3.12");
6046+
6047+
let pyproject_toml = context.temp_dir.child("pyproject.toml");
6048+
pyproject_toml.write_str(
6049+
r#"
6050+
[project]
6051+
name = "project"
6052+
version = "0.1.0"
6053+
requires-python = ">=3.12"
6054+
dependencies = []
6055+
6056+
[project.optional-dependencies]
6057+
test = ["pytest"]
6058+
6059+
[dependency-groups]
6060+
dev = ["ruff"]
6061+
"#,
6062+
)?;
6063+
6064+
// Using --only-group and --extra together should error.
6065+
uv_snapshot!(context.filters(), context.run().arg("--only-group").arg("dev").arg("--extra").arg("test").arg("python").arg("-c").arg("print('hello')"), @r###"
6066+
success: false
6067+
exit_code: 2
6068+
----- stdout -----
6069+
6070+
----- stderr -----
6071+
error: the argument '--only-group <ONLY_GROUP>' cannot be used with '--extra <EXTRA>'
6072+
6073+
Usage: uv run --cache-dir [CACHE_DIR] --only-group <ONLY_GROUP> --exclude-newer <EXCLUDE_NEWER>
6074+
6075+
For more information, try '--help'.
6076+
"###);
6077+
6078+
// Using --only-group and --all-extras together should also error.
6079+
uv_snapshot!(context.filters(), context.run().arg("--only-group").arg("dev").arg("--all-extras").arg("python").arg("-c").arg("print('hello')"), @r###"
6080+
success: false
6081+
exit_code: 2
6082+
----- stdout -----
6083+
6084+
----- stderr -----
6085+
error: the argument '--only-group <ONLY_GROUP>' cannot be used with '--all-extras'
6086+
6087+
Usage: uv run --cache-dir [CACHE_DIR] --only-group <ONLY_GROUP> --exclude-newer <EXCLUDE_NEWER>
6088+
6089+
For more information, try '--help'.
6090+
"###);
6091+
6092+
Ok(())
6093+
}

crates/uv/tests/it/sync.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14093,3 +14093,55 @@ fn workspace_editable_conflict() -> Result<()> {
1409314093

1409414094
Ok(())
1409514095
}
14096+
14097+
#[test]
14098+
fn only_group_and_extra_conflict() -> Result<()> {
14099+
let context = TestContext::new("3.12");
14100+
14101+
let pyproject_toml = context.temp_dir.child("pyproject.toml");
14102+
pyproject_toml.write_str(
14103+
r#"
14104+
[project]
14105+
name = "project"
14106+
version = "0.1.0"
14107+
requires-python = ">=3.12"
14108+
dependencies = []
14109+
14110+
[project.optional-dependencies]
14111+
test = ["pytest"]
14112+
14113+
[dependency-groups]
14114+
dev = ["ruff"]
14115+
"#,
14116+
)?;
14117+
14118+
// Using --only-group and --extra together should error.
14119+
uv_snapshot!(context.filters(), context.sync().arg("--only-group").arg("dev").arg("--extra").arg("test"), @r###"
14120+
success: false
14121+
exit_code: 2
14122+
----- stdout -----
14123+
14124+
----- stderr -----
14125+
error: the argument '--only-group <ONLY_GROUP>' cannot be used with '--extra <EXTRA>'
14126+
14127+
Usage: uv sync --cache-dir [CACHE_DIR] --only-group <ONLY_GROUP> --exclude-newer <EXCLUDE_NEWER>
14128+
14129+
For more information, try '--help'.
14130+
"###);
14131+
14132+
// Using --only-group and --all-extras together should also error.
14133+
uv_snapshot!(context.filters(), context.sync().arg("--only-group").arg("dev").arg("--all-extras"), @r###"
14134+
success: false
14135+
exit_code: 2
14136+
----- stdout -----
14137+
14138+
----- stderr -----
14139+
error: the argument '--only-group <ONLY_GROUP>' cannot be used with '--all-extras'
14140+
14141+
Usage: uv sync --cache-dir [CACHE_DIR] --only-group <ONLY_GROUP> --exclude-newer <EXCLUDE_NEWER>
14142+
14143+
For more information, try '--help'.
14144+
"###);
14145+
14146+
Ok(())
14147+
}

0 commit comments

Comments
 (0)