Skip to content

Commit

Permalink
Support virtualenvwrapper in nativelocator (microsoft#23388)
Browse files Browse the repository at this point in the history
  • Loading branch information
DonJayamanne authored and anthonykim1 committed May 16, 2024
1 parent 742cbfd commit c7410a4
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 50 deletions.
8 changes: 5 additions & 3 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::messaging::EnvManagerType;
use crate::utils::find_python_binary_path;
use regex::Regex;
use std::env;
Expand Down Expand Up @@ -218,15 +219,15 @@ pub fn find_conda_binary(environment: &impl known::Environment) -> Option<PathBu

pub fn get_conda_version(conda_binary: &PathBuf) -> Option<String> {
let mut parent = conda_binary.parent()?;
if parent.ends_with("bin"){
if parent.ends_with("bin") {
parent = parent.parent()?;
}
if parent.ends_with("Library"){
if parent.ends_with("Library") {
parent = parent.parent()?;
}
let conda_python_json_path = match get_conda_package_json_path(&parent, "conda") {
Some(exe) => Some(exe),
None => get_conda_package_json_path(&parent.parent()?, "conda")
None => get_conda_package_json_path(&parent.parent()?, "conda"),
}?;
get_version_from_meta_json(&conda_python_json_path)
}
Expand Down Expand Up @@ -387,6 +388,7 @@ pub fn find_and_report(
let params = messaging::EnvManager::new(
conda_binary.to_string_lossy().to_string(),
get_conda_version(&conda_binary),
EnvManagerType::Conda,
);
dispatcher.report_environment_manager(params);

Expand Down
27 changes: 20 additions & 7 deletions native_locator/src/global_virtualenvs.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use crate::pipenv;
use crate::virtualenvwrapper;
use crate::{
known,
utils::{find_python_binary_path, get_version},
messaging::MessageDispatcher,
utils::{find_python_binary_path, get_version, PythonEnv},
};
use std::{fs, path::PathBuf};

Expand Down Expand Up @@ -43,12 +46,6 @@ fn get_global_virtualenv_dirs(environment: &impl known::Environment) -> Vec<Path
venv_dirs
}

pub struct PythonEnv {
pub path: PathBuf,
pub executable: PathBuf,
pub version: Option<String>,
}

pub fn list_global_virtualenvs(environment: &impl known::Environment) -> Vec<PythonEnv> {
let mut python_envs: Vec<PythonEnv> = vec![];
for root_dir in get_global_virtualenv_dirs(environment).iter() {
Expand All @@ -73,3 +70,19 @@ pub fn list_global_virtualenvs(environment: &impl known::Environment) -> Vec<Pyt

python_envs
}

pub fn find_and_report(
dispatcher: &mut impl MessageDispatcher,
environment: &impl known::Environment,
) -> Option<()> {
for env in list_global_virtualenvs(environment).iter() {
if pipenv::find_and_report(&env, dispatcher).is_some() {
continue;
}
if virtualenvwrapper::find_and_report(&env, dispatcher, environment).is_some() {
continue;
}
}

None
}
3 changes: 3 additions & 0 deletions native_locator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ pub mod conda;
pub mod known;
pub mod pyenv;
pub mod global_virtualenvs;
pub mod virtualenvwrapper;
pub mod pipenv;
pub mod virtualenv;
6 changes: 4 additions & 2 deletions native_locator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ mod messaging;
mod pipenv;
mod pyenv;
mod utils;
mod virtualenv;
mod virtualenvwrapper;
mod windows_python;

fn main() {
Expand All @@ -25,6 +27,8 @@ fn main() {
dispatcher.log_info("Starting Native Locator");
let now = SystemTime::now();

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

// Finds python on PATH
common_python::find_and_report(&mut dispatcher, &environment);

Expand All @@ -37,8 +41,6 @@ fn main() {

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

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

#[cfg(unix)]
homebrew::find_and_report(&mut dispatcher, &environment);

Expand Down
13 changes: 12 additions & 1 deletion native_locator/src/messaging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,27 @@ pub trait MessageDispatcher {
fn log_error(&mut self, message: &str) -> ();
}

#[derive(Serialize, Deserialize, Copy, Clone)]
#[serde(rename_all = "camelCase")]
pub enum EnvManagerType {
Conda,
Pyenv,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EnvManager {
pub executable_path: String,
pub version: Option<String>,
pub tool: EnvManagerType,
}

impl EnvManager {
pub fn new(executable_path: String, version: Option<String>) -> Self {
pub fn new(executable_path: String, version: Option<String>, tool: EnvManagerType) -> Self {
Self {
executable_path,
version,
tool,
}
}
}
Expand All @@ -35,6 +44,7 @@ impl Clone for EnvManager {
Self {
executable_path: self.executable_path.clone(),
version: self.version.clone(),
tool: self.tool,
}
}
}
Expand Down Expand Up @@ -67,6 +77,7 @@ pub enum PythonEnvironmentCategory {
PyenvVirtualEnv,
WindowsStore,
Pipenv,
VirtualEnvWrapper,
}

#[derive(Serialize, Deserialize)]
Expand Down
57 changes: 26 additions & 31 deletions native_locator/src/pipenv.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use crate::global_virtualenvs::{list_global_virtualenvs, PythonEnv};
use crate::known;
use crate::messaging::{MessageDispatcher, PythonEnvironment};
use crate::utils::PythonEnv;
use std::fs;
use std::path::PathBuf;

fn get_project_folder(env: &PythonEnv) -> Option<String> {
fn get_pipenv_project(env: &PythonEnv) -> Option<String> {
let project_file = env.path.join(".project");
if project_file.exists() {
if let Ok(contents) = fs::read_to_string(project_file) {
Expand All @@ -21,35 +20,31 @@ fn get_project_folder(env: &PythonEnv) -> Option<String> {
None
}

pub fn find_and_report(
dispatcher: &mut impl MessageDispatcher,
environment: &impl known::Environment,
) -> Option<()> {
for env in list_global_virtualenvs(environment).iter() {
if let Some(project_path) = get_project_folder(&env) {
let env_path = env
.path
.clone()
.into_os_string()
.to_string_lossy()
.to_string();
let executable = env
.executable
.clone()
.into_os_string()
.to_string_lossy()
.to_string();
let env = PythonEnvironment::new_pipenv(
Some(executable),
env.version.clone(),
Some(env_path.clone()),
Some(env_path),
None,
project_path,
);
pub fn find_and_report(env: &PythonEnv, dispatcher: &mut impl MessageDispatcher) -> Option<()> {
if let Some(project_path) = get_pipenv_project(env) {
let env_path = env
.path
.clone()
.into_os_string()
.to_string_lossy()
.to_string();
let executable = env
.executable
.clone()
.into_os_string()
.to_string_lossy()
.to_string();
let env = PythonEnvironment::new_pipenv(
Some(executable),
env.version.clone(),
Some(env_path.clone()),
Some(env_path),
None,
project_path,
);

dispatcher.report_environment(env);
}
dispatcher.report_environment(env);
return Some(());
}

None
Expand Down
3 changes: 2 additions & 1 deletion native_locator/src/pyenv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::path::PathBuf;
use crate::known;
use crate::messaging;
use crate::messaging::EnvManager;
use crate::messaging::EnvManagerType;
use crate::utils::find_python_binary_path;
use crate::utils::parse_pyenv_cfg;

Expand Down Expand Up @@ -156,7 +157,7 @@ pub fn find_and_report(

let manager = match get_pyenv_binary(environment) {
Some(pyenv_binary) => {
let manager = messaging::EnvManager::new(pyenv_binary, None);
let manager = messaging::EnvManager::new(pyenv_binary, None, EnvManagerType::Pyenv);
dispatcher.report_environment_manager(manager.clone());
Some(manager)
}
Expand Down
7 changes: 7 additions & 0 deletions native_locator/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ use std::{
process::Command,
};

#[derive(Debug)]
pub struct PythonEnv {
pub path: PathBuf,
pub executable: PathBuf,
pub version: Option<String>,
}

#[derive(Debug)]
pub struct PyEnvCfg {
pub version: String,
Expand Down
43 changes: 43 additions & 0 deletions native_locator/src/virtualenv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use std::path::PathBuf;

use crate::utils::PythonEnv;

pub fn is_virtualenv(env: &PythonEnv) -> bool {
if let Some(file_path) = PathBuf::from(env.executable.clone()).parent() {
// Check if there are any activate.* files in the same directory as the interpreter.
//
// env
// |__ activate, activate.* <--- check if any of these files exist
// |__ python <--- interpreterPath

// if let Some(parent_path) = PathBuf::from(env.)
// const directory = path.dirname(interpreterPath);
// const files = await fsapi.readdir(directory);
// const regex = /^activate(\.([A-z]|\d)+)?$/i;
if file_path.join("activate").exists() || file_path.join("activate.bat").exists() {
return true;
}

// Support for activate.ps, etc.
match std::fs::read_dir(file_path) {
Ok(files) => {
for file in files {
if let Ok(file) = file {
if let Some(file_name) = file.file_name().to_str() {
if file_name.starts_with("activate") {
return true;
}
}
}
}
return false;
}
Err(_) => return false,
};
}

false
}
Loading

0 comments on commit c7410a4

Please sign in to comment.