diff --git a/native_locator/src/common_python.rs b/native_locator/src/common_python.rs index 039965bae502..76ce116ea995 100644 --- a/native_locator/src/common_python.rs +++ b/native_locator/src/common_python.rs @@ -38,6 +38,7 @@ impl Locator for PythonOnPath<'_> { return None; } Some(PythonEnvironment { + display_name: None, name: None, python_executable_path: Some(env.executable.clone()), version: env.version.clone(), diff --git a/native_locator/src/conda.rs b/native_locator/src/conda.rs index 65e0fd073292..8f6ae6f8b507 100644 --- a/native_locator/src/conda.rs +++ b/native_locator/src/conda.rs @@ -549,6 +549,7 @@ fn get_root_python_environment(path: &PathBuf, manager: &EnvManager) -> Option

{ for env in envs { let executable = find_python_binary_path(Path::new(&env.path)); let env = messaging::PythonEnvironment::new( + None, Some(env.name.to_string()), executable.clone(), messaging::PythonEnvironmentCategory::Conda, diff --git a/native_locator/src/homebrew.rs b/native_locator/src/homebrew.rs index 36a8cfb8f46d..34a02fe9845f 100644 --- a/native_locator/src/homebrew.rs +++ b/native_locator/src/homebrew.rs @@ -61,6 +61,7 @@ impl Locator for Homebrew<'_> { } reported.insert(exe.to_string_lossy().to_string()); let env = crate::messaging::PythonEnvironment::new( + None, None, Some(exe.clone()), crate::messaging::PythonEnvironmentCategory::Homebrew, diff --git a/native_locator/src/main.rs b/native_locator/src/main.rs index 3620fe284bda..ee976bf756d2 100644 --- a/native_locator/src/main.rs +++ b/native_locator/src/main.rs @@ -41,24 +41,24 @@ fn main() { let pipenv_locator = pipenv::PipEnv::new(); let mut path_locator = common_python::PythonOnPath::with(&environment); let mut conda_locator = conda::Conda::with(&environment); - let mut pyenv_locator = pyenv::PyEnv::with(&environment, &mut conda_locator); #[cfg(unix)] let mut homebrew_locator = homebrew::Homebrew::with(&environment); #[cfg(windows)] let mut windows_store = windows_store::WindowsStore::with(&environment); #[cfg(windows)] - let mut windows_registry = windows_registry::WindowsRegistry::new(); + let mut windows_registry = windows_registry::WindowsRegistry::with(&mut conda_locator); // Step 1: These environments take precedence over all others. // As they are very specific and guaranteed to be specific type. + #[cfg(windows)] + find_environments(&mut windows_registry, &mut dispatcher); + let mut pyenv_locator = pyenv::PyEnv::with(&environment, &mut conda_locator); find_environments(&mut pyenv_locator, &mut dispatcher); #[cfg(unix)] find_environments(&mut homebrew_locator, &mut dispatcher); find_environments(&mut conda_locator, &mut dispatcher); #[cfg(windows)] - find_environments(&mut windows_registry, &mut dispatcher); - #[cfg(windows)] find_environments(&mut windows_store, &mut dispatcher); // Step 2: Search in some global locations. diff --git a/native_locator/src/messaging.rs b/native_locator/src/messaging.rs index dddcb6e1b595..b79c5fa172ca 100644 --- a/native_locator/src/messaging.rs +++ b/native_locator/src/messaging.rs @@ -94,6 +94,7 @@ pub enum PythonEnvironmentCategory { #[serde(rename_all = "camelCase")] #[derive(Debug)] pub struct PythonEnvironment { + pub display_name: Option, pub name: Option, pub python_executable_path: Option, pub category: PythonEnvironmentCategory, @@ -110,6 +111,7 @@ pub struct PythonEnvironment { impl PythonEnvironment { pub fn new( + display_name: Option, name: Option, python_executable_path: Option, category: PythonEnvironmentCategory, @@ -120,6 +122,7 @@ impl PythonEnvironment { python_run_command: Option>, ) -> Self { Self { + display_name, name, python_executable_path, category, @@ -140,6 +143,7 @@ impl PythonEnvironment { project_path: PathBuf, ) -> Self { Self { + display_name: None, name: None, python_executable_path: python_executable_path.clone(), category: PythonEnvironmentCategory::Pipenv, diff --git a/native_locator/src/pyenv.rs b/native_locator/src/pyenv.rs index 5832718b4c44..a94d63bc4751 100644 --- a/native_locator/src/pyenv.rs +++ b/native_locator/src/pyenv.rs @@ -105,6 +105,7 @@ fn get_pure_python_environment( ) -> Option { let version = get_pyenv_version(&path.file_name().unwrap().to_string_lossy().to_string())?; Some(messaging::PythonEnvironment::new( + None, None, Some(executable.clone()), messaging::PythonEnvironmentCategory::Pyenv, @@ -138,6 +139,7 @@ fn get_virtual_env_environment( let pyenv_cfg = find_and_parse_pyvenv_cfg(executable)?; let folder_name = path.file_name().unwrap().to_string_lossy().to_string(); Some(messaging::PythonEnvironment::new( + None, Some(folder_name), Some(executable.clone()), messaging::PythonEnvironmentCategory::PyenvVirtualEnv, diff --git a/native_locator/src/venv.rs b/native_locator/src/venv.rs index 05ddaf7f7522..9e0161a6df7d 100644 --- a/native_locator/src/venv.rs +++ b/native_locator/src/venv.rs @@ -26,6 +26,7 @@ impl Locator for Venv { fn resolve(&self, env: &PythonEnv) -> Option { if is_venv(&env) { return Some(PythonEnvironment { + display_name: None, name: Some( env.path .clone() diff --git a/native_locator/src/virtualenv.rs b/native_locator/src/virtualenv.rs index 2268a8253b06..04c10e0e0d25 100644 --- a/native_locator/src/virtualenv.rs +++ b/native_locator/src/virtualenv.rs @@ -57,6 +57,7 @@ impl Locator for VirtualEnv { fn resolve(&self, env: &PythonEnv) -> Option { if is_virtualenv(env) { return Some(PythonEnvironment { + display_name: None, name: Some( env.path .clone() diff --git a/native_locator/src/virtualenvwrapper.rs b/native_locator/src/virtualenvwrapper.rs index 9e5d28e9f445..856f6a78d537 100644 --- a/native_locator/src/virtualenvwrapper.rs +++ b/native_locator/src/virtualenvwrapper.rs @@ -80,6 +80,7 @@ impl Locator for VirtualEnvWrapper<'_> { fn resolve(&self, env: &PythonEnv) -> Option { if is_virtualenvwrapper(env, self.environment) { return Some(PythonEnvironment { + display_name: None, name: Some( env.path .clone() diff --git a/native_locator/src/windows_registry.rs b/native_locator/src/windows_registry.rs index ef886b28dbdd..279ef46d173e 100644 --- a/native_locator/src/windows_registry.rs +++ b/native_locator/src/windows_registry.rs @@ -1,16 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#[cfg(windows)] +use crate::conda::CondaLocator; #[cfg(windows)] use crate::locator::{Locator, LocatorResult}; #[cfg(windows)] +use crate::messaging::EnvManager; +#[cfg(windows)] use crate::messaging::{PythonEnvironment, PythonEnvironmentCategory}; #[cfg(windows)] use crate::utils::PythonEnv; #[cfg(windows)] -use winreg::RegKey; -#[cfg(windows)] use std::path::PathBuf; +#[cfg(windows)] +use winreg::RegKey; #[cfg(windows)] fn get_registry_pythons_from_key(hk: &RegKey, company: &str) -> Option> { @@ -19,23 +23,47 @@ fn get_registry_pythons_from_key(hk: &RegKey, company: &str) -> Option 0 { + Some(version) + } else { + None + }, + env_path, + None, + None, + Some(vec![executable.to_string_lossy().to_string()]), + ); + pythons.push(env); + } + } } Some(pythons) @@ -53,39 +81,77 @@ pub fn get_registry_pythons(company: &str) -> Option> { if let Some(hkcu_pythons) = get_registry_pythons_from_key(&hkcu, company) { pythons.extend(hkcu_pythons); } - Some(pythons) } #[cfg(windows)] -pub struct WindowsRegistry {} +pub fn get_registry_pythons_anaconda(conda_locator: &mut dyn CondaLocator) -> LocatorResult { + let hklm = winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE); + let hkcu = winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER); + + let mut pythons = vec![]; + if let Some(hklm_pythons) = get_registry_pythons_from_key(&hklm, "ContinuumAnalytics") { + pythons.extend(hklm_pythons); + } + if let Some(hkcu_pythons) = get_registry_pythons_from_key(&hkcu, "ContinuumAnalytics") { + pythons.extend(hkcu_pythons); + } + + let mut environments: Vec = vec![]; + let mut managers: Vec = vec![]; + + for env in pythons.iter() { + if let Some(env_path) = env.clone().env_path { + if let Some(mut result) = conda_locator.find_in(&env_path) { + environments.append(&mut result.environments); + managers.append(&mut result.managers); + } + } + } + + LocatorResult { + managers, + environments, + } +} #[cfg(windows)] -impl WindowsRegistry { +pub struct WindowsRegistry<'a> { + pub conda_locator: &'a mut dyn CondaLocator, +} + +#[cfg(windows)] +impl WindowsRegistry<'_> { #[allow(dead_code)] - pub fn new() -> WindowsRegistry { - WindowsRegistry {} + pub fn with<'a>(conda_locator: &'a mut impl CondaLocator) -> WindowsRegistry<'a> { + WindowsRegistry { conda_locator } } } #[cfg(windows)] -impl Locator for WindowsRegistry { - fn resolve(&self, env: &PythonEnv) -> Option { +impl Locator for WindowsRegistry<'_> { + fn resolve(&self, _env: &PythonEnv) -> Option { None } fn find(&mut self) -> Option { - let environments = get_registry_pythons("PythonCore")?; - if environments.is_empty() { + let mut environments: Vec = vec![]; + let mut managers: Vec = vec![]; + + let mut result = get_registry_pythons("PythonCore").unwrap_or_default(); + environments.append(&mut result); + + let mut result = get_registry_pythons_anaconda(self.conda_locator) ; + environments.append(&mut result.environments); + managers.append(&mut result.managers); + + if environments.is_empty() && managers.is_empty() { None } else { Some(LocatorResult { - managers: vec![], + managers, environments, }) } } } - -// PythonCore -// ContinuumAnalytics diff --git a/native_locator/src/windows_store.rs b/native_locator/src/windows_store.rs index 2f2a0c2f81ce..5a0fd7b6ebd0 100644 --- a/native_locator/src/windows_store.rs +++ b/native_locator/src/windows_store.rs @@ -55,6 +55,7 @@ impl Locator for WindowsStore<'_> { fn resolve(&self, env: &PythonEnv) -> Option { if is_windows_python_executable(&env.executable) { return Some(PythonEnvironment { + display_name: None, name: None, python_executable_path: Some(env.executable.clone()), version: None, diff --git a/native_locator/tests/common_python_test.rs b/native_locator/tests/common_python_test.rs index a21f3349f38c..06ed5ff7811d 100644 --- a/native_locator/tests/common_python_test.rs +++ b/native_locator/tests/common_python_test.rs @@ -33,6 +33,7 @@ fn find_python_in_path_this() { assert_eq!(environments.len(), 1); let env = PythonEnvironment { + display_name: None, env_manager: None, project_path: None, name: None, diff --git a/native_locator/tests/conda_test.rs b/native_locator/tests/conda_test.rs index 77fed4a2393d..fb3d59e59b5d 100644 --- a/native_locator/tests/conda_test.rs +++ b/native_locator/tests/conda_test.rs @@ -119,6 +119,7 @@ fn finds_two_conda_envs_from_txt() { tool: EnvManagerType::Conda, }; let expected_conda_1 = PythonEnvironment { + display_name: None, name: Some("one".to_string()), project_path: None, python_executable_path: Some(conda_1_exe.clone()), @@ -133,9 +134,10 @@ fn finds_two_conda_envs_from_txt() { "-n".to_string(), "one".to_string(), "python".to_string(), - ]), - }; - let expected_conda_2 = PythonEnvironment { + ]), + }; + let expected_conda_2 = PythonEnvironment { + display_name: None, name: Some("two".to_string()), project_path: None, python_executable_path: Some(conda_2_exe.clone()), diff --git a/native_locator/tests/pyenv_test.rs b/native_locator/tests/pyenv_test.rs index 45df29031fb7..7ad6904867fa 100644 --- a/native_locator/tests/pyenv_test.rs +++ b/native_locator/tests/pyenv_test.rs @@ -105,6 +105,7 @@ fn find_pyenv_envs() { ); let expected_3_9_9 = json!(PythonEnvironment { + display_name: None, project_path: None, name: None, python_executable_path: Some(join_test_paths(&[ @@ -123,20 +124,21 @@ fn find_pyenv_envs() { env_path: Some(join_test_paths(&[ home.to_str().unwrap(), ".pyenv/versions/3.9.9" - ])), - sys_prefix_path: Some(join_test_paths(&[ + ])), + sys_prefix_path: Some(join_test_paths(&[ home.to_str().unwrap(), ".pyenv/versions/3.9.9" ])), env_manager: Some(expected_manager.clone()) }); let expected_virtual_env = PythonEnvironment { + display_name: None, project_path: None, name: Some("my-virtual-env".to_string()), python_executable_path: Some(join_test_paths(&[ home.to_str().unwrap(), ".pyenv/versions/my-virtual-env/bin/python", - ])), + ])), python_run_command: Some(vec![join_test_paths(&[ home.to_str().unwrap(), ".pyenv/versions/my-virtual-env/bin/python", @@ -153,16 +155,17 @@ fn find_pyenv_envs() { sys_prefix_path: Some(join_test_paths(&[ home.to_str().unwrap(), ".pyenv/versions/my-virtual-env", - ])), - env_manager: Some(expected_manager.clone()), - }; - let expected_3_12_1 = PythonEnvironment { + ])), + env_manager: Some(expected_manager.clone()), + }; + let expected_3_12_1 = PythonEnvironment { + display_name: None, project_path: None, name: None, python_executable_path: Some(join_test_paths(&[ home.to_str().unwrap(), ".pyenv/versions/3.12.1/bin/python", - ])), + ])), python_run_command: Some(vec![join_test_paths(&[ home.to_str().unwrap(), ".pyenv/versions/3.12.1/bin/python", @@ -179,10 +182,11 @@ fn find_pyenv_envs() { sys_prefix_path: Some(join_test_paths(&[ home.to_str().unwrap(), ".pyenv/versions/3.12.1", - ])), + ])), env_manager: Some(expected_manager.clone()), }; let expected_3_13_dev = PythonEnvironment { + display_name: None, project_path: None, name: None, python_executable_path: Some(join_test_paths(&[ @@ -205,10 +209,11 @@ fn find_pyenv_envs() { sys_prefix_path: Some(join_test_paths(&[ home.to_str().unwrap(), ".pyenv/versions/3.13-dev", - ])), + ])), env_manager: Some(expected_manager.clone()), }; let expected_3_12_1a3 = PythonEnvironment { + display_name: None, project_path: None, name: None, python_executable_path: Some(join_test_paths(&[