diff --git a/crates/pet-conda/src/environment_locations.rs b/crates/pet-conda/src/environment_locations.rs index 4f1743e1..f3aaac1a 100644 --- a/crates/pet-conda/src/environment_locations.rs +++ b/crates/pet-conda/src/environment_locations.rs @@ -17,17 +17,13 @@ use std::{ const APP_NAME: &str = "conda"; -pub fn get_conda_environment_paths( - env_vars: &EnvVariables, - additional_env_dirs: &Vec, -) -> Vec { +pub fn get_conda_environment_paths(env_vars: &EnvVariables) -> Vec { let mut env_paths = thread::scope(|s| { let mut envs = vec![]; for thread in [ s.spawn(|| get_conda_envs_from_environment_txt(env_vars)), s.spawn(|| get_conda_environment_paths_from_conda_rc(env_vars)), s.spawn(|| get_conda_environment_paths_from_known_paths(env_vars)), - s.spawn(|| get_conda_environment_paths_from_additional_paths(additional_env_dirs)), s.spawn(|| get_known_conda_install_locations(env_vars)), ] { if let Ok(mut env_paths) = thread.join() { @@ -121,25 +117,6 @@ fn get_conda_environment_paths_from_known_paths(env_vars: &EnvVariables) -> Vec< env_paths } -fn get_conda_environment_paths_from_additional_paths( - additional_env_dirs: &Vec, -) -> Vec { - let mut env_paths: Vec = vec![]; - for path in additional_env_dirs { - if let Ok(entries) = fs::read_dir(path) { - for entry in entries.filter_map(Result::ok) { - let path = entry.path(); - if path.is_dir() { - env_paths.push(path); - } - } - } - } - env_paths.append(&mut additional_env_dirs.clone()); - trace!("Conda environments in additional paths {:?}", env_paths); - env_paths -} - pub fn get_environments(conda_dir: &Path) -> Vec { let mut envs: Vec = vec![]; diff --git a/crates/pet-conda/src/lib.rs b/crates/pet-conda/src/lib.rs index fcd0ea01..8334e480 100644 --- a/crates/pet-conda/src/lib.rs +++ b/crates/pet-conda/src/lib.rs @@ -11,7 +11,7 @@ use pet_core::{ os_environment::Environment, python_environment::{PythonEnvironment, PythonEnvironmentKind}, reporter::Reporter, - Locator, LocatorResult, + Locator, }; use pet_python_utils::env::PythonEnv; use serde::{Deserialize, Serialize}; @@ -35,7 +35,7 @@ mod telemetry; pub mod utils; pub trait CondaLocator: Send + Sync { - fn find_in(&self, path: &Path) -> Option; + fn find_and_report(&self, reporter: &dyn Reporter, path: &Path); fn find_and_report_missing_envs( &self, reporter: &dyn Reporter, @@ -53,8 +53,6 @@ pub struct CondaTelemetryInfo { } pub struct Conda { - /// Directories where conda environments are found (env_dirs returned from `conda info --json`) - pub env_dirs: Arc>>, pub environments: Arc>>, pub managers: Arc>>, pub env_vars: EnvVariables, @@ -63,12 +61,15 @@ pub struct Conda { impl Conda { pub fn from(env: &dyn Environment) -> Conda { Conda { - env_dirs: Arc::new(Mutex::new(vec![])), environments: Arc::new(Mutex::new(HashMap::new())), managers: Arc::new(Mutex::new(HashMap::new())), env_vars: EnvVariables::from(env), } } + fn clear(&self) { + self.environments.lock().unwrap().clear(); + self.managers.lock().unwrap().clear(); + } } impl CondaLocator for Conda { @@ -80,19 +81,6 @@ impl CondaLocator for Conda { // Look for environments that we couldn't find without spawning conda. let user_provided_conda_exe = conda_executable.is_some(); let conda_info = CondaInfo::from(conda_executable)?; - - // Keep track of these directories for next refresh - // This way we can search these directories again and they will be reported. - { - let mut env_dirs = self.env_dirs.lock().unwrap(); - env_dirs.append(&mut conda_info.envs_dirs.clone()); - conda_info.envs_dirs.iter().for_each(|p| { - if !env_dirs.contains(p) { - env_dirs.push(p.clone()); - } - }); - } - let environments = self.environments.lock().unwrap().clone(); let new_envs = conda_info .envs @@ -133,9 +121,9 @@ impl CondaLocator for Conda { } } - fn find_in(&self, conda_dir: &Path) -> Option { + fn find_and_report(&self, reporter: &dyn Reporter, conda_dir: &Path) { if !is_conda_install(conda_dir) { - return None; + return; } if let Some(manager) = CondaManager::from(conda_dir) { if let Some(conda_dir) = manager.conda_dir.clone() { @@ -147,12 +135,11 @@ impl CondaLocator for Conda { managers.insert(conda_dir.clone(), manager.clone()); drop(managers); - let mut new_environments = vec![]; - // Find all the environments in the conda install folder. (under `envs` folder) for conda_env in get_conda_environments(&get_environments(&conda_dir), &manager.clone().into()) { + // If reported earlier, no point processing this again. let mut environments = self.environments.lock().unwrap(); if environments.contains_key(&conda_env.prefix) { continue; @@ -160,16 +147,11 @@ impl CondaLocator for Conda { let env = conda_env .to_python_environment(Some(conda_dir.clone()), Some(manager.to_manager())); environments.insert(conda_env.prefix.clone(), env.clone()); - new_environments.push(env); + reporter.report_manager(&manager.to_manager()); + reporter.report_environment(&env); } - - return Some(LocatorResult { - environments: new_environments, - managers: vec![manager.to_manager()], - }); } } - None } } @@ -192,7 +174,7 @@ impl Conda { impl Locator for Conda { fn get_name(&self) -> &'static str { - "Conda" + "Conda" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::Conda] @@ -261,15 +243,12 @@ impl Locator for Conda { fn find(&self, reporter: &dyn Reporter) { // if we're calling this again, then clear what ever cache we have. - let mut environments = self.environments.lock().unwrap(); - environments.clear(); - drop(environments); + self.clear(); let env_vars = self.env_vars.clone(); - let additional_paths = self.env_dirs.lock().unwrap().clone(); thread::scope(|s| { // 1. Get a list of all know conda environments file paths - let possible_conda_envs = get_conda_environment_paths(&env_vars, &additional_paths); + let possible_conda_envs = get_conda_environment_paths(&env_vars); for path in possible_conda_envs { s.spawn(move || { // 2. Get the details of the conda environment diff --git a/crates/pet-core/src/lib.rs b/crates/pet-core/src/lib.rs index 81a20177..436b4603 100644 --- a/crates/pet-core/src/lib.rs +++ b/crates/pet-core/src/lib.rs @@ -36,6 +36,9 @@ pub struct Configuration { pub trait Locator: Send + Sync { /// Returns the name of the locator. fn get_name(&self) -> &'static str; + /// Configures the locator with the given configuration. + /// Override this method if you need to have some custom configuration. + /// E.g. storing some of the configuration information in the locator. fn configure(&self, _config: &Configuration) { // } diff --git a/crates/pet-homebrew/src/lib.rs b/crates/pet-homebrew/src/lib.rs index 29b2419e..ce8093e5 100644 --- a/crates/pet-homebrew/src/lib.rs +++ b/crates/pet-homebrew/src/lib.rs @@ -110,7 +110,7 @@ fn from(env: &PythonEnv) -> Option { impl Locator for Homebrew { fn get_name(&self) -> &'static str { - "Homebrew" + "Homebrew" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::Homebrew] diff --git a/crates/pet-linux-global-python/src/lib.rs b/crates/pet-linux-global-python/src/lib.rs index f7dee33d..400f54c6 100644 --- a/crates/pet-linux-global-python/src/lib.rs +++ b/crates/pet-linux-global-python/src/lib.rs @@ -56,7 +56,7 @@ impl Default for LinuxGlobalPython { } impl Locator for LinuxGlobalPython { fn get_name(&self) -> &'static str { - "LinuxGlobalPython" + "LinuxGlobalPython" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::LinuxGlobal] diff --git a/crates/pet-mac-commandlinetools/src/lib.rs b/crates/pet-mac-commandlinetools/src/lib.rs index 64068dbe..1676a3a3 100644 --- a/crates/pet-mac-commandlinetools/src/lib.rs +++ b/crates/pet-mac-commandlinetools/src/lib.rs @@ -29,7 +29,7 @@ impl Default for MacCmdLineTools { } impl Locator for MacCmdLineTools { fn get_name(&self) -> &'static str { - "MacCmdLineTools" + "MacCmdLineTools" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::MacCommandLineTools] diff --git a/crates/pet-mac-python-org/src/lib.rs b/crates/pet-mac-python-org/src/lib.rs index afa5a1f9..76a64eb3 100644 --- a/crates/pet-mac-python-org/src/lib.rs +++ b/crates/pet-mac-python-org/src/lib.rs @@ -28,7 +28,7 @@ impl Default for MacPythonOrg { } impl Locator for MacPythonOrg { fn get_name(&self) -> &'static str { - "MacPythonOrg" + "MacPythonOrg" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::MacPythonOrg] diff --git a/crates/pet-mac-xcode/src/lib.rs b/crates/pet-mac-xcode/src/lib.rs index e273f23f..278a66cd 100644 --- a/crates/pet-mac-xcode/src/lib.rs +++ b/crates/pet-mac-xcode/src/lib.rs @@ -29,7 +29,7 @@ impl Default for MacXCode { } impl Locator for MacXCode { fn get_name(&self) -> &'static str { - "MacXCode" + "MacXCode" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::MacCommandLineTools] diff --git a/crates/pet-pipenv/src/lib.rs b/crates/pet-pipenv/src/lib.rs index c6ac20bb..1738eefd 100644 --- a/crates/pet-pipenv/src/lib.rs +++ b/crates/pet-pipenv/src/lib.rs @@ -72,7 +72,7 @@ impl PipEnv { } impl Locator for PipEnv { fn get_name(&self) -> &'static str { - "PipEnv" + "PipEnv" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::Pipenv] diff --git a/crates/pet-poetry/src/lib.rs b/crates/pet-poetry/src/lib.rs index 0838d0e5..a1502d09 100644 --- a/crates/pet-poetry/src/lib.rs +++ b/crates/pet-poetry/src/lib.rs @@ -3,7 +3,6 @@ use env_variables::EnvVariables; use environment_locations::list_environments; -use log::error; use manager::PoetryManager; use pet_core::{ os_environment::Environment, @@ -15,10 +14,7 @@ use pet_python_utils::env::PythonEnv; use pet_virtualenv::is_virtualenv; use std::{ path::PathBuf, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, - }, + sync::{Arc, Mutex}, }; use telemetry::report_missing_envs; @@ -43,27 +39,31 @@ pub struct Poetry { pub workspace_directories: Arc>>, pub env_vars: EnvVariables, pub poetry_executable: Arc>>, - searched: AtomicBool, search_result: Arc>>, } impl Poetry { pub fn new(environment: &dyn Environment) -> Self { Poetry { - searched: AtomicBool::new(false), search_result: Arc::new(Mutex::new(None)), workspace_directories: Arc::new(Mutex::new(vec![])), env_vars: EnvVariables::from(environment), poetry_executable: Arc::new(Mutex::new(None)), } } + fn clear(&self) { + self.poetry_executable.lock().unwrap().take(); + self.search_result.lock().unwrap().take(); + } pub fn from(environment: &dyn Environment) -> Poetry { Poetry::new(environment) } fn find_with_cache(&self) -> Option { - if self.searched.load(Ordering::Relaxed) { - return self.search_result.lock().unwrap().clone(); + let mut search_result = self.search_result.lock().unwrap(); + if let Some(result) = search_result.clone() { + return Some(result); } + // First find the manager let manager = manager::PoetryManager::find( self.poetry_executable.lock().unwrap().clone(), @@ -76,28 +76,18 @@ impl Poetry { if let Some(manager) = &manager { result.managers.push(manager.to_manager()); } - if let Ok(values) = self.workspace_directories.lock() { - let workspace_dirs = values.clone(); - drop(values); - let envs = list_environments(&self.env_vars, &workspace_dirs.clone(), manager) - .unwrap_or_default(); - result.environments.extend(envs.clone()); - } - match self.search_result.lock().as_mut() { - Ok(search_result) => { - if result.managers.is_empty() && result.environments.is_empty() { - search_result.take(); - None - } else { - search_result.replace(result.clone()); - Some(result) - } - } - Err(err) => { - error!("Failed to cache to Poetry environments: {:?}", err); - None - } + let workspace_dirs = self.workspace_directories.lock().unwrap().clone(); + let envs = list_environments(&self.env_vars, &workspace_dirs, manager).unwrap_or_default(); + result.environments.extend(envs.clone()); + + // Having a value in the search result means that we have already searched for environments + search_result.replace(result.clone()); + + if result.managers.is_empty() && result.environments.is_empty() { + None + } else { + Some(result) } } } @@ -136,7 +126,7 @@ impl PoetryLocator for Poetry { impl Locator for Poetry { fn get_name(&self) -> &'static str { - "Poetry" + "Poetry" // Do not change this name, as this is used in telemetry. } fn configure(&self, config: &Configuration) { if let Some(workspace_directories) = &config.workspace_directories { @@ -174,6 +164,7 @@ impl Locator for Poetry { } fn find(&self, reporter: &dyn Reporter) { + self.clear(); if let Some(result) = self.find_with_cache() { for manager in result.managers { reporter.report_manager(&manager.clone()); diff --git a/crates/pet-pyenv/src/environments.rs b/crates/pet-pyenv/src/environments.rs index ad8466ec..0f2fb51d 100644 --- a/crates/pet-pyenv/src/environments.rs +++ b/crates/pet-pyenv/src/environments.rs @@ -2,22 +2,15 @@ // Licensed under the MIT License. use lazy_static::lazy_static; -use pet_conda::{utils::is_conda_env, CondaLocator}; use pet_core::{ arch::Architecture, manager::EnvManager, python_environment::{PythonEnvironment, PythonEnvironmentBuilder, PythonEnvironmentKind}, - LocatorResult, }; -use pet_python_utils::executable::{find_executable, find_executables}; +use pet_python_utils::executable::find_executables; use pet_python_utils::version; use regex::Regex; -use std::{ - fs, - path::Path, - sync::{Arc, Mutex}, - thread, -}; +use std::path::Path; lazy_static! { // Stable Versions = like 3.10.10 @@ -34,57 +27,6 @@ lazy_static! { .expect("error parsing Version regex for Win32 Python Version in pyenv"); } -pub fn list_pyenv_environments( - manager: &Option, - versions_dir: &Path, - conda_locator: &Arc, -) -> Option { - let envs = Arc::new(Mutex::new(vec![])); - let managers = Arc::new(Mutex::new(vec![])); - - thread::scope(|s| { - if let Ok(reader) = fs::read_dir(versions_dir) { - for path in reader.filter_map(Result::ok).map(|e| e.path()) { - if let Some(executable) = find_executable(&path) { - let path = path.clone(); - let executable = executable.clone(); - let conda_locator = conda_locator.clone(); - let manager = manager.clone(); - let envs = envs.clone(); - let managers = managers.clone(); - s.spawn(move || { - if is_conda_env(&path) { - if let Some(result) = conda_locator.find_in(&path) { - result.environments.iter().for_each(|e| { - envs.lock().unwrap().push(e.clone()); - }); - result.managers.iter().for_each(|e| { - managers.lock().unwrap().push(e.clone()); - }); - } - } else if let Some(env) = - get_virtual_env_environment(&executable, &path, &manager) - { - envs.lock().unwrap().push(env); - } else if let Some(env) = - get_generic_python_environment(&executable, &path, &manager) - { - envs.lock().unwrap().push(env); - } - }); - } - } - } - }); - - let managers = managers.lock().unwrap(); - let envs = envs.lock().unwrap(); - Some(LocatorResult { - managers: managers.clone(), - environments: envs.clone(), - }) -} - pub fn get_generic_python_environment( executable: &Path, path: &Path, diff --git a/crates/pet-pyenv/src/lib.rs b/crates/pet-pyenv/src/lib.rs index 2a2279a7..de63de00 100644 --- a/crates/pet-pyenv/src/lib.rs +++ b/crates/pet-pyenv/src/lib.rs @@ -2,14 +2,17 @@ // Licensed under the MIT License. use std::{ + fs, path::PathBuf, - sync::{Arc, Mutex}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, + thread, }; use env_variables::EnvVariables; -use environments::{ - get_generic_python_environment, get_virtual_env_environment, list_pyenv_environments, -}; +use environments::{get_generic_python_environment, get_virtual_env_environment}; use manager::PyEnvInfo; use pet_conda::{utils::is_conda_env, CondaLocator}; use pet_core::{ @@ -19,7 +22,7 @@ use pet_core::{ reporter::Reporter, Locator, }; -use pet_python_utils::env::PythonEnv; +use pet_python_utils::{env::PythonEnv, executable::find_executable}; pub mod env_variables; mod environment_locations; @@ -29,6 +32,7 @@ mod manager; pub struct PyEnv { pub env_vars: EnvVariables, pub conda_locator: Arc, + found_pyenv: AtomicBool, manager: Arc>>, versions_dir: Arc>>, } @@ -39,17 +43,46 @@ impl PyEnv { conda_locator: Arc, ) -> impl Locator { PyEnv { + found_pyenv: AtomicBool::new(false), env_vars: EnvVariables::from(environment), conda_locator, manager: Arc::new(Mutex::new(None)), versions_dir: Arc::new(Mutex::new(None)), } } + fn clear(&self) { + self.found_pyenv + .store(false, std::sync::atomic::Ordering::Relaxed); + self.manager.lock().unwrap().take(); + self.versions_dir.lock().unwrap().take(); + } + fn get_manager_versions_dir(&self) -> (Option, Option) { + let mut managers = self.manager.lock().unwrap(); + let mut versions = self.versions_dir.lock().unwrap(); + if !self.found_pyenv.load(Ordering::Relaxed) && (managers.is_none() || versions.is_none()) { + let pyenv_info = PyEnvInfo::from(&self.env_vars); + if let Some(ref exe) = pyenv_info.exe { + let version = pyenv_info.version.clone(); + let manager = EnvManager::new(exe.clone(), EnvManagerType::Pyenv, version); + managers.replace(manager); + } else { + managers.take(); + } + if let Some(version_path) = &pyenv_info.versions { + versions.replace(version_path.clone()); + } else { + versions.take(); + } + self.found_pyenv.store(true, Ordering::Relaxed); + } + + (managers.clone(), versions.clone()) + } } impl Locator for PyEnv { fn get_name(&self) -> &'static str { - "PyEnv" + "PyEnv" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![ @@ -77,31 +110,9 @@ impl Locator for PyEnv { // If exe is Scripts/python.exe or bin/python.exe // Then env path is parent of Scripts or bin // & in pyenv case thats a directory inside `versions` folder. - let mut binding_manager = self.manager.lock(); - let managers = binding_manager.as_mut().unwrap(); - let mut binding_versions = self.versions_dir.lock(); - let versions = binding_versions.as_mut().unwrap(); - if managers.is_none() || versions.is_none() { - let pyenv_info = PyEnvInfo::from(&self.env_vars); - let mut manager: Option = None; - if let Some(ref exe) = pyenv_info.exe { - let version = pyenv_info.version.clone(); - manager = Some(EnvManager::new(exe.clone(), EnvManagerType::Pyenv, version)); - } - if let Some(version_path) = &pyenv_info.versions { - versions.replace(version_path.clone()); - } else { - versions.take(); - } - if let Some(manager) = manager { - managers.replace(manager.clone()); - } else { - managers.take(); - } - } - if let Some(versions) = versions.clone() { - let manager = managers.clone(); + let (manager, versions) = self.get_manager_versions_dir(); + if let Some(versions) = versions { if env.executable.starts_with(versions) { let env_path = env.prefix.clone()?; if let Some(env) = get_virtual_env_environment(&env.executable, &env_path, &manager) @@ -118,23 +129,40 @@ impl Locator for PyEnv { } fn find(&self, reporter: &dyn Reporter) { - let pyenv_info = PyEnvInfo::from(&self.env_vars); - let mut manager: Option = None; - if let Some(ref exe) = pyenv_info.exe { - let version = pyenv_info.version.clone(); - let mgr = EnvManager::new(exe.clone(), EnvManagerType::Pyenv, version); - reporter.report_manager(&mgr); - manager = Some(mgr); + self.clear(); + + let (manager, versions) = self.get_manager_versions_dir(); + + if let Some(manager) = &manager { + reporter.report_manager(manager); } - if let Some(ref versions) = &pyenv_info.versions { - if let Some(envs) = list_pyenv_environments(&manager, versions, &self.conda_locator) { - for mgr in envs.managers { - reporter.report_manager(&mgr); - } - for env in envs.environments { - reporter.report_environment(&env); + + if let Some(versions) = versions { + let conda_locator = self.conda_locator.clone(); + thread::scope(|s| { + if let Ok(reader) = fs::read_dir(versions) { + for path in reader.filter_map(Result::ok).map(|e| e.path()) { + let conda_locator = conda_locator.clone(); + let manager = manager.clone(); + let path = path.clone(); + s.spawn(move || { + if let Some(executable) = find_executable(&path) { + if is_conda_env(&path) { + conda_locator.find_and_report(reporter, &path); + } else if let Some(env) = + get_virtual_env_environment(&executable, &path, &manager) + { + reporter.report_environment(&env) + } else if let Some(env) = + get_generic_python_environment(&executable, &path, &manager) + { + reporter.report_environment(&env) + } + } + }); + } } - } + }); } } } diff --git a/crates/pet-venv/src/lib.rs b/crates/pet-venv/src/lib.rs index 8fc94ec0..3ebe808b 100644 --- a/crates/pet-venv/src/lib.rs +++ b/crates/pet-venv/src/lib.rs @@ -39,7 +39,7 @@ impl Default for Venv { } impl Locator for Venv { fn get_name(&self) -> &'static str { - "Venv" + "Venv" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::Venv] diff --git a/crates/pet-virtualenv/src/lib.rs b/crates/pet-virtualenv/src/lib.rs index a836f2cc..a7adb0e3 100644 --- a/crates/pet-virtualenv/src/lib.rs +++ b/crates/pet-virtualenv/src/lib.rs @@ -86,7 +86,7 @@ impl Default for VirtualEnv { impl Locator for VirtualEnv { fn get_name(&self) -> &'static str { - "VirtualEnv" + "VirtualEnv" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::VirtualEnv] diff --git a/crates/pet-virtualenvwrapper/src/lib.rs b/crates/pet-virtualenvwrapper/src/lib.rs index a52ba8d1..d42fd22c 100644 --- a/crates/pet-virtualenvwrapper/src/lib.rs +++ b/crates/pet-virtualenvwrapper/src/lib.rs @@ -30,7 +30,7 @@ impl VirtualEnvWrapper { impl Locator for VirtualEnvWrapper { fn get_name(&self) -> &'static str { - "VirtualEnvWrapper" + "VirtualEnvWrapper" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::VirtualEnvWrapper] diff --git a/crates/pet-windows-registry/src/environments.rs b/crates/pet-windows-registry/src/environments.rs index ece33255..a2139be3 100644 --- a/crates/pet-windows-registry/src/environments.rs +++ b/crates/pet-windows-registry/src/environments.rs @@ -4,6 +4,8 @@ #[cfg(windows)] use pet_conda::CondaLocator; #[cfg(windows)] +use pet_core::reporter::Reporter; +#[cfg(windows)] use pet_core::{ arch::Architecture, manager::EnvManager, @@ -18,7 +20,10 @@ use std::{path::PathBuf, sync::Arc}; use winreg::RegKey; #[cfg(windows)] -pub fn get_registry_pythons(conda_locator: &Arc) -> Option { +pub fn get_registry_pythons( + conda_locator: &Arc, + reporter: &Option<&dyn Reporter>, +) -> LocatorResult { use log::{trace, warn}; let mut environments = vec![]; @@ -45,15 +50,15 @@ pub fn get_registry_pythons(conda_locator: &Arc) -> Option { - if let Some(result) = get_registry_pythons_from_key_for_company( + let result = get_registry_pythons_from_key_for_company( name, &company_key, &company, conda_locator, - ) { - managers.extend(result.managers); - environments.extend(result.environments); - } + reporter, + ); + managers.extend(result.managers); + environments.extend(result.environments); } Err(err) => { warn!( @@ -69,10 +74,10 @@ pub fn get_registry_pythons(conda_locator: &Arc) -> Option, -) -> Option { + reporter: &Option<&dyn Reporter>, +) -> LocatorResult { use log::{trace, warn}; + use pet_conda::utils::is_conda_env; use pet_fs::path::norm_case; - let mut managers: Vec = vec![]; let mut environments = vec![]; // let company_display_name: Option = company_key.get_value("DisplayName").ok(); for installed_python in company_key.enum_keys().filter_map(Result::ok) { @@ -115,24 +121,9 @@ fn get_registry_pythons_from_key_for_company( ); // Possible this is a conda install folder. - if let Some(conda_result) = conda_locator.find_in(&env_path) { - for manager in conda_result.managers { - // let mgr = manager.clone(); - // mgr.company = Some(company.to_string()); - // mgr.company_display_name = company_display_name.clone(); - managers.push(manager.clone()) - } - for env in conda_result.environments { - // let env = env.clone(); - // env.company = Some(company.to_string()); - // env.company_display_name = company_display_name.clone(); - // if let Some(mgr) = env.manager { - // let mut mgr = mgr.clone(); - // // mgr.company = Some(company.to_string()); - // // mgr.company_display_name = company_display_name.clone(); - // env.manager = Some(mgr); - // } - environments.push(env.clone()); + if is_conda_env(&env_path) { + if let Some(reporter) = reporter { + conda_locator.find_and_report(*reporter, &env_path); } continue; } @@ -196,8 +187,10 @@ fn get_registry_pythons_from_key_for_company( None }) .build(); - // env.company = Some(company.to_string()); - // env.company_display_name = company_display_name.clone(); + + if let Some(reporter) = reporter { + reporter.report_environment(&env); + } environments.push(env); } Err(err) => { @@ -217,8 +210,8 @@ fn get_registry_pythons_from_key_for_company( } } - Some(LocatorResult { + LocatorResult { environments, - managers, - }) + managers: vec![], + } } diff --git a/crates/pet-windows-registry/src/lib.rs b/crates/pet-windows-registry/src/lib.rs index c0aa18b3..ee5e0269 100644 --- a/crates/pet-windows-registry/src/lib.rs +++ b/crates/pet-windows-registry/src/lib.rs @@ -4,16 +4,14 @@ #[cfg(windows)] use environments::get_registry_pythons; use pet_conda::{utils::is_conda_env, CondaLocator}; -#[cfg(windows)] -use pet_core::LocatorResult; use pet_core::{ python_environment::{PythonEnvironment, PythonEnvironmentKind}, reporter::Reporter, - Locator, + Locator, LocatorResult, }; use pet_python_utils::env::PythonEnv; use pet_virtualenv::is_virtualenv; -use std::sync::{atomic::AtomicBool, Arc, RwLock}; +use std::sync::{Arc, Mutex}; mod environments; @@ -21,49 +19,38 @@ pub struct WindowsRegistry { #[allow(dead_code)] conda_locator: Arc, #[allow(dead_code)] - searched: AtomicBool, - #[allow(dead_code)] - environments: Arc>>, + search_result: Arc>>, } impl WindowsRegistry { pub fn from(conda_locator: Arc) -> WindowsRegistry { WindowsRegistry { conda_locator, - searched: AtomicBool::new(false), - environments: Arc::new(RwLock::new(vec![])), + search_result: Arc::new(Mutex::new(None)), } } #[cfg(windows)] - fn find_with_cache(&self) -> Option { - use std::sync::atomic::Ordering; - - if self.searched.load(Ordering::Relaxed) { - if let Ok(envs) = self.environments.read() { - return Some(LocatorResult { - environments: envs.clone(), - managers: vec![], - }); - } - } - self.searched.store(false, Ordering::Relaxed); - if let Ok(mut envs) = self.environments.write() { - envs.clear(); - } - let result = get_registry_pythons(&self.conda_locator)?; - if let Ok(mut envs) = self.environments.write() { - envs.clear(); - envs.extend(result.environments.clone()); - self.searched.store(true, Ordering::Relaxed); + fn find_with_cache(&self, reporter: Option<&dyn Reporter>) -> Option { + let mut result = self.search_result.lock().unwrap(); + if let Some(result) = result.clone() { + return Some(result); } - Some(result) + let registry_result = get_registry_pythons(&self.conda_locator, &reporter); + result.replace(registry_result.clone()); + + Some(registry_result) + } + #[cfg(windows)] + fn clear(&self) { + let mut search_result = self.search_result.lock().unwrap(); + search_result.take(); } } impl Locator for WindowsRegistry { fn get_name(&self) -> &'static str { - "WindowsRegistry" + "WindowsRegistry" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::WindowsRegistry] @@ -85,7 +72,7 @@ impl Locator for WindowsRegistry { } } #[cfg(windows)] - if let Some(result) = self.find_with_cache() { + if let Some(result) = self.find_with_cache(None) { // Find the same env here for found_env in result.environments { if let Some(ref python_executable_path) = found_env.executable { @@ -100,18 +87,8 @@ impl Locator for WindowsRegistry { #[cfg(windows)] fn find(&self, reporter: &dyn Reporter) { - self.searched - .store(false, std::sync::atomic::Ordering::Relaxed); - if let Some(result) = self.find_with_cache() { - result - .managers - .iter() - .for_each(|m| reporter.report_manager(m)); - result - .environments - .iter() - .for_each(|e| reporter.report_environment(e)); - } + self.clear(); + let _ = self.find_with_cache(Some(reporter)); } #[cfg(unix)] fn find(&self, _reporter: &dyn Reporter) { diff --git a/crates/pet-windows-store/src/lib.rs b/crates/pet-windows-store/src/lib.rs index aa6d2b22..5fdb5049 100644 --- a/crates/pet-windows-store/src/lib.rs +++ b/crates/pet-windows-store/src/lib.rs @@ -13,8 +13,7 @@ use pet_core::reporter::Reporter; use pet_core::{os_environment::Environment, Locator}; use pet_python_utils::env::PythonEnv; use std::path::Path; -use std::sync::atomic::AtomicBool; -use std::sync::{Arc, RwLock}; +use std::sync::{Arc, Mutex}; pub fn is_windows_app_folder_in_program_files(path: &Path) -> bool { path.to_str().unwrap_or_default().to_string().to_lowercase()[1..] @@ -24,45 +23,36 @@ pub fn is_windows_app_folder_in_program_files(path: &Path) -> bool { pub struct WindowsStore { pub env_vars: EnvVariables, #[allow(dead_code)] - searched: AtomicBool, - #[allow(dead_code)] - environments: Arc>>, + environments: Arc>>>, } impl WindowsStore { pub fn from(environment: &dyn Environment) -> WindowsStore { WindowsStore { - searched: AtomicBool::new(false), env_vars: EnvVariables::from(environment), - environments: Arc::new(RwLock::new(vec![])), + environments: Arc::new(Mutex::new(None)), } } #[cfg(windows)] fn find_with_cache(&self) -> Option> { - use std::sync::atomic::Ordering; - - if self.searched.load(Ordering::Relaxed) { - if let Ok(envs) = self.environments.read() { - return Some(envs.clone()); - } - } - self.searched.store(false, Ordering::Relaxed); - if let Ok(mut envs) = self.environments.write() { - envs.clear(); + let mut environments = self.environments.lock().unwrap(); + if let Some(environments) = environments.clone() { + return Some(environments); } - let environments = list_store_pythons(&self.env_vars)?; - if let Ok(mut envs) = self.environments.write() { - envs.clear(); - envs.extend(environments.clone()); - self.searched.store(true, Ordering::Relaxed); - } - Some(environments) + + let envs = list_store_pythons(&self.env_vars).unwrap_or_default(); + environments.replace(envs.clone()); + Some(envs) + } + #[cfg(windows)] + fn clear(&self) { + self.environments.lock().unwrap().take(); } } impl Locator for WindowsStore { fn get_name(&self) -> &'static str { - "WindowsStore" + "WindowsStore" // Do not change this name, as this is used in telemetry. } fn supported_categories(&self) -> Vec { vec![PythonEnvironmentKind::WindowsStore] @@ -97,15 +87,12 @@ impl Locator for WindowsStore { #[cfg(windows)] fn find(&self, reporter: &dyn Reporter) { - self.searched - .store(false, std::sync::atomic::Ordering::Relaxed); + self.clear(); if let Some(environments) = self.find_with_cache() { environments .iter() .for_each(|e| reporter.report_environment(e)) } - self.searched - .store(true, std::sync::atomic::Ordering::Relaxed); } #[cfg(unix)]