Add --all-groups to uv pip install

This commit is contained in:
Charlie Marsh 2025-07-20 20:14:08 -04:00
parent 80708dea6e
commit 4526e586b7
10 changed files with 222 additions and 20 deletions

View File

@ -1542,6 +1542,15 @@ pub struct PipSyncArgs {
#[arg(long, group = "sources")]
pub group: Vec<PipGroupName>,
/// Include dependencies from all dependency groups.
///
/// Only applies to `pylock.toml` sources and the `pyproject.toml` in the working directory.
#[arg(long, group = "sources", overrides_with = "no_all_groups")]
pub all_groups: bool,
#[arg(long, overrides_with("all_groups"), hide = true)]
pub no_all_groups: bool,
#[command(flatten)]
pub installer: InstallerArgs,
@ -1844,6 +1853,15 @@ pub struct PipInstallArgs {
#[arg(long, group = "sources")]
pub group: Vec<PipGroupName>,
/// Include dependencies from all dependency groups.
///
/// Only applies to `pylock.toml` sources and the `pyproject.toml` in the working directory.
#[arg(long, group = "sources", overrides_with = "no_all_groups")]
pub all_groups: bool,
#[arg(long, overrides_with("all_groups"), hide = true)]
pub no_all_groups: bool,
#[command(flatten)]
pub installer: ResolverInstallerArgs,

View File

@ -289,7 +289,21 @@ impl RequirementsSpecification {
names.push(group.name.clone());
}
if !names.is_empty() {
if groups.all_groups {
spec.groups.insert(
pylock_toml.clone(),
DependencyGroups::from_args(
false,
false,
false,
Vec::new(),
Vec::new(),
false,
Vec::new(),
true,
),
);
} else if !names.is_empty() {
spec.groups.insert(
pylock_toml.clone(),
DependencyGroups::from_args(
@ -323,7 +337,7 @@ impl RequirementsSpecification {
}
let mut group_specs = BTreeMap::new();
for (path, groups) in groups_by_path {
for (path, group_names) in groups_by_path {
let group_spec = DependencyGroups::from_args(
false,
false,
@ -331,11 +345,29 @@ impl RequirementsSpecification {
Vec::new(),
Vec::new(),
false,
groups,
group_names,
false,
);
group_specs.insert(path, group_spec);
}
// If `--all-groups` was specified, we assume it refers to the root project.
if groups.all_groups {
group_specs.insert(
groups.root.join("pyproject.toml"),
DependencyGroups::from_args(
false,
false,
false,
Vec::new(),
Vec::new(),
false,
Vec::new(),
true,
),
);
}
spec.groups = group_specs;
}
@ -534,4 +566,6 @@ pub struct GroupsSpecification {
pub root: PathBuf,
/// The enabled groups.
pub groups: Vec<PipGroupName>,
/// Whether to include all groups.
pub all_groups: bool,
}

View File

@ -1181,6 +1181,17 @@ pub struct PipOptions {
"#
)]
pub group: Option<Vec<PipGroupName>>,
/// Include dependencies from all dependency groups.
///
/// Only applies to `pylock.toml` sources and the `pyproject.toml` in the working directory.
#[option(
default = "false",
value_type = "bool",
example = r#"
all-groups = true
"#
)]
pub all_groups: Option<bool>,
/// Allow `uv pip sync` with empty requirements, which will clear the environment of all
/// packages.
#[option(

View File

@ -473,7 +473,8 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.collect::<Result<Vec<_>, _>>()?;
let groups = GroupsSpecification {
root: project_dir.to_path_buf(),
groups: args.settings.groups,
groups: args.settings.groups.clone(),
all_groups: args.settings.all_groups,
};
commands::pip_compile(
@ -568,7 +569,8 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.collect::<Result<Vec<_>, _>>()?;
let groups = GroupsSpecification {
root: project_dir.to_path_buf(),
groups: args.settings.groups,
groups: args.settings.groups.clone(),
all_groups: args.settings.all_groups,
};
commands::pip_sync(
@ -654,7 +656,8 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.collect::<Result<Vec<_>, _>>()?;
let groups = GroupsSpecification {
root: project_dir.to_path_buf(),
groups: args.settings.groups,
groups: args.settings.groups.clone(),
all_groups: args.settings.all_groups,
};
// Special-case: any source trees specified on the command-line are automatically

View File

@ -2062,6 +2062,8 @@ impl PipSyncSettings {
all_extras,
no_all_extras,
group,
all_groups,
no_all_groups,
installer,
refresh,
require_hashes,
@ -2129,6 +2131,7 @@ impl PipSyncSettings {
extra,
all_extras: flag(all_extras, no_all_extras, "all-extras"),
group: Some(group),
all_groups: flag(all_groups, no_all_groups, "all-groups"),
torch_backend,
..PipOptions::from(installer)
},
@ -2174,6 +2177,8 @@ impl PipInstallSettings {
no_deps,
deps,
group,
all_groups,
no_all_groups,
require_hashes,
no_require_hashes,
verify_hashes,
@ -2286,6 +2291,7 @@ impl PipInstallSettings {
extra,
all_extras: flag(all_extras, no_all_extras, "all-extras"),
group: Some(group),
all_groups: flag(all_groups, no_all_groups, "all-groups"),
no_deps: flag(no_deps, deps, "deps"),
python_version,
python_platform,
@ -2907,6 +2913,7 @@ pub(crate) struct PipSettings {
pub(crate) system: bool,
pub(crate) extras: ExtrasSpecification,
pub(crate) groups: Vec<PipGroupName>,
pub(crate) all_groups: bool,
pub(crate) break_system_packages: bool,
pub(crate) target: Option<Target>,
pub(crate) prefix: Option<Prefix>,
@ -3022,6 +3029,7 @@ impl PipSettings {
upgrade_package,
reinstall,
reinstall_package,
all_groups,
} = pip.unwrap_or_default();
let ResolverInstallerOptions {
@ -3114,6 +3122,7 @@ impl PipSettings {
),
groups: args.group.combine(group).unwrap_or_default(),
all_groups: args.all_groups.combine(all_groups).unwrap_or_default(),
dependency_mode: if args.no_deps.combine(no_deps).unwrap_or_default() {
DependencyMode::Direct
} else {

View File

@ -9754,6 +9754,25 @@ fn dependency_group() -> Result<()> {
+ typing-extensions==4.10.0
");
// all groups at once
context = new_context()?;
uv_snapshot!(context.filters(), context.pip_install()
.arg("-r").arg("pyproject.toml")
.arg("--all-groups"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 4 packages in [TIME]
Prepared 4 packages in [TIME]
Installed 4 packages in [TIME]
+ iniconfig==2.0.0
+ sniffio==1.3.1
+ sortedcontainers==2.4.0
+ typing-extensions==4.10.0
");
Ok(())
}
@ -11393,6 +11412,7 @@ fn pep_751_multiple_sources() -> Result<()> {
#[test]
fn pep_751_groups() -> Result<()> {
fn new_context() -> Result<TestContext> {
let context = TestContext::new("3.13");
let pylock_toml = context.temp_dir.child("pylock.toml");
@ -11498,7 +11518,11 @@ requires_python = "==3.13.*"
"#,
)?;
Ok(context)
}
// By default, only `iniconfig` should be installed, since it's in the default group.
let context = new_context()?;
uv_snapshot!(context.filters(), context.pip_install()
.arg("--preview")
.arg("-r")
@ -11514,7 +11538,8 @@ requires_python = "==3.13.*"
"
);
// With `--extra async`, `anyio` should be installed.
// With `--extra async`, `anyio` should be installed along with `iniconfig` (default group).
let context = new_context()?;
uv_snapshot!(context.filters(), context.pip_install()
.arg("--preview")
.arg("-r")
@ -11526,15 +11551,17 @@ requires_python = "==3.13.*"
----- stdout -----
----- stderr -----
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
Prepared 4 packages in [TIME]
Installed 4 packages in [TIME]
+ anyio==4.9.0
+ idna==3.10
+ iniconfig==2.1.0
+ sniffio==1.3.1
"
);
// With `--group test`, `pygments` should be installed.
// With `--group test`, only `pygments` should be installed.
let context = new_context()?;
uv_snapshot!(context.filters(), context.pip_install()
.arg("--preview")
.arg("-r")
@ -11552,7 +11579,8 @@ requires_python = "==3.13.*"
"
);
// With `--all-extras`, `blinker` should be installed.
// With `--all-extras`, `blinker` and `anyio` should be installed, along with `iniconfig` (default group).
let context = new_context()?;
uv_snapshot!(context.filters(), context.pip_install()
.arg("--preview")
.arg("-r")
@ -11563,13 +11591,38 @@ requires_python = "==3.13.*"
----- stdout -----
----- stderr -----
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
Prepared 5 packages in [TIME]
Installed 5 packages in [TIME]
+ anyio==4.9.0
+ blinker==1.9.0
+ idna==3.10
+ iniconfig==2.1.0
+ sniffio==1.3.1
"
);
// `--group pylock.toml:test` should be rejeceted.
// With `--all-groups`, both `iniconfig` (default group) and `pygments` (test group) should be
// installed.
let context = new_context()?;
uv_snapshot!(context.filters(), context.pip_install()
.arg("--preview")
.arg("-r")
.arg("pylock.toml")
.arg("--all-groups"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Prepared 2 packages in [TIME]
Installed 2 packages in [TIME]
+ iniconfig==2.1.0
+ pygments==2.19.2
"
);
// `--group pylock.toml:test` should be rejected.
let context = new_context()?;
uv_snapshot!(context.filters(), context.pip_install()
.arg("--preview")
.arg("-r")

View File

@ -172,6 +172,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -357,6 +358,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -543,6 +545,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -761,6 +764,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -914,6 +918,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -1111,6 +1116,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -1356,6 +1362,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -1611,6 +1618,7 @@ fn resolve_index_url() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -1821,6 +1829,7 @@ fn resolve_find_links() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -1996,6 +2005,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -2231,6 +2241,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -2449,6 +2460,7 @@ fn resolve_top_level() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -2623,6 +2635,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -2781,6 +2794,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -2939,6 +2953,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -3099,6 +3114,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -3447,6 +3463,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -3673,6 +3690,7 @@ fn resolve_both() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -3903,6 +3921,7 @@ fn resolve_both_special_fields() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -4212,6 +4231,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -4463,6 +4483,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -4624,6 +4645,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -4804,6 +4826,7 @@ fn allow_insecure_host() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -5045,6 +5068,7 @@ fn index_priority() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -5265,6 +5289,7 @@ fn index_priority() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -5491,6 +5516,7 @@ fn index_priority() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -5712,6 +5738,7 @@ fn index_priority() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -5940,6 +5967,7 @@ fn index_priority() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -6161,6 +6189,7 @@ fn index_priority() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -6326,6 +6355,7 @@ fn verify_hashes() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -6477,6 +6507,7 @@ fn verify_hashes() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -6626,6 +6657,7 @@ fn verify_hashes() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -6777,6 +6809,7 @@ fn verify_hashes() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -6926,6 +6959,7 @@ fn verify_hashes() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,
@ -7076,6 +7110,7 @@ fn verify_hashes() -> anyhow::Result<()> {
},
),
groups: [],
all_groups: false,
break_system_packages: false,
target: None,
prefix: None,

View File

@ -3639,6 +3639,8 @@ uv pip sync [OPTIONS] <SRC_FILE>...
<dl class="cli-reference"><dt id="uv-pip-sync--all-extras"><a href="#uv-pip-sync--all-extras"><code>--all-extras</code></a></dt><dd><p>Include all optional dependencies.</p>
<p>Only applies to <code>pylock.toml</code>, <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
</dd><dt id="uv-pip-sync--all-groups"><a href="#uv-pip-sync--all-groups"><code>--all-groups</code></a></dt><dd><p>Include dependencies from all dependency groups.</p>
<p>Only applies to <code>pylock.toml</code> sources and the <code>pyproject.toml</code> in the working directory.</p>
</dd><dt id="uv-pip-sync--allow-empty-requirements"><a href="#uv-pip-sync--allow-empty-requirements"><code>--allow-empty-requirements</code></a></dt><dd><p>Allow sync of empty requirements, which will clear the environment of all packages</p>
</dd><dt id="uv-pip-sync--allow-insecure-host"><a href="#uv-pip-sync--allow-insecure-host"><code>--allow-insecure-host</code></a>, <code>--trusted-host</code> <i>allow-insecure-host</i></dt><dd><p>Allow insecure connections to a host.</p>
<p>Can be provided multiple times.</p>
@ -3883,7 +3885,7 @@ Install packages into an environment
<h3 class="cli-reference">Usage</h3>
```
uv pip install [OPTIONS] <PACKAGE|--requirements <REQUIREMENTS>|--editable <EDITABLE>|--group <GROUP>>
uv pip install [OPTIONS] <PACKAGE|--requirements <REQUIREMENTS>|--editable <EDITABLE>|--group <GROUP>|--all-groups>
```
<h3 class="cli-reference">Arguments</h3>
@ -3896,6 +3898,8 @@ uv pip install [OPTIONS] <PACKAGE|--requirements <REQUIREMENTS>|--editable <EDIT
<dl class="cli-reference"><dt id="uv-pip-install--all-extras"><a href="#uv-pip-install--all-extras"><code>--all-extras</code></a></dt><dd><p>Include all optional dependencies.</p>
<p>Only applies to <code>pylock.toml</code>, <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
</dd><dt id="uv-pip-install--all-groups"><a href="#uv-pip-install--all-groups"><code>--all-groups</code></a></dt><dd><p>Include dependencies from all dependency groups.</p>
<p>Only applies to <code>pylock.toml</code> sources and the <code>pyproject.toml</code> in the working directory.</p>
</dd><dt id="uv-pip-install--allow-insecure-host"><a href="#uv-pip-install--allow-insecure-host"><code>--allow-insecure-host</code></a>, <code>--trusted-host</code> <i>allow-insecure-host</i></dt><dd><p>Allow insecure connections to a host.</p>
<p>Can be provided multiple times.</p>
<p>Expects to receive either a hostname (e.g., <code>localhost</code>), a host-port pair (e.g., <code>localhost:8080</code>), or a URL (e.g., <code>https://localhost</code>).</p>

View File

@ -2121,6 +2121,34 @@ Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
---
#### [`all-groups`](#pip_all-groups) {: #pip_all-groups }
<span id="all-groups"></span>
Include dependencies from all dependency groups.
Only applies to `pylock.toml` sources and the `pyproject.toml` in the working directory.
**Default value**: `false`
**Type**: `bool`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv.pip]
all-groups = true
```
=== "uv.toml"
```toml
[pip]
all-groups = true
```
---
#### [`allow-empty-requirements`](#pip_allow-empty-requirements) {: #pip_allow-empty-requirements }
<span id="allow-empty-requirements"></span>

7
uv.schema.json generated
View File

@ -1160,6 +1160,13 @@
"null"
]
},
"all-groups": {
"description": "Include dependencies from all dependency groups.\n\nOnly applies to `pylock.toml` sources and the `pyproject.toml` in the working directory.",
"type": [
"boolean",
"null"
]
},
"allow-empty-requirements": {
"description": "Allow `uv pip sync` with empty requirements, which will clear the environment of all\npackages.",
"type": [