Skip to content

Commit 9c7809e

Browse files
authored
Identify and report conda envs not found (#97)
1 parent 439c66f commit 9c7809e

File tree

20 files changed

+598
-68
lines changed

20 files changed

+598
-68
lines changed

crates/pet-conda/src/conda_info.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ use std::path::PathBuf;
99
pub struct CondaInfo {
1010
pub executable: PathBuf,
1111
pub envs: Vec<PathBuf>,
12-
pub conda_prefix: PathBuf,
12+
pub conda_prefix: Option<PathBuf>,
1313
pub conda_version: String,
1414
pub envs_dirs: Vec<PathBuf>,
15-
pub envs_path: Vec<PathBuf>,
1615
pub config_files: Vec<PathBuf>,
1716
pub rc_path: Option<PathBuf>,
1817
pub sys_rc_path: Option<PathBuf>,
1918
pub user_rc_path: Option<PathBuf>,
19+
pub root_prefix: Option<PathBuf>,
2020
}
2121

2222
#[derive(Debug, serde::Deserialize)]
@@ -25,11 +25,13 @@ pub struct CondaInfoJson {
2525
pub conda_prefix: Option<PathBuf>,
2626
pub conda_version: Option<String>,
2727
pub envs_dirs: Option<Vec<PathBuf>>,
28+
/// This is an alias for envs_dirs
2829
pub envs_path: Option<Vec<PathBuf>>,
2930
pub config_files: Option<Vec<PathBuf>>,
3031
pub rc_path: Option<PathBuf>,
3132
pub user_rc_path: Option<PathBuf>,
3233
pub sys_rc_path: Option<PathBuf>,
34+
pub root_prefix: Option<PathBuf>,
3335
}
3436

3537
impl CondaInfo {
@@ -47,23 +49,34 @@ impl CondaInfo {
4749
.arg("info")
4850
.arg("--json")
4951
.output();
50-
trace!("Executing Conda: {:?} info --json", executable);
52+
trace!("Executing Conda: {:?} info --json -a", executable);
5153
match result {
5254
Ok(output) => {
5355
if output.status.success() {
5456
let output = String::from_utf8_lossy(&output.stdout).to_string();
5557
match serde_json::from_str::<CondaInfoJson>(output.trim()) {
5658
Ok(info) => {
59+
let envs_path = info
60+
.envs_path
61+
.unwrap_or_default()
62+
.drain(..)
63+
.collect::<Vec<PathBuf>>();
64+
let mut envs_dirs = info
65+
.envs_dirs
66+
.unwrap_or_default()
67+
.drain(..)
68+
.collect::<Vec<PathBuf>>();
69+
envs_dirs.extend(envs_path);
5770
let info = CondaInfo {
5871
executable: executable.clone(),
5972
envs: info.envs.unwrap_or_default().drain(..).collect(),
60-
conda_prefix: info.conda_prefix.unwrap_or_default(),
73+
conda_prefix: info.conda_prefix,
74+
root_prefix: info.root_prefix,
6175
rc_path: info.rc_path,
6276
sys_rc_path: info.sys_rc_path,
6377
user_rc_path: info.user_rc_path,
78+
envs_dirs,
6479
conda_version: info.conda_version.unwrap_or_default(),
65-
envs_dirs: info.envs_dirs.unwrap_or_default().drain(..).collect(),
66-
envs_path: info.envs_path.unwrap_or_default().drain(..).collect(),
6780
config_files: info
6881
.config_files
6982
.unwrap_or_default()

crates/pet-conda/src/conda_rc.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::env_variables::EnvVariables;
55
use log::trace;
66
use pet_fs::path::expand_path;
77
use std::{
8+
collections::HashSet,
89
fs,
910
path::{Path, PathBuf},
1011
};
@@ -93,11 +94,19 @@ fn get_conda_rc_search_paths(env_vars: &EnvVariables) -> Vec<PathBuf> {
9394
PathBuf::from(conda_prefix.clone()).join(".condarc.d"),
9495
]);
9596
}
97+
if let Some(ref conda_dir) = env_vars.conda_dir {
98+
search_paths.append(&mut vec![
99+
PathBuf::from(conda_dir.clone()).join(".condarc"),
100+
PathBuf::from(conda_dir.clone()).join("condarc"),
101+
PathBuf::from(conda_dir.clone()).join(".condarc.d"),
102+
]);
103+
}
96104
if let Some(ref condarc) = env_vars.condarc {
97105
search_paths.append(&mut vec![PathBuf::from(condarc)]);
98106
}
99107

100-
search_paths
108+
let search_paths: HashSet<_> = search_paths.into_iter().collect();
109+
search_paths.into_iter().collect()
101110
}
102111

103112
// https://github.com/conda/conda/blob/3ae5d7cf6cbe2b0ff9532359456b7244ae1ea5ef/conda/common/configuration.py#L1315

crates/pet-conda/src/env_variables.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub struct EnvVariables {
1818
pub programdata: Option<String>,
1919
pub homedrive: Option<String>,
2020
pub conda_root: Option<String>,
21+
/// https://github.com/jupyter/docker-stacks/issues/1086
22+
pub conda_dir: Option<String>,
2123
pub conda: Option<String>,
2224
pub conda_prefix: Option<String>,
2325
/// https://docs.conda.io/projects/conda/en/22.11.x/user-guide/configuration/use-condarc.html
@@ -37,6 +39,7 @@ impl EnvVariables {
3739
allusersprofile: env.get_env_var("ALLUSERSPROFILE".to_string()),
3840
programdata: env.get_env_var("PROGRAMDATA".to_string()),
3941
homedrive: env.get_env_var("HOMEDRIVE".to_string()),
42+
conda_dir: env.get_env_var("CONDA_DIR".to_string()),
4043
conda_root: env.get_env_var("CONDA_ROOT".to_string()),
4144
conda: env.get_env_var("CONDA".to_string()),
4245
conda_prefix: env.get_env_var("CONDA_PREFIX".to_string()),

crates/pet-conda/src/environment_locations.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ pub fn get_known_conda_install_locations(env_vars: &EnvVariables) -> Vec<PathBuf
302302
if let Some(ref conda_prefix) = env_vars.conda_prefix {
303303
known_paths.push(expand_path(PathBuf::from(conda_prefix.clone())));
304304
}
305+
if let Some(ref conda_dir) = env_vars.conda_dir {
306+
known_paths.push(expand_path(PathBuf::from(conda_dir.clone())));
307+
}
305308
if let Some(ref conda) = env_vars.conda {
306309
known_paths.push(expand_path(PathBuf::from(conda)));
307310
}

crates/pet-conda/src/environments.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pub fn get_conda_environment_info(
7777
}
7878
// If we know the conda install folder, then we can use it.
7979
let mut conda_install_folder = match manager {
80-
Some(manager) => Some(manager.conda_dir.clone()),
80+
Some(manager) => manager.conda_dir.clone(),
8181
None => get_conda_installation_used_to_create_conda_env(env_path),
8282
};
8383
if let Some(conda_dir) = &conda_install_folder {

crates/pet-conda/src/lib.rs

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use conda_info::CondaInfo;
55
use env_variables::EnvVariables;
66
use environment_locations::{get_conda_environment_paths, get_environments};
77
use environments::{get_conda_environment_info, CondaEnvironment};
8-
use log::{error, warn};
8+
use log::error;
99
use manager::CondaManager;
1010
use pet_core::{
1111
os_environment::Environment,
@@ -20,6 +20,7 @@ use std::{
2020
sync::{Arc, Mutex},
2121
thread,
2222
};
23+
use telemetry::report_missing_envs;
2324
use utils::{is_conda_env, is_conda_install};
2425

2526
mod conda_info;
@@ -29,11 +30,16 @@ pub mod environment_locations;
2930
pub mod environments;
3031
pub mod manager;
3132
pub mod package;
33+
mod telemetry;
3234
pub mod utils;
3335

3436
pub trait CondaLocator: Send + Sync {
3537
fn find_in(&self, path: &Path) -> Option<LocatorResult>;
36-
fn find_with_conda_executable(&self, conda_executable: Option<PathBuf>) -> Option<()>;
38+
fn find_and_report_missing_envs(
39+
&self,
40+
reporter: &dyn Reporter,
41+
conda_executable: Option<PathBuf>,
42+
) -> Option<()>;
3743
}
3844
pub struct Conda {
3945
/// Directories where conda environments are found (env_dirs returned from `conda info --json`)
@@ -55,11 +61,29 @@ impl Conda {
5561
}
5662

5763
impl CondaLocator for Conda {
58-
fn find_with_conda_executable(&self, conda_executable: Option<PathBuf>) -> Option<()> {
59-
let info = CondaInfo::from(conda_executable)?;
60-
// Have we seen these envs, if yes, then nothing to do
64+
fn find_and_report_missing_envs(
65+
&self,
66+
reporter: &dyn Reporter,
67+
conda_executable: Option<PathBuf>,
68+
) -> Option<()> {
69+
// Look for environments that we couldn't find without spawning conda.
70+
let user_provided_conda_exe = conda_executable.is_some();
71+
let conda_info = CondaInfo::from(conda_executable)?;
72+
73+
// Keep track of these directories for next refresh
74+
// This way we can search these directories again and they will be reported.
75+
{
76+
let mut env_dirs = self.env_dirs.lock().unwrap();
77+
env_dirs.append(&mut conda_info.envs_dirs.clone());
78+
conda_info.envs_dirs.iter().for_each(|p| {
79+
if !env_dirs.contains(p) {
80+
env_dirs.push(p.clone());
81+
}
82+
});
83+
}
84+
6185
let environments = self.environments.lock().unwrap().clone();
62-
let mut new_envs = info
86+
let new_envs = conda_info
6387
.envs
6488
.clone()
6589
.into_iter()
@@ -68,61 +92,57 @@ impl CondaLocator for Conda {
6892
if new_envs.is_empty() {
6993
return None;
7094
}
95+
let environments = environments
96+
.into_values()
97+
.collect::<Vec<PythonEnvironment>>();
7198

72-
// Oh oh, we have new envs, lets find them.
73-
let manager = CondaManager::from_info(&info.executable, &info)?;
74-
for path in new_envs.iter() {
75-
let mgr = manager.clone();
76-
if let Some(env) = get_conda_environment_info(path, &Some(mgr.clone())) {
77-
warn!(
78-
"Found a conda env {:?} using the conda exe {:?}",
79-
env.prefix, info.executable
80-
);
81-
}
82-
}
83-
84-
// Also keep track of these directories for next time
85-
let mut env_dirs = self.env_dirs.lock().unwrap();
86-
env_dirs.append(&mut new_envs);
87-
88-
// Send telemetry
99+
let _ = report_missing_envs(
100+
reporter,
101+
&self.env_vars,
102+
&new_envs,
103+
&environments,
104+
&conda_info,
105+
user_provided_conda_exe,
106+
);
89107

90108
Some(())
91109
}
110+
92111
fn find_in(&self, conda_dir: &Path) -> Option<LocatorResult> {
93112
if !is_conda_install(conda_dir) {
94113
return None;
95114
}
96115
if let Some(manager) = CondaManager::from(conda_dir) {
97-
let conda_dir = manager.conda_dir.clone();
98-
// Keep track to search again later.
99-
// Possible we'll find environments in other directories created using this manager
100-
let mut managers = self.managers.lock().unwrap();
101-
// Keep track to search again later.
102-
// Possible we'll find environments in other directories created using this manager
103-
managers.insert(conda_dir.clone(), manager.clone());
104-
drop(managers);
116+
if let Some(conda_dir) = manager.conda_dir.clone() {
117+
// Keep track to search again later.
118+
// Possible we'll find environments in other directories created using this manager
119+
let mut managers = self.managers.lock().unwrap();
120+
// Keep track to search again later.
121+
// Possible we'll find environments in other directories created using this manager
122+
managers.insert(conda_dir.clone(), manager.clone());
123+
drop(managers);
105124

106-
let mut new_environments = vec![];
125+
let mut new_environments = vec![];
107126

108-
// Find all the environments in the conda install folder. (under `envs` folder)
109-
for conda_env in
110-
get_conda_environments(&get_environments(&conda_dir), &manager.clone().into())
111-
{
112-
let mut environments = self.environments.lock().unwrap();
113-
if environments.contains_key(&conda_env.prefix) {
114-
continue;
127+
// Find all the environments in the conda install folder. (under `envs` folder)
128+
for conda_env in
129+
get_conda_environments(&get_environments(&conda_dir), &manager.clone().into())
130+
{
131+
let mut environments = self.environments.lock().unwrap();
132+
if environments.contains_key(&conda_env.prefix) {
133+
continue;
134+
}
135+
let env = conda_env
136+
.to_python_environment(Some(conda_dir.clone()), Some(manager.to_manager()));
137+
environments.insert(conda_env.prefix.clone(), env.clone());
138+
new_environments.push(env);
115139
}
116-
let env = conda_env
117-
.to_python_environment(Some(conda_dir.clone()), Some(manager.to_manager()));
118-
environments.insert(conda_env.prefix.clone(), env.clone());
119-
new_environments.push(env);
120-
}
121140

122-
return Some(LocatorResult {
123-
environments: new_environments,
124-
managers: vec![manager.to_manager()],
125-
});
141+
return Some(LocatorResult {
142+
environments: new_environments,
143+
managers: vec![manager.to_manager()],
144+
});
145+
}
126146
}
127147
None
128148
}
@@ -279,7 +299,7 @@ impl Locator for Conda {
279299
// 5. Report this env.
280300
if let Some(manager) = manager {
281301
let env = env.to_python_environment(
282-
Some(manager.conda_dir.clone()),
302+
manager.conda_dir.clone(),
283303
Some(manager.to_manager()),
284304
);
285305
let mut environments = self.environments.lock().unwrap();

crates/pet-conda/src/manager.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub fn find_conda_binary(env_vars: &EnvVariables) -> Option<PathBuf> {
6161
pub struct CondaManager {
6262
pub executable: PathBuf,
6363
pub version: Option<String>,
64-
pub conda_dir: PathBuf,
64+
pub conda_dir: Option<PathBuf>,
6565
}
6666

6767
impl CondaManager {
@@ -108,7 +108,7 @@ fn get_conda_manager(path: &Path) -> Option<CondaManager> {
108108
Some(CondaManager {
109109
executable: conda_exe,
110110
version: Some(conda_pkg.version),
111-
conda_dir: path.to_path_buf(),
111+
conda_dir: Some(path.to_path_buf()),
112112
})
113113
} else {
114114
None

0 commit comments

Comments
 (0)