Skip to content

Commit 6749105

Browse files
committed
Honor --refresh when reusing cached tool environments for uvx/uv tool run
1 parent fb5de22 commit 6749105

File tree

2 files changed

+104
-12
lines changed

2 files changed

+104
-12
lines changed

crates/uv/src/commands/project/environment.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::commands::project::{
1010
use crate::printer::Printer;
1111
use crate::settings::ResolverInstallerSettings;
1212

13-
use uv_cache::{Cache, CacheBucket};
13+
use uv_cache::{Cache, CacheBucket, Freshness};
1414
use uv_cache_key::{cache_digest, hash_digest};
1515
use uv_client::BaseClientBuilder;
1616
use uv_configuration::{Concurrency, Constraints, TargetTriple};
@@ -174,10 +174,13 @@ impl CachedEnvironment {
174174
// Search in the content-addressed cache.
175175
let cache_entry = cache.entry(CacheBucket::Environments, interpreter_hash, resolution_hash);
176176

177-
if let Ok(root) = cache.resolve_link(cache_entry.path()) {
178-
if let Ok(environment) = PythonEnvironment::from_root(root, cache) {
179-
return Ok(Self(environment));
180-
}
177+
if cache
178+
.freshness(&cache_entry, None, None)
179+
.is_ok_and(Freshness::is_fresh)
180+
&& let Ok(root) = cache.resolve_link(cache_entry.path())
181+
&& let Ok(environment) = PythonEnvironment::from_root(root, cache)
182+
{
183+
return Ok(Self(environment));
181184
}
182185

183186
// Create the environment in the cache, then relocate it to its content-addressed location.

crates/uv/tests/it/tool_run.rs

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::common::{TestContext, uv_snapshot};
1+
use crate::common::{TestContext, make_project, uv_snapshot};
22
use anyhow::Result;
33
use assert_cmd::prelude::*;
44
use assert_fs::prelude::*;
@@ -734,15 +734,15 @@ fn tool_run_cache() {
734734
Resolved [N] packages in [TIME]
735735
"###);
736736

737-
// Verify that `--refresh` allows cache reuse.
737+
// Verify that `--refresh` invalidates the cached environment.
738738
uv_snapshot!(context.filters(), context.tool_run()
739739
.arg("-p")
740740
.arg("3.12")
741741
.arg("--refresh")
742742
.arg("black")
743743
.arg("--version")
744744
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
745-
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r"
745+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
746746
success: true
747747
exit_code: 0
748748
----- stdout -----
@@ -751,9 +751,17 @@ fn tool_run_cache() {
751751
752752
----- stderr -----
753753
Resolved [N] packages in [TIME]
754-
");
754+
Prepared [N] packages in [TIME]
755+
Installed [N] packages in [TIME]
756+
+ black==24.3.0
757+
+ click==8.1.7
758+
+ mypy-extensions==1.0.0
759+
+ packaging==24.0
760+
+ pathspec==0.12.1
761+
+ platformdirs==4.2.0
762+
"###);
755763

756-
// Verify that `--refresh-package` allows cache reuse.
764+
// Verify that `--refresh-package` invalidates the cached environment.
757765
uv_snapshot!(context.filters(), context.tool_run()
758766
.arg("-p")
759767
.arg("3.12")
@@ -762,7 +770,7 @@ fn tool_run_cache() {
762770
.arg("black")
763771
.arg("--version")
764772
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
765-
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r"
773+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
766774
success: true
767775
exit_code: 0
768776
----- stdout -----
@@ -771,7 +779,15 @@ fn tool_run_cache() {
771779
772780
----- stderr -----
773781
Resolved [N] packages in [TIME]
774-
");
782+
Prepared [N] packages in [TIME]
783+
Installed [N] packages in [TIME]
784+
+ black==24.3.0
785+
+ click==8.1.7
786+
+ mypy-extensions==1.0.0
787+
+ packaging==24.0
788+
+ pathspec==0.12.1
789+
+ platformdirs==4.2.0
790+
"###);
775791

776792
// Verify that varying the interpreter leads to a fresh environment.
777793
uv_snapshot!(context.filters(), context.tool_run()
@@ -847,6 +863,79 @@ fn tool_run_cache() {
847863
"###);
848864
}
849865

866+
#[test]
867+
fn tool_run_refresh_local_project() -> Result<()> {
868+
let context = TestContext::new("3.12").with_filtered_counts();
869+
let tool_dir = context.temp_dir.child("tools");
870+
let bin_dir = context.temp_dir.child("bin");
871+
let project_dir = context.temp_dir.child("local-tool");
872+
873+
make_project(
874+
project_dir.path(),
875+
"local_tool",
876+
indoc! {r#"
877+
[project.scripts]
878+
local-tool = "local_tool:main"
879+
"#},
880+
)?;
881+
project_dir
882+
.child("src")
883+
.child("local_tool")
884+
.child("__init__.py")
885+
.write_str(indoc! {r#"
886+
def main():
887+
print("v1")
888+
"#})?;
889+
890+
uv_snapshot!(context.filters(), context.tool_run()
891+
.arg("--from")
892+
.arg(project_dir.as_os_str())
893+
.arg("local-tool")
894+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
895+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
896+
success: true
897+
exit_code: 0
898+
----- stdout -----
899+
v1
900+
901+
----- stderr -----
902+
Resolved [N] packages in [TIME]
903+
Prepared [N] packages in [TIME]
904+
Installed [N] packages in [TIME]
905+
+ local-tool==0.1.0 (from file://[TEMP_DIR]/local-tool)
906+
"###);
907+
908+
project_dir
909+
.child("src")
910+
.child("local_tool")
911+
.child("__init__.py")
912+
.write_str(indoc! {r#"
913+
def main():
914+
print("v2")
915+
"#})?;
916+
917+
uv_snapshot!(context.filters(), context.tool_run()
918+
.arg("--refresh")
919+
.arg("--from")
920+
.arg(project_dir.as_os_str())
921+
.arg("local-tool")
922+
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
923+
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
924+
success: true
925+
exit_code: 0
926+
----- stdout -----
927+
v2
928+
929+
----- stderr -----
930+
Resolved [N] packages in [TIME]
931+
Prepared [N] packages in [TIME]
932+
Installed [N] packages in [TIME]
933+
+ local-tool==0.1.0 (from file://[TEMP_DIR]/local-tool)
934+
"###);
935+
936+
Ok(())
937+
}
938+
850939
#[test]
851940
fn tool_run_url() {
852941
let context = TestContext::new("3.12").with_filtered_counts();

0 commit comments

Comments
 (0)