From 82feff9be4b84a391a797c13ed73cfd385535280 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Fri, 24 Jul 2026 14:24:25 -0700 Subject: [PATCH 1/2] fix: normalize Conda environment cache keys (PR #487) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- crates/pet-conda/src/lib.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/pet-conda/src/lib.rs b/crates/pet-conda/src/lib.rs index 25b745fd..b85b1ff9 100644 --- a/crates/pet-conda/src/lib.rs +++ b/crates/pet-conda/src/lib.rs @@ -172,13 +172,14 @@ impl Conda { where F: FnOnce() -> Option, { + let cache_key = norm_case(path); let fingerprint_before = CondaEnvironmentFingerprint::from_prefix(path); if let Some(fingerprint) = &fingerprint_before { if let Some(cached) = self .environment_info_cache .read() .expect("conda environment info cache lock poisoned") - .get(path) + .get(&cache_key) .filter(|cached| &cached.fingerprint == fingerprint) { return Some(cached.details.clone()); @@ -189,7 +190,7 @@ impl Conda { self.environment_info_cache .write() .expect("conda environment info cache lock poisoned") - .remove(path); + .remove(&cache_key); return None; }; let fingerprint_after = CondaEnvironmentFingerprint::from_prefix(path); @@ -199,14 +200,14 @@ impl Conda { .expect("conda environment info cache lock poisoned"); if fingerprint_before.is_some() && fingerprint_before == fingerprint_after { cache.insert( - path.to_path_buf(), + cache_key, CachedCondaEnvironment { fingerprint: fingerprint_after.expect("fingerprint checked as present"), details: details.clone(), }, ); } else { - cache.remove(path); + cache.remove(&cache_key); } Some(details) @@ -458,7 +459,8 @@ impl Locator for Conda { } let possible_conda_envs = get_conda_environment_paths(&env_vars, &executable); - let active_prefixes: HashSet = possible_conda_envs.iter().cloned().collect(); + let active_prefixes: HashSet = + possible_conda_envs.iter().map(norm_case).collect(); for path in possible_conda_envs { s.spawn(move || { let details = self.get_environment_details(&path)?; From 858860834eb3a65908977842ebe6b3fa102d88e5 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Sun, 26 Jul 2026 19:52:00 -0700 Subject: [PATCH 2/2] fix: preserve requested Conda cache prefixes (PR #490) Add Windows regression coverage for equivalent separator styles while ensuring cache hits return the caller's requested prefix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- crates/pet-conda/src/lib.rs | 41 ++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/crates/pet-conda/src/lib.rs b/crates/pet-conda/src/lib.rs index b85b1ff9..ef084c71 100644 --- a/crates/pet-conda/src/lib.rs +++ b/crates/pet-conda/src/lib.rs @@ -182,7 +182,9 @@ impl Conda { .get(&cache_key) .filter(|cached| &cached.fingerprint == fingerprint) { - return Some(cached.details.clone()); + let mut details = cached.details.clone(); + details.environment.prefix = Some(path.to_path_buf()); + return Some(details); } } @@ -596,4 +598,41 @@ mod tests { fs::remove_dir_all(prefix).unwrap(); } + + #[cfg(windows)] + #[test] + fn environment_info_cache_normalizes_windows_keys() { + static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + + let prefix = std::env::temp_dir().join(format!( + "pet-conda-environment-cache-case-{}-{}", + std::process::id(), + NEXT_ID.fetch_add(1, Ordering::Relaxed) + )); + let conda_meta = prefix.join("conda-meta"); + fs::create_dir_all(&conda_meta).unwrap(); + fs::write(conda_meta.join("history"), "history").unwrap(); + + let alternate_separators = PathBuf::from(prefix.to_string_lossy().replace('\\', "/")); + let environment = EnvironmentApi::new(); + let locator = Conda::from(&environment); + let loads = AtomicUsize::new(0); + + locator + .get_or_load_environment_details(&prefix, || { + loads.fetch_add(1, Ordering::Relaxed); + Some(test_details(&prefix, 1)) + }) + .unwrap(); + let cached = locator + .get_or_load_environment_details(&alternate_separators, || { + panic!("equivalent Windows paths should reuse the cache") + }) + .unwrap(); + + assert_eq!(loads.load(Ordering::Relaxed), 1); + assert_eq!(cached.environment.prefix, Some(alternate_separators)); + + fs::remove_dir_all(prefix).unwrap(); + } }