diff --git a/crates/pet-conda/src/manager.rs b/crates/pet-conda/src/manager.rs index 1659e534..2bcc5ab3 100644 --- a/crates/pet-conda/src/manager.rs +++ b/crates/pet-conda/src/manager.rs @@ -17,6 +17,8 @@ fn get_conda_executable(path: &Path) -> Option { let relative_path_to_conda_exe = vec![ PathBuf::from("Scripts").join("conda.exe"), PathBuf::from("Scripts").join("conda.bat"), + PathBuf::from("bin").join("conda.exe"), + PathBuf::from("bin").join("conda.bat"), ]; #[cfg(unix)] let relative_path_to_conda_exe = vec![PathBuf::from("bin").join("conda")]; diff --git a/crates/pet-core/src/pyvenv_cfg.rs b/crates/pet-core/src/pyvenv_cfg.rs index 8eb8bb0f..1dd454a5 100644 --- a/crates/pet-core/src/pyvenv_cfg.rs +++ b/crates/pet-core/src/pyvenv_cfg.rs @@ -61,8 +61,17 @@ fn find(path: &Path) -> Option { return Some(cfg); } - let bin = if cfg!(windows) { "Scripts" } else { "bin" }; - if path.ends_with(bin) { + if cfg!(windows) { + // Only windows installations have a `Scripts` directory. + if path.ends_with("Scripts") { + let cfg = path.parent()?.join(PYVENV_CONFIG_FILE); + if cfg.exists() { + return Some(cfg); + } + } + } + // Some windows installations have a `bin` directory. https://github.com/microsoft/vscode-python/issues/24792 + if path.ends_with("bin") { let cfg = path.parent()?.join(PYVENV_CONFIG_FILE); if cfg.exists() { return Some(cfg); diff --git a/crates/pet-python-utils/src/executable.rs b/crates/pet-python-utils/src/executable.rs index dc27455c..9808584b 100644 --- a/crates/pet-python-utils/src/executable.rs +++ b/crates/pet-python-utils/src/executable.rs @@ -21,6 +21,8 @@ pub fn find_executable(env_path: &Path) -> Option { [ env_path.join("Scripts").join("python.exe"), env_path.join("Scripts").join("python3.exe"), + env_path.join("bin").join("python.exe"), + env_path.join("bin").join("python3.exe"), env_path.join("python.exe"), env_path.join("python3.exe"), ] @@ -41,13 +43,20 @@ pub fn find_executable(env_path: &Path) -> Option { } pub fn find_executables>(env_path: T) -> Vec { + let mut env_path = env_path.as_ref().to_path_buf(); // Never find exes in `.pyenv/shims/` folder, they are not valid exes - if env_path.as_ref().ends_with(".pyenv/shims") { + if env_path.ends_with(".pyenv/shims") { return vec![]; } let mut python_executables = vec![]; - let bin = if cfg!(windows) { "Scripts" } else { "bin" }; - let mut env_path = env_path.as_ref().to_path_buf(); + if cfg!(windows) { + // Only windows can have a Scripts folder + let bin = "Scripts"; + if env_path.join(bin).exists() { + env_path = env_path.join(bin); + } + } + let bin = "bin"; // Windows can have bin as well, https://github.com/microsoft/vscode-python/issues/24792 if env_path.join(bin).exists() { env_path = env_path.join(bin); } diff --git a/crates/pet-python-utils/src/headers.rs b/crates/pet-python-utils/src/headers.rs index e8848dc3..40497e28 100644 --- a/crates/pet-python-utils/src/headers.rs +++ b/crates/pet-python-utils/src/headers.rs @@ -19,7 +19,14 @@ pub struct Headers { impl Headers { pub fn get_version(path: &Path) -> Option { let mut path = path.to_path_buf(); - let bin = if cfg!(windows) { "Scripts" } else { "bin" }; + if cfg!(windows) { + // Only Windows can have a Scripts folder + let bin = "Scripts"; + if path.join(bin).exists() { + path = path.join(bin); + } + } + let bin = "bin"; // Windows can have bin as well, see https://github.com/microsoft/vscode-python/issues/24792 if path.ends_with(bin) { path.pop(); } diff --git a/crates/pet-python-utils/src/version.rs b/crates/pet-python-utils/src/version.rs index 85db00eb..2187e9b8 100644 --- a/crates/pet-python-utils/src/version.rs +++ b/crates/pet-python-utils/src/version.rs @@ -20,8 +20,12 @@ pub fn from_creator_for_virtual_env(prefix: &Path) -> Option { if let Some(version) = Headers::get_version(prefix) { return Some(version); } - let bin = if cfg!(windows) { "Scripts" } else { "bin" }; - let executable = &prefix.join(bin).join("python"); + let mut bin = "bin"; + let mut executable = prefix.join(bin).join("python"); + if cfg!(windows) && !executable.exists() { + bin = "Scripts"; + executable = prefix.join(bin).join("python.exe"); + } // Determine who created this virtual environment, and get version of that environment. // Note, its unlikely conda envs were used to create virtual envs, thats a very bad idea (known to cause issues and not reccomended). @@ -67,8 +71,14 @@ pub fn from_prefix(prefix: &Path) -> Option { /// Using this information its possible to determine the version of the Python environment used to create the env. fn get_python_exe_used_to_create_venv>(executable: T) -> Option { let parent_dir = executable.as_ref().parent()?; - let bin = if cfg!(windows) { "Scripts" } else { "bin" }; - if parent_dir.file_name().unwrap_or_default() != bin { + if cfg!(windows) { + if parent_dir.file_name().unwrap_or_default() != "bin" + && parent_dir.file_name().unwrap_or_default() != "Scripts" + { + warn!("Attempted to determine creator of virtual environment, but the env executable ({:?}) is not in the expected location.", executable.as_ref()); + return None; + } + } else if parent_dir.file_name().unwrap_or_default() != "bin" { warn!("Attempted to determine creator of virtual environment, but the env executable ({:?}) is not in the expected location.", executable.as_ref()); return None; } @@ -93,7 +103,11 @@ fn get_version_from_pyvenv_if_pyvenv_cfg_and_exe_created_same_time( return None; } let cfg_metadata = pyvenv_cfg.metadata().ok()?; - let exe_metadata = prefix.join("Scripts").join("python.exe").metadata().ok()?; + let mut bin = prefix.join("Scripts"); + if !bin.exists() { + bin = prefix.join("bin"); + } + let exe_metadata = bin.join("python.exe").metadata().ok()?; let cfg_modified = cfg_metadata .modified() .ok()? diff --git a/crates/pet-virtualenv/src/lib.rs b/crates/pet-virtualenv/src/lib.rs index e11a0cff..4159c815 100644 --- a/crates/pet-virtualenv/src/lib.rs +++ b/crates/pet-virtualenv/src/lib.rs @@ -30,15 +30,19 @@ pub fn is_virtualenv(env: &PythonEnv) -> bool { } pub fn is_virtualenv_dir(path: &Path) -> bool { + if cfg!(windows) { + is_virtualenv_dir_impl(path, "Scripts") || is_virtualenv_dir_impl(path, "bin") + } else { + is_virtualenv_dir_impl(path, "bin") + } +} + +fn is_virtualenv_dir_impl(path: &Path, bin: &str) -> bool { // Check if the executable is in a bin or Scripts directory. // Possible for some reason we do not have the prefix. let mut path = path.to_path_buf(); if !path.ends_with("bin") && !path.ends_with("Scripts") { - if cfg!(windows) { - path = path.join("Scripts"); - } else { - path = path.join("bin"); - } + path = path.join(bin); } // Never consider global locations to be virtualenvs