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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 35 additions & 9 deletions crates/pet-conda/src/environment_locations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::{
env, fs,
path::{Path, PathBuf},
thread,
time::SystemTime,
};

const APP_NAME: &str = "conda";
Expand All @@ -21,6 +22,7 @@ pub fn get_conda_environment_paths(
env_vars: &EnvVariables,
conda_executable: &Option<PathBuf>,
) -> Vec<PathBuf> {
let start = SystemTime::now();
let mut env_paths = thread::scope(|s| {
let mut envs = vec![];
for thread in [
Expand All @@ -43,7 +45,7 @@ pub fn get_conda_environment_paths(
// & then iterate through the list of envs in the envs directory.
// let env_paths = vec![];
let mut threads = vec![];
for path in env_paths {
for path in env_paths.iter().filter(|f| f.exists()) {
let path = path.clone();
threads.push(thread::spawn(move || get_environments(&path)));
}
Expand All @@ -57,6 +59,10 @@ pub fn get_conda_environment_paths(

result.sort();
result.dedup();
trace!(
"Time taken to get conda environment paths: {:?}",
start.elapsed().unwrap()
);
result
}

Expand All @@ -68,13 +74,24 @@ fn get_conda_environment_paths_from_conda_rc(env_vars: &EnvVariables) -> Vec<Pat
// Use the conda rc directories as well.
let mut env_dirs = vec![];
for rc_file_dir in get_conda_rc_search_paths(env_vars) {
if !rc_file_dir.exists() {
continue;
}

if let Some(conda_rc) = Condarc::from_path(&rc_file_dir) {
trace!(
"Conda environments in .condarc {:?} {:?}",
conda_rc.files,
conda_rc.env_dirs
);
env_dirs.append(&mut conda_rc.env_dirs.clone());
env_dirs.append(
&mut conda_rc
.env_dirs
.clone()
.into_iter()
.filter(|f| f.exists())
.collect(),
);
}

if rc_file_dir.is_dir() {
Expand Down Expand Up @@ -151,6 +168,9 @@ fn get_conda_environment_paths_from_known_paths(env_vars: &EnvVariables) -> Vec<
}
}
env_paths.append(&mut env_vars.known_global_search_locations.clone());
env_paths.sort();
env_paths.dedup();
let env_paths = env_paths.into_iter().filter(|f| f.exists()).collect();
trace!("Conda environments in known paths {:?}", env_paths);
env_paths
}
Expand Down Expand Up @@ -217,7 +237,9 @@ pub fn get_conda_envs_from_environment_txt(env_vars: &EnvVariables) -> Vec<PathB
for line in reader.lines() {
let line = norm_case(&PathBuf::from(line.to_string()));
trace!("Conda env in environments.txt file {:?}", line);
envs.push(line);
if line.exists() {
envs.push(line);
}
}
}
}
Expand Down Expand Up @@ -364,7 +386,7 @@ pub fn get_known_conda_install_locations(
}
if let Some(home) = env_vars.home.clone() {
// https://stackoverflow.com/questions/35709497/anaconda-python-where-are-the-virtual-environments-stored
for prefix in [
let mut prefixes = vec![
home.clone(),
// https://towardsdatascience.com/manage-your-python-virtual-environment-with-conda-a0d2934d5195
home.join("opt"),
Expand All @@ -375,9 +397,14 @@ pub fn get_known_conda_install_locations(
PathBuf::from("/usr/share"),
PathBuf::from("/usr/local"),
PathBuf::from("/usr"),
PathBuf::from("/opt/homebrew"),
PathBuf::from("/home/linuxbrew/.linuxbrew"),
] {
];
if std::env::consts::OS == "macos" {
prefixes.push(PathBuf::from("/opt/homebrew"));
} else {
prefixes.push(PathBuf::from("/home/linuxbrew/.linuxbrew"));
}

for prefix in prefixes {
known_paths.push(prefix.clone().join("anaconda"));
known_paths.push(prefix.clone().join("anaconda3"));
known_paths.push(prefix.clone().join("miniconda"));
Expand All @@ -395,8 +422,7 @@ pub fn get_known_conda_install_locations(
}
known_paths.sort();
known_paths.dedup();

known_paths
known_paths.into_iter().filter(|f| f.exists()).collect()
}

pub fn get_conda_dir_from_exe(conda_executable: &Option<PathBuf>) -> Option<PathBuf> {
Expand Down
6 changes: 3 additions & 3 deletions crates/pet-conda/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use pet_core::{
os_environment::Environment,
python_environment::{PythonEnvironment, PythonEnvironmentKind},
reporter::Reporter,
Locator,
Locator, LocatorKind,
};
use pet_fs::path::norm_case;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -203,8 +203,8 @@ impl Conda {
}

impl Locator for Conda {
fn get_name(&self) -> &'static str {
"Conda" // Do not change this name, as this is used in telemetry.
fn get_kind(&self) -> LocatorKind {
LocatorKind::Conda
}
fn configure(&self, config: &pet_core::Configuration) {
if let Some(ref conda_exe) = config.conda_executable {
Expand Down
46 changes: 0 additions & 46 deletions crates/pet-conda/tests/environment_locations_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,6 @@

mod common;

#[cfg(unix)]
#[test]
fn read_environment_txt() {
use common::{create_env_variables, resolve_test_path};
use pet_conda::environment_locations::get_conda_envs_from_environment_txt;
use std::path::PathBuf;

let root = resolve_test_path(&["unix", "root_empty"]).into();
let home = resolve_test_path(&["unix", "user_home_with_environments_txt"]).into();
let env = create_env_variables(home, root);

let mut environments = get_conda_envs_from_environment_txt(&env);
environments.sort();

let mut expected = vec![
Copy link
Collaborator Author

@DonJayamanne DonJayamanne Jul 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed test, was not easy to maintain as the paths do not exist
Not a critical test,

"/Users/username/miniconda3",
"/Users/username/miniconda3/envs/xyz",
"/Users/username/miniconda3/envs/conda1",
"/Users/username/miniconda3/envs/conda2",
"/Users/username/miniconda3/envs/conda3",
"/Users/username/miniconda3/envs/conda4",
"/Users/username/miniconda3/envs/conda5",
"/Users/username/miniconda3/envs/conda6",
"/Users/username/miniconda3/envs/conda7",
"/Users/username/miniconda3/envs/conda8",
"/Users/username/.pyenv/versions/miniconda3-latest",
"/Users/username/.pyenv/versions/miniconda3-latest/envs/myenv",
"/Users/username/.pyenv/versions/miniforge3-4.10.1-1",
"/Users/username/.pyenv/versions/anaconda3-2023.03",
"/Users/username/miniforge3/envs/sample1",
"/Users/username/temp/conda_work_folder",
"/Users/username/temp/conda_work_folder_3.12",
"/Users/username/temp/conda_work_folder__no_python",
"/Users/username/temp/conda_work_folder_from_root",
"/Users/username/temp/sample-conda-envs-folder/hello_world",
"/Users/username/temp/sample-conda-envs-folder2/another",
"/Users/username/temp/sample-conda-envs-folder2/xyz",
]
.iter()
.map(PathBuf::from)
.collect::<Vec<PathBuf>>();
expected.sort();

assert_eq!(environments, expected);
}

#[cfg(unix)]
#[test]
fn non_existent_envrionments_txt() {
Expand Down
1 change: 1 addition & 0 deletions crates/pet-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
msvc_spectre_libs = { version = "0.1.1", features = ["error"] }

[dependencies]
clap = { version = "4.5.4", features = ["derive", "cargo"] }
pet-fs = { path = "../pet-fs" }
serde = { version = "1.0.152", features = ["derive"] }
lazy_static = "1.4.0"
Expand Down
21 changes: 20 additions & 1 deletion crates/pet-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct LocatorResult {
pub struct Configuration {
/// These are paths like workspace folders, where we can look for environments.
pub workspace_directories: Option<Vec<PathBuf>>,
pub executables: Option<Vec<PathBuf>>,
pub conda_executable: Option<PathBuf>,
pub poetry_executable: Option<PathBuf>,
/// Custom locations where environments can be found.
Expand All @@ -37,9 +38,27 @@ pub struct Configuration {
pub cache_directory: Option<PathBuf>,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum LocatorKind {
Conda,
Homebrew,
LinuxGlobal,
MacCommandLineTools,
MacPythonOrg,
MacXCode,
PipEnv,
Poetry,
PyEnv,
Venv,
VirtualEnv,
VirtualEnvWrapper,
WindowsRegistry,
WindowsStore,
}

pub trait Locator: Send + Sync {
/// Returns the name of the locator.
fn get_name(&self) -> &'static str;
fn get_kind(&self) -> LocatorKind;
/// Configures the locator with the given configuration.
/// Override this method if you need to have some custom configuration.
/// E.g. storing some of the configuration information in the locator.
Expand Down
6 changes: 3 additions & 3 deletions crates/pet-core/src/python_environment.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use clap::{Parser, ValueEnum};
use log::error;
use pet_fs::path::norm_case;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

use crate::{arch::Architecture, manager::EnvManager};

#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[derive(Parser, ValueEnum, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum PythonEnvironmentKind {
Conda,
Homebrew,
Pyenv, // Relates to Python installations in pyenv that are from Python org.
Pyenv,
GlobalPaths, // Python found in global locations like PATH, /usr/bin etc.
PyenvVirtualEnv, // Pyenv virtualenvs.
Pipenv,
Expand All @@ -21,7 +22,6 @@ pub enum PythonEnvironmentKind {
MacCommandLineTools,
LinuxGlobal,
MacXCode,
Unknown,
Venv,
VirtualEnv,
VirtualEnvWrapper,
Expand Down
6 changes: 3 additions & 3 deletions crates/pet-homebrew/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use pet_core::{
os_environment::Environment,
python_environment::{PythonEnvironment, PythonEnvironmentKind},
reporter::Reporter,
Locator,
Locator, LocatorKind,
};
use pet_fs::path::resolve_symlink;
use pet_python_utils::executable::find_executables;
Expand Down Expand Up @@ -110,8 +110,8 @@ fn from(env: &PythonEnv) -> Option<PythonEnvironment> {
}

impl Locator for Homebrew {
fn get_name(&self) -> &'static str {
"Homebrew" // Do not change this name, as this is used in telemetry.
fn get_kind(&self) -> LocatorKind {
LocatorKind::Homebrew
}
fn supported_categories(&self) -> Vec<PythonEnvironmentKind> {
vec![PythonEnvironmentKind::Homebrew]
Expand Down
6 changes: 3 additions & 3 deletions crates/pet-linux-global-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use pet_core::{
env::PythonEnv,
python_environment::{PythonEnvironment, PythonEnvironmentBuilder, PythonEnvironmentKind},
reporter::Reporter,
Locator,
Locator, LocatorKind,
};
use pet_fs::path::resolve_symlink;
use pet_python_utils::{env::ResolvedPythonEnv, executable::find_executables};
Expand Down Expand Up @@ -53,8 +53,8 @@ impl Default for LinuxGlobalPython {
}
}
impl Locator for LinuxGlobalPython {
fn get_name(&self) -> &'static str {
"LinuxGlobalPython" // Do not change this name, as this is used in telemetry.
fn get_kind(&self) -> LocatorKind {
LocatorKind::LinuxGlobal
}
fn supported_categories(&self) -> Vec<PythonEnvironmentKind> {
vec![PythonEnvironmentKind::LinuxGlobal]
Expand Down
20 changes: 17 additions & 3 deletions crates/pet-mac-commandlinetools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
// Licensed under the MIT License.

use pet_core::{
arch::Architecture,
env::PythonEnv,
python_environment::{PythonEnvironment, PythonEnvironmentBuilder, PythonEnvironmentKind},
reporter::Reporter,
Locator,
Locator, LocatorKind,
};
use pet_fs::path::resolve_symlink;
use pet_python_utils::version;
Expand All @@ -26,8 +27,8 @@ impl Default for MacCmdLineTools {
}
}
impl Locator for MacCmdLineTools {
fn get_name(&self) -> &'static str {
"MacCmdLineTools" // Do not change this name, as this is used in telemetry.
fn get_kind(&self) -> LocatorKind {
LocatorKind::MacCommandLineTools
}
fn supported_categories(&self) -> Vec<PythonEnvironmentKind> {
vec![PythonEnvironmentKind::MacCommandLineTools]
Expand Down Expand Up @@ -57,6 +58,7 @@ impl Locator for MacCmdLineTools {
let mut version = env.version.clone();
let mut prefix = env.prefix.clone();
let mut symlinks = vec![env.executable.clone()];
let mut arch = None;

let existing_symlinks = env.symlinks.clone();
if let Some(existing_symlinks) = existing_symlinks {
Expand Down Expand Up @@ -113,6 +115,11 @@ impl Locator for MacCmdLineTools {
// Use the latest accurate information we have.
version = Some(resolved_env.version);
prefix = Some(resolved_env.prefix);
arch = if resolved_env.is64_bit {
Some(Architecture::X64)
} else {
Some(Architecture::X86)
};
}
}
}
Expand Down Expand Up @@ -165,18 +172,25 @@ impl Locator for MacCmdLineTools {
version = version::from_header_files(prefix);
}
}

if version.is_none() || prefix.is_none() {
if let Some(resolved_env) = ResolvedPythonEnv::from(&env.executable) {
resolved_environments.push(resolved_env.clone());
version = Some(resolved_env.version);
prefix = Some(resolved_env.prefix);
arch = if resolved_env.is64_bit {
Some(Architecture::X64)
} else {
Some(Architecture::X86)
};
}
}

let env = PythonEnvironmentBuilder::new(Some(PythonEnvironmentKind::MacCommandLineTools))
.executable(Some(env.executable.clone()))
.version(version)
.prefix(prefix)
.arch(arch)
.symlinks(Some(symlinks.clone()))
.build();

Expand Down
Loading