Skip to content

Commit

Permalink
Support for pyenv locator (#23356)
Browse files Browse the repository at this point in the history
  • Loading branch information
DonJayamanne committed Jun 24, 2024
1 parent 9c29fd1 commit 7546f1e
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 14 deletions.
14 changes: 1 addition & 13 deletions native_locator/src/conda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use crate::known;
use crate::messaging;
use crate::utils::find_python_binary_path;
use regex::Regex;
use std::env;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -145,19 +146,6 @@ fn find_conda_binary_on_path(environment: &impl known::Environment) -> Option<Pa
None
}

fn find_python_binary_path(env_path: &Path) -> Option<PathBuf> {
let python_bin_name = if cfg!(windows) {
"python.exe"
} else {
"python"
};
let path_1 = env_path.join("bin").join(python_bin_name);
let path_2 = env_path.join("Scripts").join(python_bin_name);
let path_3 = env_path.join(python_bin_name);
let paths = vec![path_1, path_2, path_3];
paths.into_iter().find(|path| path.exists())
}

#[cfg(windows)]
fn get_known_conda_locations(environment: &impl known::Environment) -> Vec<PathBuf> {
let user_profile = environment.get_env_var("USERPROFILE".to_string()).unwrap();
Expand Down
3 changes: 3 additions & 0 deletions native_locator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod logging;
mod messaging;
mod utils;
mod windows_python;
mod pyenv;

fn main() {
let mut dispatcher = create_dispatcher();
Expand All @@ -31,6 +32,8 @@ fn main() {
#[cfg(windows)]
windows_python::find_and_report(&mut dispatcher, &environment);

pyenv::find_and_report(&mut dispatcher, &environment);

match now.elapsed() {
Ok(elapsed) => {
dispatcher.log_info(&format!(
Expand Down
115 changes: 115 additions & 0 deletions native_locator/src/pyenv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use std::fs;
use std::path::PathBuf;

use crate::known;
use crate::messaging;
use crate::utils::find_python_binary_path;

#[cfg(windows)]
fn get_home_pyenv_dir(environment: &impl known::Environment) -> Option<String> {
let home = environment.get_user_home()?;
PathBuf::from(home)
.join(".pyenv")
.join("pyenv-win")
.into_os_string()
.into_string()
.ok()
}

#[cfg(unix)]
fn get_home_pyenv_dir(environment: &impl known::Environment) -> Option<String> {
let home = environment.get_user_home()?;
PathBuf::from(home)
.join(".pyenv")
.into_os_string()
.into_string()
.ok()
}

fn get_binary_from_known_paths(environment: &impl known::Environment) -> Option<String> {
for known_path in environment.get_know_global_search_locations() {
let bin = known_path.join("pyenv");
if bin.exists() {
return bin.into_os_string().into_string().ok();
}
}
None
}

fn get_pyenv_dir(environment: &impl known::Environment) -> Option<String> {
// Check if the pyenv environment variables exist: PYENV on Windows, PYENV_ROOT on Unix.
// They contain the path to pyenv's installation folder.
// If they don't exist, use the default path: ~/.pyenv/pyenv-win on Windows, ~/.pyenv on Unix.
// If the interpreter path starts with the path to the pyenv folder, then it is a pyenv environment.
// See https://github.com/pyenv/pyenv#locating-the-python-installation for general usage,
// And https://github.com/pyenv-win/pyenv-win for Windows specifics.

match environment.get_env_var("PYENV_ROOT".to_string()) {
Some(dir) => Some(dir),
None => match environment.get_env_var("PYENV".to_string()) {
Some(dir) => Some(dir),
None => get_home_pyenv_dir(environment),
},
}
}

fn get_pyenv_binary(environment: &impl known::Environment) -> Option<String> {
let dir = get_pyenv_dir(environment)?;
let exe = PathBuf::from(dir).join("bin").join("pyenv");
if fs::metadata(&exe).is_ok() {
exe.into_os_string().into_string().ok()
} else {
get_binary_from_known_paths(environment)
}
}

pub fn find_and_report(
dispatcher: &mut impl messaging::MessageDispatcher,
environment: &impl known::Environment,
) -> Option<()> {
let pyenv_dir = get_pyenv_dir(environment)?;

if let Some(pyenv_binary) = get_pyenv_binary(environment) {
let params = messaging::EnvManager::new(vec![pyenv_binary], None);
let message = messaging::EnvManagerMessage::new(params);
dispatcher.send_message(message);
}

let versions_dir = PathBuf::from(&pyenv_dir)
.join("versions")
.into_os_string()
.into_string()
.ok()?;

let pyenv_binary_for_activation = match get_pyenv_binary(environment) {
Some(binary) => binary,
None => "pyenv".to_string(),
};
for entry in fs::read_dir(&versions_dir).ok()? {
if let Ok(path) = entry {
let path = path.path();
if path.is_dir() {
if let Some(executable) = find_python_binary_path(&path) {
let version = path.file_name().unwrap().to_string_lossy().to_string();
dispatcher.send_message(messaging::PythonEnvironment::new(
"Python".to_string(),
vec![executable.into_os_string().into_string().unwrap()],
"Pyenv".to_string(),
Some(version.clone()),
Some(vec![
pyenv_binary_for_activation.clone(),
"shell".to_string(),
version,
]),
Some(path.into_os_string().into_string().unwrap()),
));
}
}
}
}

None
}
18 changes: 17 additions & 1 deletion native_locator/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

use std::process::Command;
use std::{
path::{Path, PathBuf},
process::Command,
};

fn get_version_impl(path: &str) -> Option<String> {
let output = Command::new(path)
Expand Down Expand Up @@ -32,3 +35,16 @@ pub fn get_version(path: &str) -> Option<String> {
}
get_version_impl(path)
}

pub fn find_python_binary_path(env_path: &Path) -> Option<PathBuf> {
let python_bin_name = if cfg!(windows) {
"python.exe"
} else {
"python"
};
let path_1 = env_path.join("bin").join(python_bin_name);
let path_2 = env_path.join("Scripts").join(python_bin_name);
let path_3 = env_path.join(python_bin_name);
let paths = vec![path_1, path_2, path_3];
paths.into_iter().find(|path| path.exists())
}

0 comments on commit 7546f1e

Please sign in to comment.