From df5bc84294b5ff4d285f24f4ea1fcf7f6b752f4f Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Fri, 3 May 2024 12:45:13 -0700 Subject: [PATCH 1/2] Minor fixes --- .vscode/settings.json | 4 ++ native_locator/src/conda.rs | 76 ++++++++++++------------------------- 2 files changed, 28 insertions(+), 52 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4869c4535290..76501f1f6d1c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,6 +30,10 @@ }, "editor.defaultFormatter": "charliermarsh.ruff", }, + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "editor.formatOnSave": true + }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true diff --git a/native_locator/src/conda.rs b/native_locator/src/conda.rs index e3134754a7a8..47bffb582514 100644 --- a/native_locator/src/conda.rs +++ b/native_locator/src/conda.rs @@ -68,21 +68,15 @@ pub fn is_conda_environment(any_path: &Path) -> bool { /// Get the version of a package in a conda environment. This will find the version /// from the 'conda-meta' directory in a platform agnostic way. fn get_version_from_meta_json(json_file: &Path) -> Option { - match Regex::new(r"(?m)([\d\w\-]*)-([\d\.]*)-.*\.json") { - Ok(re) => match json_file.file_name() { - Some(file_name) => { - let file_name = file_name.to_string_lossy(); - match re.captures(&file_name) { - Some(captures) => match captures.get(2) { - Some(version) => Some(version.as_str().to_string()), - None => None, - }, - None => None, - } - } - None => None, - }, - Err(_) => None, + let file_name = json_file.file_name()?.to_string_lossy(); + + match Regex::new(r"(?m)([\d\w\-]*)-([\d\.]*)-.*\.json") + .ok()? + .captures(&file_name)? + .get(2) + { + Some(version) => Some(version.as_str().to_string()), + None => None, } } @@ -134,29 +128,19 @@ fn get_conda_bin_names() -> Vec<&'static str> { /// Find the conda binary on the PATH environment variable fn find_conda_binary_on_path() -> Option { - let paths = env::var("PATH"); - match paths { - Ok(paths) => { - let paths = env::split_paths(&paths); - for path in paths { - let path = Path::new(&path); - for bin in get_conda_bin_names() { - let conda_path = path.join(bin); - let metadata = std::fs::metadata(&conda_path); - match metadata { - Ok(metadata) => { - if metadata.is_file() || metadata.is_symlink() { - return Some(conda_path); - } - } - Err(_) => (), - } - } + let paths = env::var("PATH").ok()?; + let paths = env::split_paths(&paths); + for path in paths { + let path = Path::new(&path); + for bin in get_conda_bin_names() { + let conda_path = path.join(bin); + let metadata = std::fs::metadata(&conda_path).ok()?; + if metadata.is_file() || metadata.is_symlink() { + return Some(conda_path); } - None } - Err(_) => None, } + None } fn find_python_binary_path(env_path: &Path) -> Option { @@ -168,15 +152,8 @@ fn find_python_binary_path(env_path: &Path) -> Option { 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); - if path_1.exists() { - Some(path_1) - } else if path_2.exists() { - Some(path_2) - } else if path_3.exists() { - Some(path_3) - } else { - None - } + let paths = vec![path_1, path_2, path_3]; + paths.into_iter().find(|path| path.exists()) } #[cfg(windows)] @@ -232,14 +209,9 @@ fn find_conda_binary_in_known_locations() -> Option { for location in known_locations { for bin in &conda_bin_names { let conda_path = location.join(bin); - let metadata = std::fs::metadata(&conda_path); - match metadata { - Ok(metadata) => { - if metadata.is_file() || metadata.is_symlink() { - return Some(conda_path); - } - } - Err(_) => (), + let metadata = std::fs::metadata(&conda_path).ok()?; + if metadata.is_file() || metadata.is_symlink() { + return Some(conda_path); } } } From 4d0dc12cf47cd254d6e563b9de00f875a66f36cb Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Fri, 3 May 2024 14:52:39 -0700 Subject: [PATCH 2/2] Add some windows locators --- native_locator/src/common_python.rs | 62 +++++++++++++++++++++++++++ native_locator/src/conda.rs | 12 ++++-- native_locator/src/main.rs | 18 +++++--- native_locator/src/utils.rs | 16 +++++++ native_locator/src/windows_python.rs | 63 ++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 native_locator/src/common_python.rs create mode 100644 native_locator/src/utils.rs create mode 100644 native_locator/src/windows_python.rs diff --git a/native_locator/src/common_python.rs b/native_locator/src/common_python.rs new file mode 100644 index 000000000000..f9c67f433fbf --- /dev/null +++ b/native_locator/src/common_python.rs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +use crate::messaging; +use crate::utils; +use std::env; +use std::path::Path; + +fn get_env_path(path: &str) -> Option { + let path = Path::new(path); + match path.parent() { + Some(parent) => { + if parent.file_name()? == "Scripts" { + return Some(parent.parent()?.to_string_lossy().to_string()); + } else { + return Some(parent.to_string_lossy().to_string()); + } + } + None => None, + } +} + +fn report_path_python(path: &str) { + let version = utils::get_version(path); + let env_path = get_env_path(path); + messaging::send_message(messaging::PythonEnvironment::new( + "Python".to_string(), + vec![path.to_string()], + "System".to_string(), + version, + None, + env_path, + )); +} + +fn report_python_on_path() { + let paths = env::var("PATH"); + let bin = if cfg!(windows) { + "python.exe" + } else { + "python" + }; + match paths { + Ok(paths) => { + let paths = env::split_paths(&paths); + for path in paths { + let full_path = path.join(bin); + if full_path.exists() { + match full_path.to_str() { + Some(full_path) => report_path_python(full_path), + None => (), + } + } + } + } + Err(_) => (), + } +} + +pub fn find_and_report() { + report_python_on_path(); +} diff --git a/native_locator/src/conda.rs b/native_locator/src/conda.rs index 47bffb582514..0aadd559b390 100644 --- a/native_locator/src/conda.rs +++ b/native_locator/src/conda.rs @@ -134,9 +134,13 @@ fn find_conda_binary_on_path() -> Option { let path = Path::new(&path); for bin in get_conda_bin_names() { let conda_path = path.join(bin); - let metadata = std::fs::metadata(&conda_path).ok()?; - if metadata.is_file() || metadata.is_symlink() { - return Some(conda_path); + match std::fs::metadata(&conda_path) { + Ok(metadata) => { + if metadata.is_file() || metadata.is_symlink() { + return Some(conda_path); + } + } + Err(_) => (), } } } @@ -363,7 +367,7 @@ fn get_distinct_conda_envs(conda_bin: PathBuf) -> Vec { conda_envs } -pub fn find_and_report_conda_envs() { +pub fn find_and_report() { let conda_binary = find_conda_binary(); match conda_binary { Some(conda_binary) => { diff --git a/native_locator/src/main.rs b/native_locator/src/main.rs index 8b4e615a9531..51f9a5b3d016 100644 --- a/native_locator/src/main.rs +++ b/native_locator/src/main.rs @@ -1,15 +1,23 @@ - // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -pub use known::*; -pub use conda::*; -pub use messaging::*; +mod common_python; mod conda; mod known; mod messaging; +mod utils; +mod windows_python; fn main() { - conda::find_and_report_conda_envs(); + // Finds python on PATH + common_python::find_and_report(); + + // finds conda binary and conda environments + conda::find_and_report(); + + // Finds Windows Store, Known Path, and Registry pythons + #[cfg(windows)] + windows_python::find_and_report(); + messaging::send_message(messaging::ExitMessage::new()); } diff --git a/native_locator/src/utils.rs b/native_locator/src/utils.rs new file mode 100644 index 000000000000..001f56c815ca --- /dev/null +++ b/native_locator/src/utils.rs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +use std::process::Command; + +pub fn get_version(path: &str) -> Option { + let output = Command::new(path) + .arg("-c") + .arg("import sys; print(sys.version)") + .output() + .ok()?; + let output = String::from_utf8(output.stdout).ok()?; + let output = output.trim(); + let output = output.split_whitespace().next().unwrap_or(output); + Some(output.to_string()) +} diff --git a/native_locator/src/windows_python.rs b/native_locator/src/windows_python.rs new file mode 100644 index 000000000000..0cb49d975685 --- /dev/null +++ b/native_locator/src/windows_python.rs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +use crate::known; +use crate::messaging; +use crate::utils; +use std::path::Path; + +fn report_path_python(path: &str) { + let version = utils::get_version(path); + messaging::send_message(messaging::PythonEnvironment::new( + "Python".to_string(), + vec![path.to_string()], + "WindowsStore".to_string(), + version, + None, + None, + )); +} + +fn report_windows_store_python() { + let home = known::get_user_home(); + match home { + Some(home) => { + let apps_path = Path::new(&home) + .join("AppData") + .join("Local") + .join("Microsoft") + .join("WindowsApps"); + let files = std::fs::read_dir(apps_path); + match files { + Ok(files) => { + for file in files { + match file { + Ok(file) => { + let path = file.path(); + match path.file_name() { + Some(name) => { + let name = name.to_string_lossy().to_lowercase(); + if name.starts_with("python3.") && name.ends_with(".exe") { + report_path_python(&path.to_string_lossy()); + } + } + None => {} + } + } + Err(_) => {} + } + } + } + Err(_) => {} + } + } + None => {} + } +} + +fn report_registry_pythons() {} + +pub fn find_and_report() { + report_windows_store_python(); + report_registry_pythons(); +}