From 3f98bfc797b3a6eb02baf1a6a3d317bc36702225 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Sat, 11 Apr 2026 23:56:06 -0700 Subject: [PATCH 1/3] fix: tighten Poetry cache path matching (Fixes #398) --- crates/pet-poetry/src/lib.rs | 114 +++++++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 4 deletions(-) diff --git a/crates/pet-poetry/src/lib.rs b/crates/pet-poetry/src/lib.rs index 54c37081..72afe644 100644 --- a/crates/pet-poetry/src/lib.rs +++ b/crates/pet-poetry/src/lib.rs @@ -45,10 +45,7 @@ fn is_poetry_cache_environment(path: &Path) -> bool { // - Linux: ~/.cache/pypoetry/virtualenvs/ // - macOS: ~/Library/Caches/pypoetry/virtualenvs/ // - Windows: %LOCALAPPDATA%\pypoetry\Cache\virtualenvs\ - let path_str = path.to_str().unwrap_or_default(); - - // Check if path contains typical Poetry cache directory structure - if path_str.contains("pypoetry") && path_str.contains("virtualenvs") { + if has_poetry_cache_components(path) { // Further validate by checking if the directory name matches Poetry's naming pattern // Pattern: {name}-{8-char-hash}-py or just .venv if let Some(dir_name) = path.file_name().and_then(|n| n.to_str()) { @@ -63,6 +60,21 @@ fn is_poetry_cache_environment(path: &Path) -> bool { false } +fn has_poetry_cache_components(path: &Path) -> bool { + let mut found_pypoetry = false; + + path.components() + .filter_map(|component| component.as_os_str().to_str()) + .any(|component| { + if component.eq_ignore_ascii_case("pypoetry") { + found_pypoetry = true; + return false; + } + + found_pypoetry && component.eq_ignore_ascii_case("virtualenvs") + }) +} + /// Check if a .venv directory is an in-project Poetry environment /// This is for the case when virtualenvs.in-project = true is set. /// We check if the parent directory has Poetry configuration files. @@ -359,6 +371,100 @@ mod tests { use super::*; use pet_core::os_environment::EnvironmentApi; + fn path_from_components(components: &[&str]) -> PathBuf { + let mut path = PathBuf::new(); + for component in components { + path.push(component); + } + path + } + + #[test] + fn test_poetry_cache_environment_requires_exact_cache_components() { + let path = path_from_components(&[ + "home", + "user", + ".cache", + "pypoetry", + "virtualenvs", + "project-1a2b3c4d-py3.11", + ]); + + assert!(is_poetry_cache_environment(&path)); + } + + #[test] + fn test_poetry_cache_environment_allows_windows_cache_component() { + let path = path_from_components(&[ + "Users", + "user", + "AppData", + "Local", + "pypoetry", + "Cache", + "virtualenvs", + "project-1a2b3c4d-py3.11", + ]); + + assert!(is_poetry_cache_environment(&path)); + } + + #[test] + fn test_poetry_cache_environment_rejects_substring_cache_components() { + let path = path_from_components(&[ + "Users", + "pypoetry_user", + "virtualenvs_backup", + "project-1a2b3c4d-py3.11", + ]); + + assert!(!is_poetry_cache_environment(&path)); + } + + #[test] + fn test_poetry_cache_environment_requires_ordered_cache_components() { + let path = path_from_components(&[ + "home", + "user", + ".cache", + "virtualenvs", + "pypoetry", + "project-1a2b3c4d-py3.11", + ]); + + assert!(!is_poetry_cache_environment(&path)); + } + + #[test] + fn test_poetry_cache_environment_allows_mixed_case_cache_components() { + let path = path_from_components(&[ + "Users", + "user", + "AppData", + "Local", + "PyPoetry", + "Cache", + "VirtualEnvs", + "project-1a2b3c4d-py3.11", + ]); + + assert!(is_poetry_cache_environment(&path)); + } + + #[test] + fn test_poetry_cache_environment_requires_poetry_env_name() { + let path = path_from_components(&[ + "home", + "user", + ".cache", + "pypoetry", + "virtualenvs", + "not-a-poetry-env", + ]); + + assert!(!is_poetry_cache_environment(&path)); + } + #[test] fn test_sync_search_result_from_replaces_cached_result() { let environment = EnvironmentApi::new(); From 98d1437c409908cf2ba82608a81a2596c5d331e5 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Sun, 12 Apr 2026 00:17:15 -0700 Subject: [PATCH 2/3] fix: address Poetry cache review feedback (PR #417) --- crates/pet-poetry/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pet-poetry/src/lib.rs b/crates/pet-poetry/src/lib.rs index 72afe644..89a33c39 100644 --- a/crates/pet-poetry/src/lib.rs +++ b/crates/pet-poetry/src/lib.rs @@ -47,7 +47,7 @@ fn is_poetry_cache_environment(path: &Path) -> bool { // - Windows: %LOCALAPPDATA%\pypoetry\Cache\virtualenvs\ if has_poetry_cache_components(path) { // Further validate by checking if the directory name matches Poetry's naming pattern - // Pattern: {name}-{8-char-hash}-py or just .venv + // Pattern: {name}-{8-char-hash}-py{version} if let Some(dir_name) = path.file_name().and_then(|n| n.to_str()) { // Check for Poetry's hash-based naming: name-XXXXXXXX-py // The hash is 8 characters of base64url encoding From 27d13de4a8df97e64915335e879a5cdffc71a383 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Sun, 12 Apr 2026 00:38:51 -0700 Subject: [PATCH 3/3] fix: address Poetry cache path review feedback (PR #417) --- crates/pet-poetry/src/lib.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/crates/pet-poetry/src/lib.rs b/crates/pet-poetry/src/lib.rs index 89a33c39..861c1edf 100644 --- a/crates/pet-poetry/src/lib.rs +++ b/crates/pet-poetry/src/lib.rs @@ -49,7 +49,7 @@ fn is_poetry_cache_environment(path: &Path) -> bool { // Further validate by checking if the directory name matches Poetry's naming pattern // Pattern: {name}-{8-char-hash}-py{version} if let Some(dir_name) = path.file_name().and_then(|n| n.to_str()) { - // Check for Poetry's hash-based naming: name-XXXXXXXX-py + // Check for Poetry's hash-based naming: name-XXXXXXXX-py. // The hash is 8 characters of base64url encoding if POETRY_ENV_NAME_PATTERN.is_match(dir_name) { return true; @@ -63,16 +63,22 @@ fn is_poetry_cache_environment(path: &Path) -> bool { fn has_poetry_cache_components(path: &Path) -> bool { let mut found_pypoetry = false; - path.components() - .filter_map(|component| component.as_os_str().to_str()) - .any(|component| { - if component.eq_ignore_ascii_case("pypoetry") { - found_pypoetry = true; - return false; - } + for component in path.components() { + let Some(component) = component.as_os_str().to_str() else { + return false; + }; - found_pypoetry && component.eq_ignore_ascii_case("virtualenvs") - }) + if component.eq_ignore_ascii_case("pypoetry") { + found_pypoetry = true; + continue; + } + + if found_pypoetry && component.eq_ignore_ascii_case("virtualenvs") { + return true; + } + } + + false } /// Check if a .venv directory is an in-project Poetry environment