From c24f32742e6df79d0e98fe3e416cc70cfd4bba0e Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Wed, 4 Feb 2026 11:43:08 -0800 Subject: [PATCH 1/2] fix: conda installation detection via PATH lookups --- crates/pet-conda/src/environment_locations.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/pet-conda/src/environment_locations.rs b/crates/pet-conda/src/environment_locations.rs index 94ae308b..e654c060 100644 --- a/crates/pet-conda/src/environment_locations.rs +++ b/crates/pet-conda/src/environment_locations.rs @@ -4,6 +4,7 @@ use crate::{ conda_rc::{get_conda_rc_search_paths, Condarc}, env_variables::EnvVariables, + manager::find_conda_binary, utils::{is_conda_env, is_conda_install}, }; use log::trace; @@ -265,6 +266,14 @@ pub fn get_known_conda_install_locations( ) -> Vec { use pet_fs::path::norm_case; + // First, try to find conda from PATH - this handles conda installations on mapped drives + // and other non-standard locations that aren't in the hardcoded search paths. + let conda_from_path = if conda_executable.is_none() { + find_conda_binary(env_vars) + } else { + None + }; + let user_profile = env_vars.userprofile.clone().unwrap_or_default(); let program_data = env_vars.programdata.clone().unwrap_or_default(); let all_user_profile = env_vars.allusersprofile.clone().unwrap_or_default(); @@ -359,6 +368,10 @@ pub fn get_known_conda_install_locations( if let Some(conda_dir) = get_conda_dir_from_exe(conda_executable) { known_paths.push(conda_dir); } + // Add conda installation found from PATH (handles mapped drives and non-standard locations) + if let Some(conda_dir) = get_conda_dir_from_exe(&conda_from_path) { + known_paths.push(conda_dir); + } known_paths.sort(); known_paths.dedup(); @@ -370,6 +383,14 @@ pub fn get_known_conda_install_locations( env_vars: &EnvVariables, conda_executable: &Option, ) -> Vec { + // First, try to find conda from PATH - this handles conda installations in + // non-standard locations that aren't in the hardcoded search paths. + let conda_from_path = if conda_executable.is_none() { + find_conda_binary(env_vars) + } else { + None + }; + let mut known_paths = vec![ // We need to look in `/anaconda3` and `/miniconda3` as well. PathBuf::from("/anaconda"), @@ -431,6 +452,10 @@ pub fn get_known_conda_install_locations( if let Some(conda_dir) = get_conda_dir_from_exe(conda_executable) { known_paths.push(conda_dir); } + // Add conda installation found from PATH (handles non-standard locations) + if let Some(conda_dir) = get_conda_dir_from_exe(&conda_from_path) { + known_paths.push(conda_dir); + } known_paths.sort(); known_paths.dedup(); known_paths.into_iter().filter(|f| f.exists()).collect() From aece59749c0bad3427fe389d3e4a7938937c6a12 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Wed, 4 Feb 2026 11:46:53 -0800 Subject: [PATCH 2/2] test: conda binary discovery tests for PATH and condabin locations --- .../tests/environment_locations_test.rs | 110 ++++++++++++++++++ crates/pet-conda/tests/manager_test.rs | 81 +++++++++++++ 2 files changed, 191 insertions(+) diff --git a/crates/pet-conda/tests/environment_locations_test.rs b/crates/pet-conda/tests/environment_locations_test.rs index 5aa52065..c1c401e0 100644 --- a/crates/pet-conda/tests/environment_locations_test.rs +++ b/crates/pet-conda/tests/environment_locations_test.rs @@ -95,3 +95,113 @@ fn list_conda_envs_discovers_base_from_another_child_env() { ] ); } + +/// Test that get_known_conda_install_locations discovers conda installations from PATH +/// when no explicit conda_executable is provided. This is important for discovering +/// conda installations on mapped drives and other non-standard locations. +/// Fixes https://github.com/microsoft/python-environment-tools/issues/194 +#[cfg(unix)] +#[test] +fn discovers_conda_install_from_path() { + use common::{create_test_environment, resolve_test_path}; + use pet_conda::env_variables::EnvVariables; + use pet_conda::environment_locations::get_known_conda_install_locations; + use std::collections::HashMap; + + // Set up PATH to include the conda bin directory (simulating conda on a mapped drive) + let anaconda_bin = resolve_test_path(&["unix", "anaconda3-2023.03", "bin"]); + let path_value = anaconda_bin.to_string_lossy().to_string(); + + let mut vars = HashMap::new(); + vars.insert("PATH".to_string(), path_value); + + let env = create_test_environment(vars, None, vec![], None); + let env_vars = EnvVariables::from(&env); + + // Call get_known_conda_install_locations without an explicit conda_executable + let locations = get_known_conda_install_locations(&env_vars, &None); + + // The anaconda3-2023.03 install should be discovered from PATH + let expected_conda_install = resolve_test_path(&["unix", "anaconda3-2023.03"]); + assert!( + locations.contains(&expected_conda_install), + "Expected {:?} to be in {:?}", + expected_conda_install, + locations + ); +} + +/// Test that get_known_conda_install_locations discovers conda installations from condabin in PATH. +/// This simulates the typical Windows Miniforge/Anaconda setup where condabin is added to PATH. +/// Fixes https://github.com/microsoft/python-environment-tools/issues/194 +#[cfg(unix)] +#[test] +fn discovers_conda_install_from_condabin_in_path() { + use common::{create_test_environment, resolve_test_path}; + use pet_conda::env_variables::EnvVariables; + use pet_conda::environment_locations::get_known_conda_install_locations; + use std::collections::HashMap; + + // Set up PATH to include the condabin directory (typical Miniforge/Anaconda setup on Windows) + let anaconda_condabin = resolve_test_path(&["unix", "anaconda3-2023.03", "condabin"]); + let path_value = anaconda_condabin.to_string_lossy().to_string(); + + let mut vars = HashMap::new(); + vars.insert("PATH".to_string(), path_value); + + let env = create_test_environment(vars, None, vec![], None); + let env_vars = EnvVariables::from(&env); + + // Call get_known_conda_install_locations without an explicit conda_executable + let locations = get_known_conda_install_locations(&env_vars, &None); + + // The anaconda3-2023.03 install should be discovered from PATH via condabin + let expected_conda_install = resolve_test_path(&["unix", "anaconda3-2023.03"]); + assert!( + locations.contains(&expected_conda_install), + "Expected {:?} to be in {:?}", + expected_conda_install, + locations + ); +} + +/// Test that when an explicit conda_executable is provided, PATH lookup is skipped. +/// This ensures we don't do unnecessary work when the user has configured a conda path. +#[cfg(unix)] +#[test] +fn skips_path_lookup_when_conda_executable_provided() { + use common::{create_test_environment, resolve_test_path}; + use pet_conda::env_variables::EnvVariables; + use pet_conda::environment_locations::get_known_conda_install_locations; + use std::collections::HashMap; + + // Set up PATH to include a conda directory + let anaconda_bin = resolve_test_path(&["unix", "anaconda3-2023.03", "bin"]); + let path_value = anaconda_bin.to_string_lossy().to_string(); + + let mut vars = HashMap::new(); + vars.insert("PATH".to_string(), path_value); + + let env = create_test_environment(vars, None, vec![], None); + let env_vars = EnvVariables::from(&env); + + // Provide an explicit conda_executable + let conda_executable = Some(resolve_test_path(&[ + "unix", + "anaconda3-2023.03", + "bin", + "conda", + ])); + + // Call get_known_conda_install_locations with an explicit conda_executable + let locations = get_known_conda_install_locations(&env_vars, &conda_executable); + + // The conda install should still be discovered (from the explicit path, not PATH) + let expected_conda_install = resolve_test_path(&["unix", "anaconda3-2023.03"]); + assert!( + locations.contains(&expected_conda_install), + "Expected {:?} to be in {:?}", + expected_conda_install, + locations + ); +} diff --git a/crates/pet-conda/tests/manager_test.rs b/crates/pet-conda/tests/manager_test.rs index f19481ef..25f572f6 100644 --- a/crates/pet-conda/tests/manager_test.rs +++ b/crates/pet-conda/tests/manager_test.rs @@ -50,3 +50,84 @@ fn does_not_find_conda_env_for_bogus_dirs() { assert!(CondaManager::from(&path).is_none()); } + +/// Test that find_conda_binary finds conda from the PATH environment variable. +/// This is important for discovering conda installations on mapped drives and +/// other non-standard locations (fixes https://github.com/microsoft/python-environment-tools/issues/194). +#[cfg(unix)] +#[test] +fn finds_conda_binary_from_path() { + use common::{create_test_environment, resolve_test_path}; + use pet_conda::env_variables::EnvVariables; + use pet_conda::manager::find_conda_binary; + use std::collections::HashMap; + + let anaconda_bin = resolve_test_path(&["unix", "anaconda3-2023.03", "bin"]); + let path_value = anaconda_bin.to_string_lossy().to_string(); + + let mut vars = HashMap::new(); + vars.insert("PATH".to_string(), path_value); + + let env = create_test_environment(vars, None, vec![], None); + let env_vars = EnvVariables::from(&env); + + let conda_binary = find_conda_binary(&env_vars); + + assert!(conda_binary.is_some()); + assert_eq!( + conda_binary.unwrap(), + resolve_test_path(&["unix", "anaconda3-2023.03", "bin", "conda"]) + ); +} + +/// Test that find_conda_binary also works when conda is in the condabin directory +/// (common on Windows with Miniforge/Anaconda where condabin is added to PATH). +#[cfg(unix)] +#[test] +fn finds_conda_binary_from_condabin_path() { + use common::{create_test_environment, resolve_test_path}; + use pet_conda::env_variables::EnvVariables; + use pet_conda::manager::find_conda_binary; + use std::collections::HashMap; + + let anaconda_condabin = resolve_test_path(&["unix", "anaconda3-2023.03", "condabin"]); + let path_value = anaconda_condabin.to_string_lossy().to_string(); + + let mut vars = HashMap::new(); + vars.insert("PATH".to_string(), path_value); + + let env = create_test_environment(vars, None, vec![], None); + let env_vars = EnvVariables::from(&env); + + let conda_binary = find_conda_binary(&env_vars); + + assert!(conda_binary.is_some()); + assert_eq!( + conda_binary.unwrap(), + resolve_test_path(&["unix", "anaconda3-2023.03", "condabin", "conda"]) + ); +} + +/// Test that find_conda_binary returns None when conda is not on PATH. +#[cfg(unix)] +#[test] +fn does_not_find_conda_binary_when_not_on_path() { + use common::{create_test_environment, resolve_test_path}; + use pet_conda::env_variables::EnvVariables; + use pet_conda::manager::find_conda_binary; + use std::collections::HashMap; + + // Use a path that doesn't have conda + let some_other_path = resolve_test_path(&["unix", "bogus_directory"]); + let path_value = some_other_path.to_string_lossy().to_string(); + + let mut vars = HashMap::new(); + vars.insert("PATH".to_string(), path_value); + + let env = create_test_environment(vars, None, vec![], None); + let env_vars = EnvVariables::from(&env); + + let conda_binary = find_conda_binary(&env_vars); + + assert!(conda_binary.is_none()); +}