Skip to content

Commit c7410a4

Browse files
DonJayamanneanthonykim1
authored andcommitted
Support virtualenvwrapper in nativelocator (microsoft#23388)
1 parent 742cbfd commit c7410a4

12 files changed

+223
-50
lines changed

native_locator/src/conda.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use crate::known;
55
use crate::messaging;
6+
use crate::messaging::EnvManagerType;
67
use crate::utils::find_python_binary_path;
78
use regex::Regex;
89
use std::env;
@@ -218,15 +219,15 @@ pub fn find_conda_binary(environment: &impl known::Environment) -> Option<PathBu
218219

219220
pub fn get_conda_version(conda_binary: &PathBuf) -> Option<String> {
220221
let mut parent = conda_binary.parent()?;
221-
if parent.ends_with("bin"){
222+
if parent.ends_with("bin") {
222223
parent = parent.parent()?;
223224
}
224-
if parent.ends_with("Library"){
225+
if parent.ends_with("Library") {
225226
parent = parent.parent()?;
226227
}
227228
let conda_python_json_path = match get_conda_package_json_path(&parent, "conda") {
228229
Some(exe) => Some(exe),
229-
None => get_conda_package_json_path(&parent.parent()?, "conda")
230+
None => get_conda_package_json_path(&parent.parent()?, "conda"),
230231
}?;
231232
get_version_from_meta_json(&conda_python_json_path)
232233
}
@@ -387,6 +388,7 @@ pub fn find_and_report(
387388
let params = messaging::EnvManager::new(
388389
conda_binary.to_string_lossy().to_string(),
389390
get_conda_version(&conda_binary),
391+
EnvManagerType::Conda,
390392
);
391393
dispatcher.report_environment_manager(params);
392394

native_locator/src/global_virtualenvs.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
use crate::pipenv;
5+
use crate::virtualenvwrapper;
46
use crate::{
57
known,
6-
utils::{find_python_binary_path, get_version},
8+
messaging::MessageDispatcher,
9+
utils::{find_python_binary_path, get_version, PythonEnv},
710
};
811
use std::{fs, path::PathBuf};
912

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

46-
pub struct PythonEnv {
47-
pub path: PathBuf,
48-
pub executable: PathBuf,
49-
pub version: Option<String>,
50-
}
51-
5249
pub fn list_global_virtualenvs(environment: &impl known::Environment) -> Vec<PythonEnv> {
5350
let mut python_envs: Vec<PythonEnv> = vec![];
5451
for root_dir in get_global_virtualenv_dirs(environment).iter() {
@@ -73,3 +70,19 @@ pub fn list_global_virtualenvs(environment: &impl known::Environment) -> Vec<Pyt
7370

7471
python_envs
7572
}
73+
74+
pub fn find_and_report(
75+
dispatcher: &mut impl MessageDispatcher,
76+
environment: &impl known::Environment,
77+
) -> Option<()> {
78+
for env in list_global_virtualenvs(environment).iter() {
79+
if pipenv::find_and_report(&env, dispatcher).is_some() {
80+
continue;
81+
}
82+
if virtualenvwrapper::find_and_report(&env, dispatcher, environment).is_some() {
83+
continue;
84+
}
85+
}
86+
87+
None
88+
}

native_locator/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ pub mod conda;
99
pub mod known;
1010
pub mod pyenv;
1111
pub mod global_virtualenvs;
12+
pub mod virtualenvwrapper;
13+
pub mod pipenv;
14+
pub mod virtualenv;

native_locator/src/main.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ mod messaging;
1616
mod pipenv;
1717
mod pyenv;
1818
mod utils;
19+
mod virtualenv;
20+
mod virtualenvwrapper;
1921
mod windows_python;
2022

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

30+
global_virtualenvs::find_and_report(&mut dispatcher, &environment);
31+
2832
// Finds python on PATH
2933
common_python::find_and_report(&mut dispatcher, &environment);
3034

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

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

40-
pipenv::find_and_report(&mut dispatcher, &environment);
41-
4244
#[cfg(unix)]
4345
homebrew::find_and_report(&mut dispatcher, &environment);
4446

native_locator/src/messaging.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,27 @@ pub trait MessageDispatcher {
1414
fn log_error(&mut self, message: &str) -> ();
1515
}
1616

17+
#[derive(Serialize, Deserialize, Copy, Clone)]
18+
#[serde(rename_all = "camelCase")]
19+
pub enum EnvManagerType {
20+
Conda,
21+
Pyenv,
22+
}
23+
1724
#[derive(Serialize, Deserialize)]
1825
#[serde(rename_all = "camelCase")]
1926
pub struct EnvManager {
2027
pub executable_path: String,
2128
pub version: Option<String>,
29+
pub tool: EnvManagerType,
2230
}
2331

2432
impl EnvManager {
25-
pub fn new(executable_path: String, version: Option<String>) -> Self {
33+
pub fn new(executable_path: String, version: Option<String>, tool: EnvManagerType) -> Self {
2634
Self {
2735
executable_path,
2836
version,
37+
tool,
2938
}
3039
}
3140
}
@@ -35,6 +44,7 @@ impl Clone for EnvManager {
3544
Self {
3645
executable_path: self.executable_path.clone(),
3746
version: self.version.clone(),
47+
tool: self.tool,
3848
}
3949
}
4050
}
@@ -67,6 +77,7 @@ pub enum PythonEnvironmentCategory {
6777
PyenvVirtualEnv,
6878
WindowsStore,
6979
Pipenv,
80+
VirtualEnvWrapper,
7081
}
7182

7283
#[derive(Serialize, Deserialize)]

native_locator/src/pipenv.rs

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
use crate::global_virtualenvs::{list_global_virtualenvs, PythonEnv};
5-
use crate::known;
64
use crate::messaging::{MessageDispatcher, PythonEnvironment};
5+
use crate::utils::PythonEnv;
76
use std::fs;
87
use std::path::PathBuf;
98

10-
fn get_project_folder(env: &PythonEnv) -> Option<String> {
9+
fn get_pipenv_project(env: &PythonEnv) -> Option<String> {
1110
let project_file = env.path.join(".project");
1211
if project_file.exists() {
1312
if let Ok(contents) = fs::read_to_string(project_file) {
@@ -21,35 +20,31 @@ fn get_project_folder(env: &PythonEnv) -> Option<String> {
2120
None
2221
}
2322

24-
pub fn find_and_report(
25-
dispatcher: &mut impl MessageDispatcher,
26-
environment: &impl known::Environment,
27-
) -> Option<()> {
28-
for env in list_global_virtualenvs(environment).iter() {
29-
if let Some(project_path) = get_project_folder(&env) {
30-
let env_path = env
31-
.path
32-
.clone()
33-
.into_os_string()
34-
.to_string_lossy()
35-
.to_string();
36-
let executable = env
37-
.executable
38-
.clone()
39-
.into_os_string()
40-
.to_string_lossy()
41-
.to_string();
42-
let env = PythonEnvironment::new_pipenv(
43-
Some(executable),
44-
env.version.clone(),
45-
Some(env_path.clone()),
46-
Some(env_path),
47-
None,
48-
project_path,
49-
);
23+
pub fn find_and_report(env: &PythonEnv, dispatcher: &mut impl MessageDispatcher) -> Option<()> {
24+
if let Some(project_path) = get_pipenv_project(env) {
25+
let env_path = env
26+
.path
27+
.clone()
28+
.into_os_string()
29+
.to_string_lossy()
30+
.to_string();
31+
let executable = env
32+
.executable
33+
.clone()
34+
.into_os_string()
35+
.to_string_lossy()
36+
.to_string();
37+
let env = PythonEnvironment::new_pipenv(
38+
Some(executable),
39+
env.version.clone(),
40+
Some(env_path.clone()),
41+
Some(env_path),
42+
None,
43+
project_path,
44+
);
5045

51-
dispatcher.report_environment(env);
52-
}
46+
dispatcher.report_environment(env);
47+
return Some(());
5348
}
5449

5550
None

native_locator/src/pyenv.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::path::PathBuf;
88
use crate::known;
99
use crate::messaging;
1010
use crate::messaging::EnvManager;
11+
use crate::messaging::EnvManagerType;
1112
use crate::utils::find_python_binary_path;
1213
use crate::utils::parse_pyenv_cfg;
1314

@@ -156,7 +157,7 @@ pub fn find_and_report(
156157

157158
let manager = match get_pyenv_binary(environment) {
158159
Some(pyenv_binary) => {
159-
let manager = messaging::EnvManager::new(pyenv_binary, None);
160+
let manager = messaging::EnvManager::new(pyenv_binary, None, EnvManagerType::Pyenv);
160161
dispatcher.report_environment_manager(manager.clone());
161162
Some(manager)
162163
}

native_locator/src/utils.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ use std::{
88
process::Command,
99
};
1010

11+
#[derive(Debug)]
12+
pub struct PythonEnv {
13+
pub path: PathBuf,
14+
pub executable: PathBuf,
15+
pub version: Option<String>,
16+
}
17+
1118
#[derive(Debug)]
1219
pub struct PyEnvCfg {
1320
pub version: String,

native_locator/src/virtualenv.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use std::path::PathBuf;
5+
6+
use crate::utils::PythonEnv;
7+
8+
pub fn is_virtualenv(env: &PythonEnv) -> bool {
9+
if let Some(file_path) = PathBuf::from(env.executable.clone()).parent() {
10+
// Check if there are any activate.* files in the same directory as the interpreter.
11+
//
12+
// env
13+
// |__ activate, activate.* <--- check if any of these files exist
14+
// |__ python <--- interpreterPath
15+
16+
// if let Some(parent_path) = PathBuf::from(env.)
17+
// const directory = path.dirname(interpreterPath);
18+
// const files = await fsapi.readdir(directory);
19+
// const regex = /^activate(\.([A-z]|\d)+)?$/i;
20+
if file_path.join("activate").exists() || file_path.join("activate.bat").exists() {
21+
return true;
22+
}
23+
24+
// Support for activate.ps, etc.
25+
match std::fs::read_dir(file_path) {
26+
Ok(files) => {
27+
for file in files {
28+
if let Ok(file) = file {
29+
if let Some(file_name) = file.file_name().to_str() {
30+
if file_name.starts_with("activate") {
31+
return true;
32+
}
33+
}
34+
}
35+
}
36+
return false;
37+
}
38+
Err(_) => return false,
39+
};
40+
}
41+
42+
false
43+
}

0 commit comments

Comments
 (0)