Skip to content

Commit 93f19f7

Browse files
karthiknadigDonJayamanne
authored andcommitted
Add more Windows based locators (#23325)
1 parent f5fae81 commit 93f19f7

File tree

6 files changed

+185
-56
lines changed

6 files changed

+185
-56
lines changed

.vscode/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
},
3131
"editor.defaultFormatter": "charliermarsh.ruff",
3232
},
33+
"[rust]": {
34+
"editor.defaultFormatter": "rust-lang.rust-analyzer",
35+
"editor.formatOnSave": true
36+
},
3337
"[typescript]": {
3438
"editor.defaultFormatter": "esbenp.prettier-vscode",
3539
"editor.formatOnSave": true

native_locator/src/common_python.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
use crate::messaging;
5+
use crate::utils;
6+
use std::env;
7+
use std::path::Path;
8+
9+
fn get_env_path(path: &str) -> Option<String> {
10+
let path = Path::new(path);
11+
match path.parent() {
12+
Some(parent) => {
13+
if parent.file_name()? == "Scripts" {
14+
return Some(parent.parent()?.to_string_lossy().to_string());
15+
} else {
16+
return Some(parent.to_string_lossy().to_string());
17+
}
18+
}
19+
None => None,
20+
}
21+
}
22+
23+
fn report_path_python(path: &str) {
24+
let version = utils::get_version(path);
25+
let env_path = get_env_path(path);
26+
messaging::send_message(messaging::PythonEnvironment::new(
27+
"Python".to_string(),
28+
vec![path.to_string()],
29+
"System".to_string(),
30+
version,
31+
None,
32+
env_path,
33+
));
34+
}
35+
36+
fn report_python_on_path() {
37+
let paths = env::var("PATH");
38+
let bin = if cfg!(windows) {
39+
"python.exe"
40+
} else {
41+
"python"
42+
};
43+
match paths {
44+
Ok(paths) => {
45+
let paths = env::split_paths(&paths);
46+
for path in paths {
47+
let full_path = path.join(bin);
48+
if full_path.exists() {
49+
match full_path.to_str() {
50+
Some(full_path) => report_path_python(full_path),
51+
None => (),
52+
}
53+
}
54+
}
55+
}
56+
Err(_) => (),
57+
}
58+
}
59+
60+
pub fn find_and_report() {
61+
report_python_on_path();
62+
}

native_locator/src/conda.rs

Lines changed: 27 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,15 @@ pub fn is_conda_environment(any_path: &Path) -> bool {
6868
/// Get the version of a package in a conda environment. This will find the version
6969
/// from the 'conda-meta' directory in a platform agnostic way.
7070
fn get_version_from_meta_json(json_file: &Path) -> Option<String> {
71-
match Regex::new(r"(?m)([\d\w\-]*)-([\d\.]*)-.*\.json") {
72-
Ok(re) => match json_file.file_name() {
73-
Some(file_name) => {
74-
let file_name = file_name.to_string_lossy();
75-
match re.captures(&file_name) {
76-
Some(captures) => match captures.get(2) {
77-
Some(version) => Some(version.as_str().to_string()),
78-
None => None,
79-
},
80-
None => None,
81-
}
82-
}
83-
None => None,
84-
},
85-
Err(_) => None,
71+
let file_name = json_file.file_name()?.to_string_lossy();
72+
73+
match Regex::new(r"(?m)([\d\w\-]*)-([\d\.]*)-.*\.json")
74+
.ok()?
75+
.captures(&file_name)?
76+
.get(2)
77+
{
78+
Some(version) => Some(version.as_str().to_string()),
79+
None => None,
8680
}
8781
}
8882

@@ -134,29 +128,23 @@ fn get_conda_bin_names() -> Vec<&'static str> {
134128

135129
/// Find the conda binary on the PATH environment variable
136130
fn find_conda_binary_on_path() -> Option<PathBuf> {
137-
let paths = env::var("PATH");
138-
match paths {
139-
Ok(paths) => {
140-
let paths = env::split_paths(&paths);
141-
for path in paths {
142-
let path = Path::new(&path);
143-
for bin in get_conda_bin_names() {
144-
let conda_path = path.join(bin);
145-
let metadata = std::fs::metadata(&conda_path);
146-
match metadata {
147-
Ok(metadata) => {
148-
if metadata.is_file() || metadata.is_symlink() {
149-
return Some(conda_path);
150-
}
151-
}
152-
Err(_) => (),
131+
let paths = env::var("PATH").ok()?;
132+
let paths = env::split_paths(&paths);
133+
for path in paths {
134+
let path = Path::new(&path);
135+
for bin in get_conda_bin_names() {
136+
let conda_path = path.join(bin);
137+
match std::fs::metadata(&conda_path) {
138+
Ok(metadata) => {
139+
if metadata.is_file() || metadata.is_symlink() {
140+
return Some(conda_path);
153141
}
154142
}
143+
Err(_) => (),
155144
}
156-
None
157145
}
158-
Err(_) => None,
159146
}
147+
None
160148
}
161149

162150
fn find_python_binary_path(env_path: &Path) -> Option<PathBuf> {
@@ -168,15 +156,8 @@ fn find_python_binary_path(env_path: &Path) -> Option<PathBuf> {
168156
let path_1 = env_path.join("bin").join(python_bin_name);
169157
let path_2 = env_path.join("Scripts").join(python_bin_name);
170158
let path_3 = env_path.join(python_bin_name);
171-
if path_1.exists() {
172-
Some(path_1)
173-
} else if path_2.exists() {
174-
Some(path_2)
175-
} else if path_3.exists() {
176-
Some(path_3)
177-
} else {
178-
None
179-
}
159+
let paths = vec![path_1, path_2, path_3];
160+
paths.into_iter().find(|path| path.exists())
180161
}
181162

182163
#[cfg(windows)]
@@ -232,14 +213,9 @@ fn find_conda_binary_in_known_locations() -> Option<PathBuf> {
232213
for location in known_locations {
233214
for bin in &conda_bin_names {
234215
let conda_path = location.join(bin);
235-
let metadata = std::fs::metadata(&conda_path);
236-
match metadata {
237-
Ok(metadata) => {
238-
if metadata.is_file() || metadata.is_symlink() {
239-
return Some(conda_path);
240-
}
241-
}
242-
Err(_) => (),
216+
let metadata = std::fs::metadata(&conda_path).ok()?;
217+
if metadata.is_file() || metadata.is_symlink() {
218+
return Some(conda_path);
243219
}
244220
}
245221
}
@@ -391,7 +367,7 @@ fn get_distinct_conda_envs(conda_bin: PathBuf) -> Vec<CondaEnv> {
391367
conda_envs
392368
}
393369

394-
pub fn find_and_report_conda_envs() {
370+
pub fn find_and_report() {
395371
let conda_binary = find_conda_binary();
396372
match conda_binary {
397373
Some(conda_binary) => {

native_locator/src/main.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1-
21
// Copyright (c) Microsoft Corporation. All rights reserved.
32
// Licensed under the MIT License.
43

5-
pub use known::*;
6-
pub use conda::*;
7-
pub use messaging::*;
4+
mod common_python;
85
mod conda;
96
mod known;
107
mod messaging;
8+
mod utils;
9+
mod windows_python;
1110

1211
fn main() {
13-
conda::find_and_report_conda_envs();
12+
// Finds python on PATH
13+
common_python::find_and_report();
14+
15+
// finds conda binary and conda environments
16+
conda::find_and_report();
17+
18+
// Finds Windows Store, Known Path, and Registry pythons
19+
#[cfg(windows)]
20+
windows_python::find_and_report();
21+
1422
messaging::send_message(messaging::ExitMessage::new());
1523
}

native_locator/src/utils.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
use std::process::Command;
5+
6+
pub fn get_version(path: &str) -> Option<String> {
7+
let output = Command::new(path)
8+
.arg("-c")
9+
.arg("import sys; print(sys.version)")
10+
.output()
11+
.ok()?;
12+
let output = String::from_utf8(output.stdout).ok()?;
13+
let output = output.trim();
14+
let output = output.split_whitespace().next().unwrap_or(output);
15+
Some(output.to_string())
16+
}

native_locator/src/windows_python.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
use crate::known;
5+
use crate::messaging;
6+
use crate::utils;
7+
use std::path::Path;
8+
9+
fn report_path_python(path: &str) {
10+
let version = utils::get_version(path);
11+
messaging::send_message(messaging::PythonEnvironment::new(
12+
"Python".to_string(),
13+
vec![path.to_string()],
14+
"WindowsStore".to_string(),
15+
version,
16+
None,
17+
None,
18+
));
19+
}
20+
21+
fn report_windows_store_python() {
22+
let home = known::get_user_home();
23+
match home {
24+
Some(home) => {
25+
let apps_path = Path::new(&home)
26+
.join("AppData")
27+
.join("Local")
28+
.join("Microsoft")
29+
.join("WindowsApps");
30+
let files = std::fs::read_dir(apps_path);
31+
match files {
32+
Ok(files) => {
33+
for file in files {
34+
match file {
35+
Ok(file) => {
36+
let path = file.path();
37+
match path.file_name() {
38+
Some(name) => {
39+
let name = name.to_string_lossy().to_lowercase();
40+
if name.starts_with("python3.") && name.ends_with(".exe") {
41+
report_path_python(&path.to_string_lossy());
42+
}
43+
}
44+
None => {}
45+
}
46+
}
47+
Err(_) => {}
48+
}
49+
}
50+
}
51+
Err(_) => {}
52+
}
53+
}
54+
None => {}
55+
}
56+
}
57+
58+
fn report_registry_pythons() {}
59+
60+
pub fn find_and_report() {
61+
report_windows_store_python();
62+
report_registry_pythons();
63+
}

0 commit comments

Comments
 (0)