Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion crates/uv/src/commands/pip/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,12 @@ pub(crate) async fn pip_install(
// Check if the current environment satisfies the requirements.
// Ideally, the resolver would be fast enough to let us remove this check. But right now, for large environments,
// it's an order of magnitude faster to validate the environment than to resolve the requirements.
if reinstall.is_none() && upgrade.is_none() && source_trees.is_empty() && overrides.is_empty() {
if reinstall.is_none()
&& upgrade.is_none()
&& source_trees.is_empty()
&& overrides.is_empty()
&& matches!(modifications, Modifications::Sufficient)
{
match site_packages.satisfies(&requirements, &constraints, &markers)? {
// If the requirements are already satisfied, we're done.
SatisfiesResult::Fresh {
Expand All @@ -223,6 +228,7 @@ pub(crate) async fn pip_install(
if dry_run {
writeln!(printer.stderr(), "Would make no changes")?;
}

return Ok(ExitStatus::Success);
}
SatisfiesResult::Unsatisfied(requirement) => {
Expand Down
76 changes: 70 additions & 6 deletions crates/uv/tests/it/pip_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,27 @@ fn reinstall_incomplete() -> Result<()> {
#[test]
fn exact_install_removes_extraneous_packages() -> Result<()> {
let context = TestContext::new("3.12").with_filtered_counts();
// Install anyio
let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("anyio==3.7.0")?;

uv_snapshot!(context.filters(), context.pip_install()
.arg("--exact")
.arg("-r")
.arg("requirements.txt"), @r###"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved [N] packages in [TIME]
Prepared [N] packages in [TIME]
Installed [N] packages in [TIME]
+ anyio==3.7.0
+ idna==3.6
+ sniffio==1.3.1
"###
);

// Install flask
uv_snapshot!(context.filters(), context.pip_install()
Expand All @@ -770,10 +791,55 @@ fn exact_install_removes_extraneous_packages() -> Result<()> {
"###
);

// Install anyio with exact flag removes flask and flask dependencies.
let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("anyio==3.7.0")?;
// Install requirements file with exact flag removes flask and flask dependencies.
uv_snapshot!(context.filters(), context.pip_install()
.arg("--exact")
.arg("-r")
.arg("requirements.txt"), @r###"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved [N] packages in [TIME]
Uninstalled [N] packages in [TIME]
- blinker==1.7.0
- click==8.1.7
- flask==3.0.2
- itsdangerous==2.1.2
- jinja2==3.1.3
- markupsafe==2.1.5
- werkzeug==3.0.1
"###
);

// Install flask again
uv_snapshot!(context.filters(), context.pip_install()
.arg("flask"), @r###"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved [N] packages in [TIME]
Installed [N] packages in [TIME]
+ blinker==1.7.0
+ click==8.1.7
+ flask==3.0.2
+ itsdangerous==2.1.2
+ jinja2==3.1.3
+ markupsafe==2.1.5
+ werkzeug==3.0.1
"###
);

requirements_txt.write_str(indoc! {r"
anyio==3.7.0
flit_core<4.0.0
"
})?;

// Install requirements file with exact flag installs flit_core and removes flask and flask dependencies.
uv_snapshot!(context.filters(), context.pip_install()
.arg("--exact")
.arg("-r")
Expand All @@ -787,15 +853,13 @@ fn exact_install_removes_extraneous_packages() -> Result<()> {
Prepared [N] packages in [TIME]
Uninstalled [N] packages in [TIME]
Installed [N] packages in [TIME]
+ anyio==3.7.0
- blinker==1.7.0
- click==8.1.7
- flask==3.0.2
+ idna==3.6
+ flit-core==3.9.0
- itsdangerous==2.1.2
- jinja2==3.1.3
- markupsafe==2.1.5
+ sniffio==1.3.1
- werkzeug==3.0.1
"###
);
Expand Down