Skip to content

Add more Windows based locators #23325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
62 changes: 62 additions & 0 deletions native_locator/src/common_python.rs
Original file line number Diff line number Diff line change
@@ -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<String> {
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();
}
78 changes: 27 additions & 51 deletions native_locator/src/conda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
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,
}
}

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

/// Find the conda binary on the PATH environment variable
fn find_conda_binary_on_path() -> Option<PathBuf> {
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);
match std::fs::metadata(&conda_path) {
Ok(metadata) => {
if metadata.is_file() || metadata.is_symlink() {
return Some(conda_path);
}
}
Err(_) => (),
}
None
}
Err(_) => None,
}
None
}

fn find_python_binary_path(env_path: &Path) -> Option<PathBuf> {
Expand All @@ -168,15 +156,8 @@ fn find_python_binary_path(env_path: &Path) -> Option<PathBuf> {
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)]
Expand Down Expand Up @@ -232,14 +213,9 @@ fn find_conda_binary_in_known_locations() -> Option<PathBuf> {
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);
}
}
}
Expand Down Expand Up @@ -391,7 +367,7 @@ fn get_distinct_conda_envs(conda_bin: PathBuf) -> Vec<CondaEnv> {
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) => {
Expand Down
18 changes: 13 additions & 5 deletions native_locator/src/main.rs
Original file line number Diff line number Diff line change
@@ -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());
}
16 changes: 16 additions & 0 deletions native_locator/src/utils.rs
Original file line number Diff line number Diff line change
@@ -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<String> {
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())
}
63 changes: 63 additions & 0 deletions native_locator/src/windows_python.rs
Original file line number Diff line number Diff line change
@@ -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();
}
Loading