Skip to content
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
5 changes: 4 additions & 1 deletion crates/pet-fs/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@

use std::{
env,
path::{Path, PathBuf, MAIN_SEPARATOR},
path::{Path, PathBuf},
};

#[cfg(unix)]
use std::path::MAIN_SEPARATOR;

/// Strips trailing path separators from a path, preserving root paths.
///
/// This function removes trailing `/` or `\` from paths while ensuring that root paths
Expand Down
36 changes: 28 additions & 8 deletions crates/pet-windows-store/src/environments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,33 @@ impl PotentialPython {
if let Some(result) = get_package_display_name_and_location(&name, hkcu) {
let env_path = norm_case(PathBuf::from(result.env_path));

// Build the base symlinks list
// parent = WindowsApps folder (e.g., C:\Users\...\AppData\Local\Microsoft\WindowsApps)
// path = Package folder inside WindowsApps (e.g., WindowsApps\PythonSoftwareFoundation.Python.3.12_...)
// env_path = Program Files location (e.g., C:\Program Files\WindowsApps\PythonSoftwareFoundation...)
let mut symlinks = vec![
// Symlinks in the user WindowsApps folder
parent.join(format!("python{}.exe", self.version)),
parent.join("python3.exe"),
parent.join("python.exe"),
// Symlinks in the package subfolder under user WindowsApps
path.join("python.exe"),
path.join("python3.exe"),
path.join(format!("python{}.exe", self.version)),
// Symlinks in Program Files
env_path.join("python.exe"),
env_path.join("python3.exe"),
env_path.join(format!("python{}.exe", self.version)),
];

// Add symlinks discovered by find_symlinks (includes python.exe and python3.exe
// from WindowsApps when there's only one Python version installed)
for symlink in &self.symlinks {
if !symlinks.contains(symlink) {
symlinks.push(symlink.clone());
}
}

Some(
PythonEnvironmentBuilder::new(Some(
pet_core::python_environment::PythonEnvironmentKind::WindowsStore,
Expand All @@ -70,14 +97,7 @@ impl PotentialPython {
})
// We only have the partial version, no point returning bogus info.
// .version(Some(self.version.clone()))
.symlinks(Some(vec![
parent.join(format!("python{}.exe", self.version)),
path.join("python.exe"),
path.join("python3.exe"),
path.join(format!("python{}.exe", self.version)),
env_path.join("python.exe"),
env_path.join(format!("python{}.exe", self.version)),
]))
.symlinks(Some(symlinks))
.build(),
)
} else {
Expand Down
23 changes: 20 additions & 3 deletions crates/pet-windows-store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,42 @@ impl Locator for WindowsStore {
use std::path::PathBuf;

use pet_core::python_environment::PythonEnvironmentBuilder;
use pet_fs::path::norm_case;
use pet_virtualenv::is_virtualenv;

// Helper to normalize paths for comparison by stripping \\?\ prefix
fn normalize_for_comparison(path: &PathBuf) -> PathBuf {
let normalized = norm_case(path);
let path_str = normalized.to_string_lossy();
if path_str.starts_with(r"\\?\") {
PathBuf::from(path_str.trim_start_matches(r"\\?\"))
} else {
normalized
}
}

// Assume we create a virtual env from a python install,
// Then the exe in the virtual env bin will be a symlink to the homebrew python install.
// Hence the first part of the condition will be true, but the second part will be false.
if is_virtualenv(env) {
return None;
}
let list_of_possible_exes = vec![env.executable.clone()]
// Normalize paths to handle \\?\ prefix differences
let list_of_possible_exes: Vec<PathBuf> = vec![env.executable.clone()]
.into_iter()
.chain(env.symlinks.clone().unwrap_or_default())
.collect::<Vec<PathBuf>>();
.map(|p| normalize_for_comparison(&p))
.collect();
if let Some(environments) = self.find_with_cache() {
for found_env in environments {
if let Some(symlinks) = &found_env.symlinks {
// Normalize symlinks for comparison
let normalized_symlinks: Vec<PathBuf> =
symlinks.iter().map(normalize_for_comparison).collect();
// Check if we have found this exe.
if list_of_possible_exes
.iter()
.any(|exe| symlinks.contains(exe))
.any(|exe| normalized_symlinks.contains(exe))
{
// Its possible the env discovery was not aware of the symlink
// E.g. if we are asked to resolve `../WindowsApp/python.exe`
Expand Down
Loading