diff --git a/crates/pet-conda/src/environment_locations.rs b/crates/pet-conda/src/environment_locations.rs index e276e457..f281c49c 100644 --- a/crates/pet-conda/src/environment_locations.rs +++ b/crates/pet-conda/src/environment_locations.rs @@ -90,10 +90,8 @@ fn get_conda_environment_paths_from_known_paths(env_vars: &EnvVariables) -> Vec< if let Ok(entries) = fs::read_dir(full_path) { for entry in entries.filter_map(Result::ok) { let path = entry.path(); - if let Ok(meta) = fs::metadata(&path) { - if meta.is_dir() { - env_paths.push(path); - } + if path.is_dir() { + env_paths.push(path); } } } @@ -112,10 +110,8 @@ fn get_conda_environment_paths_from_additional_paths( if let Ok(entries) = fs::read_dir(path) { for entry in entries.filter_map(Result::ok) { let path = entry.path(); - if let Ok(meta) = fs::metadata(&path) { - if meta.is_dir() { - env_paths.push(path); - } + if path.is_dir() { + env_paths.push(path); } } } @@ -146,7 +142,7 @@ pub fn get_environments(conda_dir: &Path) -> Vec { } } else if is_conda_env(conda_dir) { envs.push(conda_dir.to_path_buf()); - } else if fs::metadata(conda_dir.join("envs")).is_ok() { + } else if conda_dir.join("envs").exists() { // This could be a directory where conda environments are stored. // I.e. its not necessarily the root conda install directory. // E.g. C:\Users\donjayamanne\.conda diff --git a/crates/pet-conda/src/environments.rs b/crates/pet-conda/src/environments.rs index 7b29b0a4..f2028aba 100644 --- a/crates/pet-conda/src/environments.rs +++ b/crates/pet-conda/src/environments.rs @@ -14,10 +14,7 @@ use pet_core::{ }; use pet_fs::path::{norm_case, resolve_symlink}; use pet_python_utils::executable::{find_executable, find_executables}; -use std::{ - fs, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; #[derive(Debug, Clone)] pub struct CondaEnvironment { @@ -84,7 +81,7 @@ pub fn get_conda_environment_info( None => get_conda_installation_used_to_create_conda_env(env_path), }; if let Some(conda_dir) = &conda_install_folder { - if fs::metadata(conda_dir).is_err() { + if !conda_dir.exists() { warn!( "Conda install folder {}, does not exist, hence will not be used for the Conda Env: {}", env_path.display(), diff --git a/crates/pet-conda/src/manager.rs b/crates/pet-conda/src/manager.rs index 4073c039..ffeca204 100644 --- a/crates/pet-conda/src/manager.rs +++ b/crates/pet-conda/src/manager.rs @@ -23,7 +23,7 @@ fn get_conda_executable(path: &Path) -> Option { for relative_path in relative_path_to_conda_exe { let exe = path.join(&relative_path); - if exe.metadata().is_ok() { + if exe.exists() { return Some(exe); } } @@ -49,10 +49,8 @@ pub fn find_conda_binary(env_vars: &EnvVariables) -> Option { for path in env::split_paths(&paths) { for bin in get_conda_bin_names() { let conda_path = path.join(bin); - if let Ok(metadata) = std::fs::metadata(&conda_path) { - if metadata.is_file() || metadata.is_symlink() { - return Some(conda_path); - } + if conda_path.is_file() || conda_path.is_symlink() { + return Some(conda_path); } } } diff --git a/crates/pet-conda/src/utils.rs b/crates/pet-conda/src/utils.rs index 46e93833..b6fcdc94 100644 --- a/crates/pet-conda/src/utils.rs +++ b/crates/pet-conda/src/utils.rs @@ -1,25 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -use std::{ - fs, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; /// conda-meta must exist as this contains a mandatory `history` file. pub fn is_conda_install(path: &Path) -> bool { - (path.join("condabin").metadata().is_ok() || path.join("envs").metadata().is_ok()) - && path.join("conda-meta").metadata().is_ok() + (path.join("condabin").exists() || path.join("envs").exists()) + && path.join("conda-meta").exists() } /// conda-meta must exist as this contains a mandatory `history` file. /// The root conda installation folder is also a conda environment (its the base environment). pub fn is_conda_env(path: &Path) -> bool { - if let Ok(metadata) = fs::metadata(path.join("conda-meta")) { - metadata.is_dir() - } else { - false - } + path.join("conda-meta").is_dir() } /// Only used in tests, noop in production. diff --git a/crates/pet-core/src/os_environment.rs b/crates/pet-core/src/os_environment.rs index 7a8b6ef7..6fa0b44f 100644 --- a/crates/pet-core/src/os_environment.rs +++ b/crates/pet-core/src/os_environment.rs @@ -10,7 +10,7 @@ use std::{ use log::trace; use pet_fs::path::norm_case; -pub trait Environment { +pub trait Environment: Send + Sync { fn get_user_home(&self) -> Option; /// Only used in tests, None in production. #[allow(dead_code)] diff --git a/crates/pet-core/src/python_environment.rs b/crates/pet-core/src/python_environment.rs index 6326b851..095de101 100644 --- a/crates/pet-core/src/python_environment.rs +++ b/crates/pet-core/src/python_environment.rs @@ -114,7 +114,14 @@ impl PythonEnvironment { impl std::fmt::Display for PythonEnvironment { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - writeln!(f, "Environment ({:?})", self.kind).unwrap_or_default(); + writeln!( + f, + "Environment ({})", + self.kind + .map(|v| format!("{v:?}")) + .unwrap_or("Unknown".to_string()) + ) + .unwrap_or_default(); if let Some(name) = &self.display_name { writeln!(f, " Display-Name: {name}").unwrap_or_default(); } diff --git a/crates/pet-global-virtualenvs/src/lib.rs b/crates/pet-global-virtualenvs/src/lib.rs index 5241cc53..4e3763e3 100644 --- a/crates/pet-global-virtualenvs/src/lib.rs +++ b/crates/pet-global-virtualenvs/src/lib.rs @@ -14,12 +14,12 @@ fn get_global_virtualenv_dirs( if let Some(work_on_home) = work_on_home_env_var { let work_on_home = norm_case(PathBuf::from(work_on_home)); - if fs::metadata(&work_on_home).is_ok() { + if work_on_home.exists() { venv_dirs.push(work_on_home); } else if let Some(home) = &user_home { if let Ok(work_on_home) = work_on_home.strip_prefix("~") { let work_on_home = home.join(work_on_home); - if fs::metadata(&work_on_home).is_ok() { + if work_on_home.exists() { venv_dirs.push(work_on_home); } } @@ -28,7 +28,7 @@ fn get_global_virtualenv_dirs( // Used by pipenv (https://github.com/pypa/pipenv/blob/main/pipenv/utils/shell.py#L184) if let Some(xdg_data_home) = xdg_data_home.map(|d| PathBuf::from(d).join("virtualenvs")) { - if fs::metadata(&xdg_data_home).is_ok() { + if xdg_data_home.exists() { venv_dirs.push(xdg_data_home); } } @@ -41,7 +41,7 @@ fn get_global_virtualenv_dirs( PathBuf::from(".local").join("share").join("virtualenvs"), // Used by pipenv (https://github.com/pypa/pipenv/blob/main/pipenv/utils/shell.py#L184) ] { let venv_dir = home.join(dir); - if fs::metadata(&venv_dir).is_ok() { + if venv_dir.exists() { venv_dirs.push(venv_dir); } } @@ -49,11 +49,11 @@ fn get_global_virtualenv_dirs( // https://virtualenvwrapper.readthedocs.io/en/latest/index.html // Default recommended location for virtualenvwrapper let envs = PathBuf::from("Envs"); - if fs::metadata(&envs).is_ok() { + if envs.exists() { venv_dirs.push(envs); } let envs = PathBuf::from("envs"); - if fs::metadata(&envs).is_ok() { + if envs.exists() { venv_dirs.push(envs); } } diff --git a/crates/pet-homebrew/src/environment_locations.rs b/crates/pet-homebrew/src/environment_locations.rs index c6af2d59..08e88e91 100644 --- a/crates/pet-homebrew/src/environment_locations.rs +++ b/crates/pet-homebrew/src/environment_locations.rs @@ -4,7 +4,7 @@ use crate::env_variables::EnvVariables; use lazy_static::lazy_static; use regex::Regex; -use std::{fs, path::PathBuf}; +use std::path::PathBuf; lazy_static! { static ref PYTHON_VERSION: Regex = @@ -41,9 +41,7 @@ pub fn get_homebrew_prefix_bin(env_vars: &EnvVariables) -> Vec { // Check the environment variables if let Some(homebrew_prefix) = &env_vars.homebrew_prefix { let homebrew_prefix_bin = PathBuf::from(homebrew_prefix).join("bin"); - if fs::metadata(&homebrew_prefix_bin).is_ok() - && !homebrew_prefixes.contains(&homebrew_prefix_bin) - { + if homebrew_prefix_bin.exists() && !homebrew_prefixes.contains(&homebrew_prefix_bin) { homebrew_prefixes.push(homebrew_prefix_bin); } } diff --git a/crates/pet-pipenv/src/lib.rs b/crates/pet-pipenv/src/lib.rs index 53e354db..c6ac20bb 100644 --- a/crates/pet-pipenv/src/lib.rs +++ b/crates/pet-pipenv/src/lib.rs @@ -37,7 +37,7 @@ fn get_pipenv_project_from_prefix(prefix: &Path) -> Option { let project_file = prefix.join(".project"); let contents = fs::read_to_string(project_file).ok()?; let project_folder = norm_case(PathBuf::from(contents.trim().to_string())); - if fs::metadata(&project_folder).is_ok() { + if project_folder.exists() { Some(project_folder) } else { None @@ -46,14 +46,14 @@ fn get_pipenv_project_from_prefix(prefix: &Path) -> Option { fn is_pipenv(env: &PythonEnv, env_vars: &EnvVariables) -> bool { if let Some(project_path) = get_pipenv_project(env) { - if fs::metadata(project_path.join(env_vars.pipenv_pipfile.clone())).is_ok() { + if project_path.join(env_vars.pipenv_pipfile.clone()).exists() { return true; } } // If we have a Pipfile, then this is a pipenv environment. // Else likely a virtualenvwrapper or the like. if let Some(project_path) = get_pipenv_project(env) { - fs::metadata(project_path.join(env_vars.pipenv_pipfile.clone())).is_ok() + project_path.join(env_vars.pipenv_pipfile.clone()).exists() } else { false } diff --git a/crates/pet-pyenv/src/environment_locations.rs b/crates/pet-pyenv/src/environment_locations.rs index e99cef26..d73c4024 100644 --- a/crates/pet-pyenv/src/environment_locations.rs +++ b/crates/pet-pyenv/src/environment_locations.rs @@ -3,7 +3,7 @@ use crate::env_variables::EnvVariables; use pet_fs::path::norm_case; -use std::{fs, path::PathBuf}; +use std::path::PathBuf; #[cfg(windows)] pub fn get_home_pyenv_dir(env_vars: &EnvVariables) -> Option { @@ -24,10 +24,8 @@ pub fn get_binary_from_known_paths(env_vars: &EnvVariables) -> Option { } else { known_path.join("pyenv") }; - if let Ok(metadata) = fs::metadata(&exe) { - if metadata.is_file() { - return Some(norm_case(exe)); - } + if exe.is_file() { + return Some(norm_case(exe)); } } None diff --git a/crates/pet-pyenv/src/manager.rs b/crates/pet-pyenv/src/manager.rs index 1efcfee9..df3fc04d 100644 --- a/crates/pet-pyenv/src/manager.rs +++ b/crates/pet-pyenv/src/manager.rs @@ -41,11 +41,11 @@ fn get_pyenv_info(environment: &EnvVariables) -> PyEnvInfo { }; if let Some(dir) = get_pyenv_dir(environment) { let versions = dir.join("versions"); - if fs::metadata(&versions).is_ok() { + if versions.exists() { pyenv.versions = Some(versions); } let exe = dir.join("bin").join("pyenv"); - if fs::metadata(&exe).is_ok() { + if exe.exists() { pyenv.exe = Some(exe); } } @@ -57,13 +57,13 @@ fn get_pyenv_info(environment: &EnvVariables) -> PyEnvInfo { if let Some(path) = get_home_pyenv_dir(environment) { if pyenv.exe.is_none() { let exe = path.join("bin").join("pyenv"); - if fs::metadata(&exe).is_ok() { + if exe.exists() { pyenv.exe = Some(exe); } } if pyenv.versions.is_none() { let versions = path.join("versions"); - if fs::metadata(&versions).is_ok() { + if versions.exists() { pyenv.versions = Some(versions); } } diff --git a/crates/pet-python-utils/src/executable.rs b/crates/pet-python-utils/src/executable.rs index cbabd6c2..49c88772 100644 --- a/crates/pet-python-utils/src/executable.rs +++ b/crates/pet-python-utils/src/executable.rs @@ -25,7 +25,7 @@ pub fn find_executable(env_path: &Path) -> Option { env_path.join("python3.exe"), ] .into_iter() - .find(|path| fs::metadata(path).is_ok()) + .find(|path| path.exists()) } #[cfg(unix)] @@ -37,7 +37,7 @@ pub fn find_executable(env_path: &Path) -> Option { env_path.join("python3"), ] .into_iter() - .find(|path| fs::metadata(path).is_ok()) + .find(|path| path.exists()) } pub fn find_executables>(env_path: T) -> Vec { @@ -48,7 +48,7 @@ pub fn find_executables>(env_path: T) -> Vec { let mut python_executables = vec![]; let bin = if cfg!(windows) { "Scripts" } else { "bin" }; let mut env_path = env_path.as_ref().to_path_buf(); - if env_path.join(bin).metadata().is_ok() { + if env_path.join(bin).exists() { env_path = env_path.join(bin); } @@ -72,18 +72,16 @@ pub fn find_executables>(env_path: T) -> Vec { // If you install python@3.10, then only a python3.10 exe is created in that bin directory. // As a compromise, we only enumerate if this is a bin directory and there are no python exes // Else enumerating entire directories is very expensive. - if env_path.join(python_exe).metadata().is_ok() - || env_path.join(python3_exe).metadata().is_ok() + if env_path.join(python_exe).exists() + || env_path.join(python3_exe).exists() || env_path.ends_with(bin) { // Enumerate this directory and get all `python` & `pythonX.X` files. if let Ok(entries) = fs::read_dir(env_path) { for entry in entries.filter_map(Result::ok) { let file = entry.path(); - if let Ok(metadata) = fs::metadata(&file) { - if is_python_executable_name(&entry.path()) && metadata.is_file() { - python_executables.push(file); - } + if file.is_file() && is_python_executable_name(&file) { + python_executables.push(file); } } } diff --git a/crates/pet-python-utils/src/headers.rs b/crates/pet-python-utils/src/headers.rs index c454fc92..ebdd4fc4 100644 --- a/crates/pet-python-utils/src/headers.rs +++ b/crates/pet-python-utils/src/headers.rs @@ -41,7 +41,7 @@ pub fn get_version(path: &Path) -> Option { let mut contents = "".to_string(); if let Ok(result) = fs::read_to_string(patchlevel_h) { contents = result; - } else if fs::metadata(&headers_path).is_err() { + } else if !headers_path.exists() { // TODO: Remove this check, unnecessary, as we try to read the dir below. // Such a path does not exist, get out. continue; diff --git a/crates/pet-python-utils/src/pyvenv_cfg.rs b/crates/pet-python-utils/src/pyvenv_cfg.rs index a36e2d17..01f06015 100644 --- a/crates/pet-python-utils/src/pyvenv_cfg.rs +++ b/crates/pet-python-utils/src/pyvenv_cfg.rs @@ -44,14 +44,14 @@ fn find(path: &Path) -> Option { // Check if the pyvenv.cfg file is in the current directory. // Possible the passed value is the `env`` directory. let cfg = path.join(PYVENV_CONFIG_FILE); - if fs::metadata(&cfg).is_ok() { + if cfg.exists() { return Some(cfg); } let bin = if cfg!(windows) { "Scripts" } else { "bin" }; if path.ends_with(bin) { let cfg = path.parent()?.join(PYVENV_CONFIG_FILE); - if fs::metadata(&cfg).is_ok() { + if cfg.exists() { return Some(cfg); } } diff --git a/crates/pet-venv/src/lib.rs b/crates/pet-venv/src/lib.rs index 46253428..8fc94ec0 100644 --- a/crates/pet-venv/src/lib.rs +++ b/crates/pet-venv/src/lib.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use std::path::Path; + use pet_core::{ python_environment::{PythonEnvironment, PythonEnvironmentBuilder, PythonEnvironmentKind}, reporter::Reporter, @@ -20,6 +22,9 @@ fn is_venv_internal(env: &PythonEnv) -> Option { pub fn is_venv(env: &PythonEnv) -> bool { is_venv_internal(env).unwrap_or_default() } +pub fn is_venv_dir(path: &Path) -> bool { + PyVenvCfg::find(path).is_some() +} pub struct Venv {} impl Venv { diff --git a/crates/pet-virtualenv/src/lib.rs b/crates/pet-virtualenv/src/lib.rs index 7e5e11af..d67d3c30 100644 --- a/crates/pet-virtualenv/src/lib.rs +++ b/crates/pet-virtualenv/src/lib.rs @@ -8,7 +8,6 @@ use pet_core::{ }; use pet_python_utils::version; use pet_python_utils::{env::PythonEnv, executable::find_executables}; -use std::fs; pub fn is_virtualenv(env: &PythonEnv) -> bool { if env.prefix.is_none() { @@ -31,9 +30,7 @@ pub fn is_virtualenv(env: &PythonEnv) -> bool { // const directory = path.dirname(interpreterPath); // const files = await fsapi.readdir(directory); // const regex = /^activate(\.([A-z]|\d)+)?$/i; - if fs::metadata(bin.join("activate")).is_ok() - || fs::metadata(bin.join("activate.bat")).is_ok() - { + if bin.join("activate").exists() || bin.join("activate.bat").exists() { return true; } diff --git a/crates/pet-virtualenvwrapper/src/environment_locations.rs b/crates/pet-virtualenvwrapper/src/environment_locations.rs index 4246edf3..72c2e6c2 100644 --- a/crates/pet-virtualenvwrapper/src/environment_locations.rs +++ b/crates/pet-virtualenvwrapper/src/environment_locations.rs @@ -3,7 +3,7 @@ use crate::env_variables::EnvVariables; use pet_fs::path::norm_case; -use std::{fs, path::PathBuf}; +use std::path::PathBuf; #[cfg(windows)] fn get_default_virtualenvwrapper_path(env_vars: &EnvVariables) -> Option { @@ -13,11 +13,11 @@ fn get_default_virtualenvwrapper_path(env_vars: &EnvVariables) -> Option Option Option { - use std::fs; - if let Some(home) = &env_vars.home { let home = home.join(".virtualenvs"); - if fs::metadata(&home).is_ok() { + if home.exists() { return Some(norm_case(&home)); } } @@ -43,7 +41,7 @@ pub fn get_work_on_home_path(environment: &EnvVariables) -> Option { if let Some(work_on_home) = &environment.workon_home { // TODO: Why do we need to canonicalize the path? if let Ok(work_on_home) = std::fs::canonicalize(work_on_home) { - if fs::metadata(&work_on_home).is_ok() { + if work_on_home.exists() { return Some(norm_case(&work_on_home)); } } diff --git a/crates/pet/src/find.rs b/crates/pet/src/find.rs index 70516290..3acbf122 100644 --- a/crates/pet/src/find.rs +++ b/crates/pet/src/find.rs @@ -3,16 +3,16 @@ use log::{trace, warn}; use pet_conda::CondaLocator; -use pet_core::os_environment::{Environment, EnvironmentApi}; +use pet_core::os_environment::Environment; use pet_core::reporter::Reporter; use pet_core::{Configuration, Locator}; use pet_env_var_path::get_search_paths_from_env_variables; use pet_global_virtualenvs::list_global_virtual_envs_paths; -use pet_poetry::Poetry; use pet_python_utils::env::PythonEnv; use pet_python_utils::executable::{ find_executable, find_executables, should_search_for_environments_in_path, }; +use pet_venv::is_venv_dir; use std::collections::BTreeMap; use std::fs; use std::path::PathBuf; @@ -36,6 +36,7 @@ pub fn find_and_report_envs( configuration: Configuration, locators: &Arc>>, conda_locator: Arc, + environment: &dyn Environment, ) -> Arc> { let summary = Arc::new(Mutex::new(Summary { time: Duration::from_secs(0), @@ -81,22 +82,20 @@ pub fn find_and_report_envs( conda_locator.find_with_conda_executable(conda_executable); Some(()) }); - // By now all poetry envs have been found - // Spawn poetry exe in a separate thread. - // & see if we can find more environments by spawning poetry. - // But we will not wait for this to complete. - thread::spawn(move || { - let env = EnvironmentApi::new(); - Poetry::new(&env).find_with_executable(); - Some(()) - }); + // // By now all poetry envs have been found + // // Spawn poetry exe in a separate thread. + // // & see if we can find more environments by spawning poetry. + // // But we will not wait for this to complete. + // thread::spawn(move || { + // Poetry::new(os_environment).find_with_executable(); + // Some(()) + // }); }); // Step 2: Search in PATH variable s.spawn(|| { let start = std::time::Instant::now(); - let environment = EnvironmentApi::new(); let global_env_search_paths: Vec = - get_search_paths_from_env_variables(&environment); + get_search_paths_from_env_variables(environment); trace!( "Searching for environments in global folders: {:?}", @@ -115,7 +114,6 @@ pub fn find_and_report_envs( // Step 3: Search in some global locations for virtual envs. s.spawn(|| { let start = std::time::Instant::now(); - let environment = EnvironmentApi::new(); let search_paths: Vec = [ list_global_virtual_envs_paths( environment.get_env_var("WORKON_HOME".into()), @@ -126,7 +124,7 @@ pub fn find_and_report_envs( ] .concat(); let global_env_search_paths: Vec = - get_search_paths_from_env_variables(&environment); + get_search_paths_from_env_variables(environment); trace!( "Searching for environments in global venv folders: {:?}", @@ -179,17 +177,18 @@ fn find_python_environments_in_workspace_folders_recursive( ) { thread::scope(|s| { s.spawn(|| { - let bin = if cfg!(windows) { "Scripts" } else { "bin" }; for workspace_folder in workspace_folders { + let paths_to_search_first = vec![ + // Possible this is a virtual env + workspace_folder.clone(), + // Optimize for finding these first. + workspace_folder.join(".venv"), + workspace_folder.join(".conda"), + workspace_folder.join(".virtualenv"), + workspace_folder.join("venv"), + ]; find_python_environments_in_paths_with_locators( - vec![ - // Possible this is a virtual env - workspace_folder.clone(), - // Optimize for finding these first. - workspace_folder.join(".venv"), - // Optimize for finding these first. - workspace_folder.join(".conda"), - ], + paths_to_search_first.clone(), locators, reporter, true, @@ -197,9 +196,8 @@ fn find_python_environments_in_workspace_folders_recursive( Some(workspace_folder.clone()), ); - if workspace_folder.join(bin).exists() { - // If the folder has a bin or scripts, then ignore it, its most likely an env. - // I.e. no point looking for python environments in a Python environment. + // If this is a virtual env folder, no need to scan this. + if is_venv_dir(&workspace_folder) { continue; } @@ -209,6 +207,7 @@ fn find_python_environments_in_workspace_folders_recursive( .filter(|d| d.file_type().is_ok_and(|f| f.is_dir())) .map(|p| p.path()) .filter(should_search_for_environments_in_path) + .filter(|p| !paths_to_search_first.contains(p)) { find_python_environments( vec![folder], diff --git a/crates/pet/src/jsonrpc.rs b/crates/pet/src/jsonrpc.rs index 0293fc1d..1f06e918 100644 --- a/crates/pet/src/jsonrpc.rs +++ b/crates/pet/src/jsonrpc.rs @@ -4,7 +4,11 @@ use log::{error, info, trace}; use pet::resolve::resolve_environment; use pet_conda::Conda; -use pet_core::{os_environment::EnvironmentApi, reporter::Reporter, Configuration, Locator}; +use pet_core::{ + os_environment::{Environment, EnvironmentApi}, + reporter::Reporter, + Configuration, Locator, +}; use pet_jsonrpc::{ send_error, send_reply, server::{start_server, HandlersKeyedByMethodName}, @@ -14,6 +18,7 @@ use pet_telemetry::report_inaccuracies_identified_after_resolving; use serde::{Deserialize, Serialize}; use serde_json::{self, Value}; use std::{ + ops::Deref, path::PathBuf, sync::{Arc, RwLock}, thread, @@ -27,6 +32,7 @@ pub struct Context { configuration: RwLock, locators: Arc>>, conda_locator: Arc, + os_environment: Arc, } pub fn start_jsonrpc_server() { @@ -40,9 +46,10 @@ pub fn start_jsonrpc_server() { let conda_locator = Arc::new(Conda::from(&environment)); let context = Context { reporter, - locators: create_locators(conda_locator.clone()), + locators: create_locators(conda_locator.clone(), &environment), conda_locator, configuration: RwLock::new(Configuration::default()), + os_environment: Arc::new(environment), }; let mut handlers = HandlersKeyedByMethodName::new(Arc::new(context)); @@ -97,6 +104,7 @@ pub fn handle_refresh(context: Arc, id: u32, params: Value) { config, &context.locators, context.conda_locator.clone(), + context.os_environment.deref(), ); let summary = summary.lock().unwrap(); for locator in summary.find_locators_times.iter() { @@ -143,12 +151,16 @@ pub fn handle_resolve(context: Arc, id: u32, params: Value) { .project_directories; let search_paths = search_paths.unwrap_or_default(); // Start in a new thread, we can have multiple resolve requests. + let environment = context.os_environment.clone(); thread::spawn(move || { let now = SystemTime::now(); trace!("Resolving env {:?}", executable); - if let Some(result) = - resolve_environment(&executable, &context.locators, search_paths) - { + if let Some(result) = resolve_environment( + &executable, + &context.locators, + search_paths, + environment.deref(), + ) { if let Some(resolved) = result.resolved { // Gather telemetry of this resolved env and see what we got wrong. let _ = report_inaccuracies_identified_after_resolving( diff --git a/crates/pet/src/lib.rs b/crates/pet/src/lib.rs index 2039d266..b45a4275 100644 --- a/crates/pet/src/lib.rs +++ b/crates/pet/src/lib.rs @@ -29,12 +29,12 @@ pub fn find_and_report_envs_stdio(print_list: bool, print_summary: bool, verbose if let Ok(cwd) = env::current_dir() { config.project_directories = Some(vec![cwd]); } - let locators = create_locators(conda_locator.clone()); + let locators = create_locators(conda_locator.clone(), &environment); for locator in locators.iter() { locator.configure(&config); } - let summary = find_and_report_envs(&reporter, config, &locators, conda_locator); + let summary = find_and_report_envs(&reporter, config, &locators, conda_locator, &environment); if print_summary { let summary = summary.lock().unwrap(); @@ -89,7 +89,13 @@ pub fn find_and_report_envs_stdio(print_list: bool, print_summary: bool, verbose .environments .clone() .into_iter() - .map(|(k, v)| (format!("{k:?}"), v)) + .map(|(k, v)| { + ( + k.map(|v| format!("{:?}", v)) + .unwrap_or("Unknown".to_string()), + v, + ) + }) .collect::>() { println!("{k:<20} : {v:?}"); diff --git a/crates/pet/src/locators.rs b/crates/pet/src/locators.rs index 5cffdfe4..9aca6d4a 100644 --- a/crates/pet/src/locators.rs +++ b/crates/pet/src/locators.rs @@ -4,7 +4,7 @@ use log::{info, trace}; use pet_conda::Conda; use pet_core::arch::Architecture; -use pet_core::os_environment::EnvironmentApi; +use pet_core::os_environment::Environment; use pet_core::python_environment::{ PythonEnvironment, PythonEnvironmentBuilder, PythonEnvironmentKind, }; @@ -23,9 +23,11 @@ use pet_virtualenvwrapper::VirtualEnvWrapper; use std::path::PathBuf; use std::sync::Arc; -pub fn create_locators(conda_locator: Arc) -> Arc>> { +pub fn create_locators( + conda_locator: Arc, + environment: &dyn Environment, +) -> Arc>> { // NOTE: The order of the items matter. - let environment = EnvironmentApi::new(); let mut locators: Vec> = vec![]; @@ -37,18 +39,18 @@ pub fn create_locators(conda_locator: Arc) -> Arc>> #[cfg(windows)] use pet_windows_store::WindowsStore; #[cfg(windows)] - locators.push(Arc::new(WindowsStore::from(&environment))); + locators.push(Arc::new(WindowsStore::from(environment))); #[cfg(windows)] locators.push(Arc::new(WindowsRegistry::from(conda_locator.clone()))) } // 3. Pyenv Python - locators.push(Arc::new(PyEnv::from(&environment, conda_locator.clone()))); + locators.push(Arc::new(PyEnv::from(environment, conda_locator.clone()))); // 4. Homebrew Python if cfg!(unix) { #[cfg(unix)] use pet_homebrew::Homebrew; #[cfg(unix)] - let homebrew_locator = Homebrew::from(&environment); + let homebrew_locator = Homebrew::from(environment); #[cfg(unix)] locators.push(Arc::new(homebrew_locator)); } @@ -57,9 +59,9 @@ pub fn create_locators(conda_locator: Arc) -> Arc>> // 6. Support for Virtual Envs // The order of these matter. // Basically PipEnv is a superset of VirtualEnvWrapper, which is a superset of Venv, which is a superset of VirtualEnv. - locators.push(Arc::new(Poetry::from(&environment))); - locators.push(Arc::new(PipEnv::from(&environment))); - locators.push(Arc::new(VirtualEnvWrapper::from(&environment))); + locators.push(Arc::new(Poetry::from(environment))); + locators.push(Arc::new(PipEnv::from(environment))); + locators.push(Arc::new(VirtualEnvWrapper::from(environment))); locators.push(Arc::new(Venv::new())); // VirtualEnv is the most generic, hence should be the last. locators.push(Arc::new(VirtualEnv::new())); @@ -115,11 +117,7 @@ pub fn identify_python_environment_using_locators( |e, loc| if e.is_some() { e } else { loc.try_from(&env) }, ) { - trace!( - "Unknown Env ({:?}) in Path resolved as {:?}", - executable, - env.kind - ); + trace!("Env ({:?}) in Path resolved as {:?}", executable, env.kind); identify_and_set_search_path(&mut env, &search_paths); // TODO: Telemetry point. // As we had to spawn earlier. @@ -145,7 +143,7 @@ pub fn identify_python_environment_using_locators( } } info!( - "Unknown Env ({:?}) in Path resolved as {:?} and reported as {:?}", + "Env ({:?}) in Path resolved as {:?} and reported as {:?}", executable, resolved_env, fallback_kind ); let mut env = create_unknown_env(resolved_env, fallback_kind); diff --git a/crates/pet/src/resolve.rs b/crates/pet/src/resolve.rs index f71881f1..d3f0c477 100644 --- a/crates/pet/src/resolve.rs +++ b/crates/pet/src/resolve.rs @@ -6,7 +6,7 @@ use std::{path::PathBuf, sync::Arc}; use log::{trace, warn}; use pet_core::{ arch::Architecture, - os_environment::EnvironmentApi, + os_environment::Environment, python_environment::{PythonEnvironment, PythonEnvironmentBuilder}, Locator, }; @@ -25,11 +25,11 @@ pub fn resolve_environment( executable: &PathBuf, locators: &Arc>>, search_paths: Vec, + os_environment: &dyn Environment, ) -> Option { // First check if this is a known environment let env = PythonEnv::new(executable.to_owned(), None, None); - let environment = EnvironmentApi::new(); - let global_env_search_paths: Vec = get_search_paths_from_env_variables(&environment); + let global_env_search_paths: Vec = get_search_paths_from_env_variables(os_environment); if let Some(mut env) = identify_python_environment_using_locators(&env, locators, &global_env_search_paths, None) diff --git a/crates/pet/tests/ci_homebrew_container.rs b/crates/pet/tests/ci_homebrew_container.rs index 2bc78b26..73ea605c 100644 --- a/crates/pet/tests/ci_homebrew_container.rs +++ b/crates/pet/tests/ci_homebrew_container.rs @@ -23,8 +23,9 @@ fn verify_python_in_homebrew_contaner() { find_and_report_envs( &reporter, Default::default(), - &create_locators(conda_locator.clone()), + &create_locators(conda_locator.clone(), &environment), conda_locator, + &environment, ); let result = reporter.get_result(); diff --git a/crates/pet/tests/ci_jupyter_container.rs b/crates/pet/tests/ci_jupyter_container.rs index 22e57a09..44c863f3 100644 --- a/crates/pet/tests/ci_jupyter_container.rs +++ b/crates/pet/tests/ci_jupyter_container.rs @@ -41,8 +41,9 @@ fn verify_python_in_jupyter_contaner() { find_and_report_envs( &reporter, Default::default(), - &create_locators(conda_locator.clone()), + &create_locators(conda_locator.clone(), &environment), conda_locator, + &environment, ); let result = reporter.get_result(); diff --git a/crates/pet/tests/ci_poetry.rs b/crates/pet/tests/ci_poetry.rs index 033b5e95..5fbf06c4 100644 --- a/crates/pet/tests/ci_poetry.rs +++ b/crates/pet/tests/ci_poetry.rs @@ -24,12 +24,18 @@ fn verify_ci_poetry_global() { let conda_locator = Arc::new(Conda::from(&environment)); let mut config = Configuration::default(); config.project_directories = Some(vec![project_dir.clone()]); - let locators = create_locators(conda_locator.clone()); + let locators = create_locators(conda_locator.clone(), &environment); for locator in locators.iter() { locator.configure(&config); } - find_and_report_envs(&reporter, Default::default(), &locators, conda_locator); + find_and_report_envs( + &reporter, + Default::default(), + &locators, + conda_locator, + &environment, + ); let result = reporter.get_result(); @@ -84,12 +90,18 @@ fn verify_ci_poetry_project() { let conda_locator = Arc::new(Conda::from(&environment)); let mut config = Configuration::default(); config.project_directories = Some(vec![project_dir.clone()]); - let locators = create_locators(conda_locator.clone()); + let locators = create_locators(conda_locator.clone(), &environment); for locator in locators.iter() { locator.configure(&config); } - find_and_report_envs(&reporter, Default::default(), &locators, conda_locator); + find_and_report_envs( + &reporter, + Default::default(), + &locators, + conda_locator, + &environment, + ); let result = reporter.get_result(); diff --git a/crates/pet/tests/ci_test.rs b/crates/pet/tests/ci_test.rs index 582e1b1d..3cad719e 100644 --- a/crates/pet/tests/ci_test.rs +++ b/crates/pet/tests/ci_test.rs @@ -72,13 +72,19 @@ fn verify_validity_of_discovered_envs() { let conda_locator = Arc::new(Conda::from(&environment)); let mut config = Configuration::default(); config.project_directories = Some(vec![project_dir.clone()]); - let locators = create_locators(conda_locator.clone()); + let locators = create_locators(conda_locator.clone(), &environment); for locator in locators.iter() { locator.configure(&config); } // Find all environments on this machine. - find_and_report_envs(&reporter, Default::default(), &locators, conda_locator); + find_and_report_envs( + &reporter, + Default::default(), + &locators, + conda_locator, + &environment, + ); let result = reporter.get_result(); let environments = result.environments; @@ -136,8 +142,9 @@ fn check_if_virtualenvwrapper_exists() { find_and_report_envs( &reporter, Default::default(), - &create_locators(conda_locator.clone()), + &create_locators(conda_locator.clone(), &environment), conda_locator, + &environment, ); let result = reporter.get_result(); @@ -177,8 +184,9 @@ fn check_if_pipenv_exists() { find_and_report_envs( &reporter, Default::default(), - &create_locators(conda_locator.clone()), + &create_locators(conda_locator.clone(), &environment), conda_locator, + &environment, ); let result = reporter.get_result(); @@ -214,8 +222,9 @@ fn check_if_pyenv_virtualenv_exists() { find_and_report_envs( &reporter, Default::default(), - &create_locators(conda_locator.clone()), + &create_locators(conda_locator.clone(), &environment), conda_locator, + &environment, ); let result = reporter.get_result(); @@ -337,7 +346,7 @@ fn verify_we_can_get_same_env_info_using_from_with_exe( let mut config = Configuration::default(); let search_paths = vec![project_dir.clone()]; config.project_directories = Some(search_paths.clone()); - let locators = create_locators(conda_locator.clone()); + let locators = create_locators(conda_locator.clone(), &os_environment); for locator in locators.iter() { locator.configure(&config); } @@ -523,14 +532,18 @@ fn verify_we_can_get_same_env_info_using_resolve_with_exe( let conda_locator = Arc::new(Conda::from(&os_environment)); let mut config = Configuration::default(); config.project_directories = Some(vec![project_dir.clone()]); - let locators = create_locators(conda_locator.clone()); + let locators = create_locators(conda_locator.clone(), &os_environment); for locator in locators.iter() { locator.configure(&config); } - let env = resolve_environment(&executable, &locators, vec![project_dir.clone()]).expect( - format!("Failed to resolve environment using `resolve` for {environment:?}").as_str(), - ); + let env = resolve_environment( + &executable, + &locators, + vec![project_dir.clone()], + &os_environment, + ) + .expect(format!("Failed to resolve environment using `resolve` for {environment:?}").as_str()); trace!( "For exe {:?} we got Environment = {:?}, To compare against {:?}", executable, @@ -571,8 +584,9 @@ fn verify_bin_usr_bin_user_local_are_separate_python_envs() { find_and_report_envs( &reporter, Default::default(), - &create_locators(conda_locator.clone()), + &create_locators(conda_locator.clone(), &environment), conda_locator, + &environment, ); let result = reporter.get_result();