From 39b88a194d062c06f422bef0b21cdbf267d285dc Mon Sep 17 00:00:00 2001 From: nichmor Date: Mon, 22 Jan 2024 14:52:06 +0200 Subject: [PATCH] feat: cache retrieving of python executable (#160) closes #134 I've decided to not introduce lazy_static as dependency, and to use OnceLock https://github.com/rust-lang-nursery/lazy-static.rs/pull/216/files ( in my case, sync::OnceCell ) --- .../src/python_env/system_python.rs | 67 ++++++++++--------- .../src/python_env/venv.rs | 2 +- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/crates/rattler_installs_packages/src/python_env/system_python.rs b/crates/rattler_installs_packages/src/python_env/system_python.rs index 23ccf47f..b58fb662 100644 --- a/crates/rattler_installs_packages/src/python_env/system_python.rs +++ b/crates/rattler_installs_packages/src/python_env/system_python.rs @@ -1,4 +1,5 @@ use itertools::Itertools; +use once_cell::sync::OnceCell; use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -13,38 +14,41 @@ pub enum FindPythonError { IoError(#[from] std::io::Error), } +/// Return cached python executable. /// Try to find the python executable in the current environment. -/// Using sys.executable approach will return original interpretation path -/// and not the shim in case of using which -pub fn system_python_executable() -> Result { - // When installed with homebrew on macOS, the python3 executable is called `python3` instead - // Also on some ubuntu installs this is the case - // For windows it should just be python - - let output = match std::process::Command::new("python3") - .arg("-c") - .arg("import sys; print(sys.executable, end='')") - .output() - .or_else(|_| { - std::process::Command::new("python") - .arg("-c") - .arg("import sys; print(sys.executable, end='')") - .output() - }) { - Err(e) if e.kind() == ErrorKind::NotFound => return Err(FindPythonError::NotFound), - Err(e) => return Err(FindPythonError::IoError(e)), - Ok(output) => output, - }; - - let stdout = String::from_utf8_lossy(&output.stdout); - let python_path = PathBuf::from_str(&stdout).unwrap(); - - // sys.executable can return empty string or python's None - if !python_path.exists() { - return Err(FindPythonError::NotFound); - } +/// Using sys.executable aproach will return original interpretator path +/// and not the shim in case of using which. +pub fn system_python_executable() -> Result<&'static PathBuf, FindPythonError> { + static SYSTEM_PYTHON_EXECUTABLE: OnceCell = OnceCell::new(); + SYSTEM_PYTHON_EXECUTABLE.get_or_try_init(|| { + // When installed with homebrew on macOS, the python3 executable is called `python3` instead + // Also on some ubuntu installs this is the case + // For windows it should just be python + let output = match std::process::Command::new("python3") + .arg("-c") + .arg("import sys; print(sys.executable, end='')") + .output() + .or_else(|_| { + std::process::Command::new("python") + .arg("-c") + .arg("import sys; print(sys.executable, end='')") + .output() + }) { + Err(e) if e.kind() == ErrorKind::NotFound => return Err(FindPythonError::NotFound), + Err(e) => return Err(FindPythonError::IoError(e)), + Ok(output) => output, + }; + + let stdout = String::from_utf8_lossy(&output.stdout); + let python_path = PathBuf::from_str(&stdout).unwrap(); + + // sys.executable can return empty string or python's None + if !python_path.exists() { + return Err(FindPythonError::NotFound); + } - Ok(python_path) + Ok(python_path) + }) } /// Errors that can occur while trying to parse the python version @@ -125,7 +129,8 @@ impl PythonInterpreterVersion { /// Get the python version from the system interpreter pub fn from_system() -> Result { - Self::from_path(&system_python_executable()?) + let python_path = system_python_executable()?; + Self::from_path(python_path) } /// Get the python version a path to the python executable diff --git a/crates/rattler_installs_packages/src/python_env/venv.rs b/crates/rattler_installs_packages/src/python_env/venv.rs index 4a04a446..5949fdaa 100644 --- a/crates/rattler_installs_packages/src/python_env/venv.rs +++ b/crates/rattler_installs_packages/src/python_env/venv.rs @@ -45,7 +45,7 @@ impl PythonLocation { /// Location of python executable pub fn executable(&self) -> Result { match self { - PythonLocation::System => system_python_executable(), + PythonLocation::System => system_python_executable().cloned(), PythonLocation::Custom(path) => Ok(path.clone()), PythonLocation::CustomWithVersion(path, _) => Ok(path.clone()), }