mirror of https://github.com/astral-sh/uv
Add support for `uv run --all-packages` (#8741)
## Summary Closes https://github.com/astral-sh/uv/issues/8724.
This commit is contained in:
parent
3c9dd97fe9
commit
3808b61fc1
|
|
@ -2740,10 +2740,20 @@ pub struct RunArgs {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub refresh: RefreshArgs,
|
pub refresh: RefreshArgs,
|
||||||
|
|
||||||
|
/// Run the command with all workspace members installed.
|
||||||
|
///
|
||||||
|
/// The workspace's environment (`.venv`) is updated to include all workspace
|
||||||
|
/// members.
|
||||||
|
///
|
||||||
|
/// Any extras or groups specified via `--extra`, `--group`, or related options
|
||||||
|
/// will be applied to all workspace members.
|
||||||
|
#[arg(long, conflicts_with = "package")]
|
||||||
|
pub all_packages: bool,
|
||||||
|
|
||||||
/// Run the command in a specific package in the workspace.
|
/// Run the command in a specific package in the workspace.
|
||||||
///
|
///
|
||||||
/// If the workspace member does not exist, uv will exit with an error.
|
/// If the workspace member does not exist, uv will exit with an error.
|
||||||
#[arg(long)]
|
#[arg(long, conflicts_with = "all_packages")]
|
||||||
pub package: Option<PackageName>,
|
pub package: Option<PackageName>,
|
||||||
|
|
||||||
/// Avoid discovering the project or workspace.
|
/// Avoid discovering the project or workspace.
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ pub(crate) async fn run(
|
||||||
frozen: bool,
|
frozen: bool,
|
||||||
no_sync: bool,
|
no_sync: bool,
|
||||||
isolated: bool,
|
isolated: bool,
|
||||||
|
all_packages: bool,
|
||||||
package: Option<PackageName>,
|
package: Option<PackageName>,
|
||||||
no_project: bool,
|
no_project: bool,
|
||||||
no_config: bool,
|
no_config: bool,
|
||||||
|
|
@ -346,6 +347,11 @@ pub(crate) async fn run(
|
||||||
if let Some(flag) = dev.groups().and_then(GroupsSpecification::as_flag) {
|
if let Some(flag) = dev.groups().and_then(GroupsSpecification::as_flag) {
|
||||||
warn_user!("`{flag}` is not supported for Python scripts with inline metadata");
|
warn_user!("`{flag}` is not supported for Python scripts with inline metadata");
|
||||||
}
|
}
|
||||||
|
if all_packages {
|
||||||
|
warn_user!(
|
||||||
|
"`--all-packages` is a no-op for Python scripts with inline metadata, which always run in isolation"
|
||||||
|
);
|
||||||
|
}
|
||||||
if package.is_some() {
|
if package.is_some() {
|
||||||
warn_user!(
|
warn_user!(
|
||||||
"`--package` is a no-op for Python scripts with inline metadata, which always run in isolation"
|
"`--package` is a no-op for Python scripts with inline metadata, which always run in isolation"
|
||||||
|
|
@ -550,8 +556,14 @@ pub(crate) async fn run(
|
||||||
.flatten();
|
.flatten();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let target = if all_packages {
|
||||||
|
InstallTarget::from_workspace(&project)
|
||||||
|
} else {
|
||||||
|
InstallTarget::from_project(&project)
|
||||||
|
};
|
||||||
|
|
||||||
// Determine the default groups to include.
|
// Determine the default groups to include.
|
||||||
validate_dependency_groups(InstallTarget::from_project(&project), &dev)?;
|
validate_dependency_groups(target, &dev)?;
|
||||||
let defaults = default_dependency_groups(project.pyproject_toml())?;
|
let defaults = default_dependency_groups(project.pyproject_toml())?;
|
||||||
|
|
||||||
// Determine the lock mode.
|
// Determine the lock mode.
|
||||||
|
|
@ -607,7 +619,7 @@ pub(crate) async fn run(
|
||||||
let install_options = InstallOptions::default();
|
let install_options = InstallOptions::default();
|
||||||
|
|
||||||
project::sync::do_sync(
|
project::sync::do_sync(
|
||||||
InstallTarget::from_project(&project),
|
target,
|
||||||
&venv,
|
&venv,
|
||||||
result.lock(),
|
result.lock(),
|
||||||
&extras,
|
&extras,
|
||||||
|
|
|
||||||
|
|
@ -1293,6 +1293,7 @@ async fn run_project(
|
||||||
args.frozen,
|
args.frozen,
|
||||||
args.no_sync,
|
args.no_sync,
|
||||||
args.isolated,
|
args.isolated,
|
||||||
|
args.all_packages,
|
||||||
args.package,
|
args.package,
|
||||||
args.no_project,
|
args.no_project,
|
||||||
no_config,
|
no_config,
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,7 @@ pub(crate) struct RunSettings {
|
||||||
pub(crate) with_requirements: Vec<PathBuf>,
|
pub(crate) with_requirements: Vec<PathBuf>,
|
||||||
pub(crate) isolated: bool,
|
pub(crate) isolated: bool,
|
||||||
pub(crate) show_resolution: bool,
|
pub(crate) show_resolution: bool,
|
||||||
|
pub(crate) all_packages: bool,
|
||||||
pub(crate) package: Option<PackageName>,
|
pub(crate) package: Option<PackageName>,
|
||||||
pub(crate) no_project: bool,
|
pub(crate) no_project: bool,
|
||||||
pub(crate) no_sync: bool,
|
pub(crate) no_sync: bool,
|
||||||
|
|
@ -271,6 +272,7 @@ impl RunSettings {
|
||||||
installer,
|
installer,
|
||||||
build,
|
build,
|
||||||
refresh,
|
refresh,
|
||||||
|
all_packages,
|
||||||
package,
|
package,
|
||||||
no_project,
|
no_project,
|
||||||
python,
|
python,
|
||||||
|
|
@ -296,6 +298,7 @@ impl RunSettings {
|
||||||
.collect(),
|
.collect(),
|
||||||
isolated,
|
isolated,
|
||||||
show_resolution,
|
show_resolution,
|
||||||
|
all_packages,
|
||||||
package,
|
package,
|
||||||
no_project,
|
no_project,
|
||||||
no_sync,
|
no_sync,
|
||||||
|
|
|
||||||
|
|
@ -806,6 +806,169 @@ fn run_with() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sync all members in a workspace.
|
||||||
|
#[test]
|
||||||
|
fn run_in_workspace() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["anyio>3"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = ["child1", "child2"]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
child1 = { workspace = true }
|
||||||
|
child2 = { workspace = true }
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
context
|
||||||
|
.temp_dir
|
||||||
|
.child("src")
|
||||||
|
.child("project")
|
||||||
|
.child("__init__.py")
|
||||||
|
.touch()?;
|
||||||
|
|
||||||
|
let child1 = context.temp_dir.child("child1");
|
||||||
|
child1.child("pyproject.toml").write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "child1"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["iniconfig>1"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
child1
|
||||||
|
.child("src")
|
||||||
|
.child("child1")
|
||||||
|
.child("__init__.py")
|
||||||
|
.touch()?;
|
||||||
|
|
||||||
|
let child2 = context.temp_dir.child("child2");
|
||||||
|
child2.child("pyproject.toml").write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "child2"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["typing-extensions>4"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
child2
|
||||||
|
.child("src")
|
||||||
|
.child("child2")
|
||||||
|
.child("__init__.py")
|
||||||
|
.touch()?;
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("main.py");
|
||||||
|
test_script.write_str(indoc! { r"
|
||||||
|
import anyio
|
||||||
|
"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 8 packages in [TIME]
|
||||||
|
Prepared 4 packages in [TIME]
|
||||||
|
Installed 4 packages in [TIME]
|
||||||
|
+ anyio==4.3.0
|
||||||
|
+ idna==3.6
|
||||||
|
+ project==0.1.0 (from file://[TEMP_DIR]/)
|
||||||
|
+ sniffio==1.3.1
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("main.py");
|
||||||
|
test_script.write_str(indoc! { r"
|
||||||
|
import iniconfig
|
||||||
|
"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 8 packages in [TIME]
|
||||||
|
Audited 4 packages in [TIME]
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "[TEMP_DIR]/main.py", line 1, in <module>
|
||||||
|
import iniconfig
|
||||||
|
ModuleNotFoundError: No module named 'iniconfig'
|
||||||
|
"###);
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--package").arg("child1").arg("main.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 8 packages in [TIME]
|
||||||
|
Prepared 2 packages in [TIME]
|
||||||
|
Installed 2 packages in [TIME]
|
||||||
|
+ child1==0.1.0 (from file://[TEMP_DIR]/child1)
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let test_script = context.temp_dir.child("main.py");
|
||||||
|
test_script.write_str(indoc! { r"
|
||||||
|
import typing_extensions
|
||||||
|
"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 8 packages in [TIME]
|
||||||
|
Audited 4 packages in [TIME]
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "[TEMP_DIR]/main.py", line 1, in <module>
|
||||||
|
import typing_extensions
|
||||||
|
ModuleNotFoundError: No module named 'typing_extensions'
|
||||||
|
"###);
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--all-packages").arg("main.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 8 packages in [TIME]
|
||||||
|
Prepared 2 packages in [TIME]
|
||||||
|
Installed 2 packages in [TIME]
|
||||||
|
+ child2==0.1.0 (from file://[TEMP_DIR]/child2)
|
||||||
|
+ typing-extensions==4.10.0
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn run_with_editable() -> Result<()> {
|
fn run_with_editable() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,12 @@ uv run [OPTIONS] [COMMAND]
|
||||||
|
|
||||||
<p>This option is only available when running in a project.</p>
|
<p>This option is only available when running in a project.</p>
|
||||||
|
|
||||||
|
</dd><dt><code>--all-packages</code></dt><dd><p>Run the command with all workspace members installed.</p>
|
||||||
|
|
||||||
|
<p>The workspace’s environment (<code>.venv</code>) is updated to include all workspace members.</p>
|
||||||
|
|
||||||
|
<p>Any extras or groups specified via <code>--extra</code>, <code>--group</code>, or related options will be applied to all workspace members.</p>
|
||||||
|
|
||||||
</dd><dt><code>--allow-insecure-host</code> <i>allow-insecure-host</i></dt><dd><p>Allow insecure connections to a host.</p>
|
</dd><dt><code>--allow-insecure-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>Can be provided multiple times.</p>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue