Add --exact flag to uv run (#10198)
## Summary `uv run --exact` will remove any unnecessary packages prior to running the given command. (By default, `uv run` uses "inexact" semantics.) Closes https://github.com/astral-sh/uv/issues/7838.
This commit is contained in:
@@ -2718,6 +2718,17 @@ pub struct RunArgs {
|
||||
#[arg(long)]
|
||||
pub no_editable: bool,
|
||||
|
||||
/// Do not remove extraneous packages present in the environment.
|
||||
#[arg(long, overrides_with("exact"), alias = "no-exact", hide = true)]
|
||||
pub inexact: bool,
|
||||
|
||||
/// Perform an exact sync, removing extraneous packages.
|
||||
///
|
||||
/// When enabled, uv will remove any extraneous packages from the environment.
|
||||
/// By default, `uv run` will make the minimum necessary changes to satisfy the requirements.
|
||||
#[arg(long, overrides_with("inexact"))]
|
||||
pub exact: bool,
|
||||
|
||||
/// Load environment variables from a `.env` file.
|
||||
///
|
||||
/// Can be provided multiple times, with subsequent files overriding values defined in
|
||||
|
||||
@@ -74,6 +74,7 @@ pub(crate) async fn run(
|
||||
extras: ExtrasSpecification,
|
||||
dev: DevGroupsSpecification,
|
||||
editable: EditableMode,
|
||||
modifications: Modifications,
|
||||
python: Option<String>,
|
||||
install_mirrors: PythonInstallMirrors,
|
||||
settings: ResolverInstallerSettings,
|
||||
@@ -705,7 +706,7 @@ pub(crate) async fn run(
|
||||
&dev.with_defaults(defaults),
|
||||
editable,
|
||||
install_options,
|
||||
Modifications::Sufficient,
|
||||
modifications,
|
||||
settings.as_ref().into(),
|
||||
if show_resolution {
|
||||
Box::new(DefaultInstallLogger)
|
||||
|
||||
@@ -1414,6 +1414,7 @@ async fn run_project(
|
||||
args.extras,
|
||||
args.dev,
|
||||
args.editable,
|
||||
args.modifications,
|
||||
args.python,
|
||||
args.install_mirrors,
|
||||
args.settings,
|
||||
|
||||
@@ -262,6 +262,7 @@ pub(crate) struct RunSettings {
|
||||
pub(crate) extras: ExtrasSpecification,
|
||||
pub(crate) dev: DevGroupsSpecification,
|
||||
pub(crate) editable: EditableMode,
|
||||
pub(crate) modifications: Modifications,
|
||||
pub(crate) with: Vec<String>,
|
||||
pub(crate) with_editable: Vec<String>,
|
||||
pub(crate) with_requirements: Vec<PathBuf>,
|
||||
@@ -297,6 +298,8 @@ impl RunSettings {
|
||||
module: _,
|
||||
only_dev,
|
||||
no_editable,
|
||||
inexact,
|
||||
exact,
|
||||
script: _,
|
||||
gui_script: _,
|
||||
command: _,
|
||||
@@ -336,6 +339,11 @@ impl RunSettings {
|
||||
dev, no_dev, only_dev, group, no_group, only_group, all_groups,
|
||||
),
|
||||
editable: EditableMode::from_args(no_editable),
|
||||
modifications: if flag(exact, inexact).unwrap_or(false) {
|
||||
Modifications::Exact
|
||||
} else {
|
||||
Modifications::Sufficient
|
||||
},
|
||||
with: with
|
||||
.into_iter()
|
||||
.flat_map(CommaSeparatedRequirements::into_iter)
|
||||
|
||||
@@ -196,7 +196,8 @@ fn run_args() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run without specifying any argunments.
|
||||
/// Run without specifying any arguments.
|
||||
///
|
||||
/// This should list the available scripts.
|
||||
#[test]
|
||||
fn run_no_args() -> Result<()> {
|
||||
@@ -807,6 +808,75 @@ fn run_managed_false() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_exact() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(indoc! { r#"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "1.0.0"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = ["iniconfig"]
|
||||
"#
|
||||
})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.run().arg("python").arg("-c").arg("import iniconfig"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 2 packages in [TIME]
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ iniconfig==2.0.0
|
||||
"###);
|
||||
|
||||
// Remove `iniconfig`.
|
||||
pyproject_toml.write_str(indoc! { r#"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "1.0.0"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = ["anyio"]
|
||||
"#
|
||||
})?;
|
||||
|
||||
// By default, `uv run` uses inexact semantics, so both `iniconfig` and `anyio` should still be available.
|
||||
uv_snapshot!(context.filters(), context.run().arg("python").arg("-c").arg("import iniconfig; import anyio"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
Prepared 3 packages in [TIME]
|
||||
Installed 3 packages in [TIME]
|
||||
+ anyio==4.3.0
|
||||
+ idna==3.6
|
||||
+ sniffio==1.3.1
|
||||
"###);
|
||||
|
||||
// But under `--exact`, `iniconfig` should not be available.
|
||||
uv_snapshot!(context.filters(), context.run().arg("--exact").arg("python").arg("-c").arg("import iniconfig"), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
Uninstalled 1 package in [TIME]
|
||||
- iniconfig==2.0.0
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 1, in <module>
|
||||
ModuleNotFoundError: No module named 'iniconfig'
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_with() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
@@ -148,6 +148,10 @@ uv run [OPTIONS] [COMMAND]
|
||||
<p>Can be provided multiple times, with subsequent files overriding values defined in previous files.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_ENV_FILE</code> environment variable.</p>
|
||||
</dd><dt><code>--exact</code></dt><dd><p>Perform an exact sync, removing extraneous packages.</p>
|
||||
|
||||
<p>When enabled, uv will remove any extraneous packages from the environment. By default, <code>uv run</code> will make the minimum necessary changes to satisfy the requirements.</p>
|
||||
|
||||
</dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
|
||||
|
||||
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and local dates in the same format (e.g., <code>2006-12-02</code>) in your system’s configured time zone.</p>
|
||||
|
||||
Reference in New Issue
Block a user