Skip to content

Commit 8a66345

Browse files
authored
Determine environments within the search instead of being asked to use a fallback (#76)
1 parent 762dabb commit 8a66345

5 files changed

Lines changed: 263 additions & 76 deletions

File tree

crates/pet/src/find.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
use log::{info, trace, warn};
55
use pet_conda::CondaLocator;
66
use pet_core::os_environment::{Environment, EnvironmentApi};
7-
use pet_core::python_environment::PythonEnvironmentCategory;
87
use pet_core::reporter::Reporter;
98
use pet_core::{Configuration, Locator};
109
use pet_env_var_path::get_search_paths_from_env_variables;
@@ -97,18 +96,19 @@ pub fn find_and_report_envs(
9796
s.spawn(|| {
9897
let start = std::time::Instant::now();
9998
let environment = EnvironmentApi::new();
100-
let search_paths: Vec<PathBuf> = get_search_paths_from_env_variables(&environment);
99+
let global_env_search_paths: Vec<PathBuf> =
100+
get_search_paths_from_env_variables(&environment);
101101

102102
trace!(
103103
"Searching for environments in global folders: {:?}",
104-
search_paths
104+
global_env_search_paths
105105
);
106106
find_python_environments(
107-
search_paths,
107+
global_env_search_paths.clone(),
108108
reporter,
109109
locators,
110110
false,
111-
Some(PythonEnvironmentCategory::GlobalPaths),
111+
&global_env_search_paths,
112112
);
113113
summary.lock().unwrap().find_path_time = start.elapsed();
114114
});
@@ -124,13 +124,21 @@ pub fn find_and_report_envs(
124124
environment_paths,
125125
]
126126
.concat();
127+
let global_env_search_paths: Vec<PathBuf> =
128+
get_search_paths_from_env_variables(&environment);
127129

128130
trace!(
129131
"Searching for environments in global venv folders: {:?}",
130132
search_paths
131133
);
132134

133-
find_python_environments(search_paths, reporter, locators, false, None);
135+
find_python_environments(
136+
search_paths,
137+
reporter,
138+
locators,
139+
false,
140+
&global_env_search_paths,
141+
);
134142
summary.lock().unwrap().find_global_virtual_envs_time = start.elapsed();
135143
});
136144
// Step 4: Find in workspace folders too.
@@ -175,7 +183,7 @@ fn find_python_environments_in_workspace_folders_recursive(
175183
// Find in cwd
176184
let paths1 = paths.clone();
177185
s.spawn(|| {
178-
find_python_environments(paths1, reporter, locators, true, None);
186+
find_python_environments(paths1, reporter, locators, true, &[]);
179187

180188
if depth >= max_depth {
181189
return;
@@ -233,7 +241,7 @@ fn find_python_environments(
233241
reporter: &dyn Reporter,
234242
locators: &Arc<Vec<Arc<dyn Locator>>>,
235243
is_workspace_folder: bool,
236-
fallback_category: Option<PythonEnvironmentCategory>,
244+
global_env_search_paths: &[PathBuf],
237245
) {
238246
if paths.is_empty() {
239247
return;
@@ -249,7 +257,7 @@ fn find_python_environments(
249257
&locators,
250258
reporter,
251259
is_workspace_folder,
252-
fallback_category,
260+
global_env_search_paths,
253261
);
254262
});
255263
}
@@ -261,7 +269,7 @@ fn find_python_environments_in_paths_with_locators(
261269
locators: &Arc<Vec<Arc<dyn Locator>>>,
262270
reporter: &dyn Reporter,
263271
is_workspace_folder: bool,
264-
fallback_category: Option<PythonEnvironmentCategory>,
272+
global_env_search_paths: &[PathBuf],
265273
) {
266274
let executables = if is_workspace_folder {
267275
// If we're in a workspace folder, then we only need to look for bin/python or bin/python.exe
@@ -291,20 +299,25 @@ fn find_python_environments_in_paths_with_locators(
291299
.collect::<Vec<PathBuf>>()
292300
};
293301

294-
identify_python_executables_using_locators(executables, locators, reporter, fallback_category);
302+
identify_python_executables_using_locators(
303+
executables,
304+
locators,
305+
reporter,
306+
global_env_search_paths,
307+
);
295308
}
296309

297310
fn identify_python_executables_using_locators(
298311
executables: Vec<PathBuf>,
299312
locators: &Arc<Vec<Arc<dyn Locator>>>,
300313
reporter: &dyn Reporter,
301-
fallback_category: Option<PythonEnvironmentCategory>,
314+
global_env_search_paths: &[PathBuf],
302315
) {
303316
for exe in executables.into_iter() {
304317
let executable = exe.clone();
305318
let env = PythonEnv::new(exe.to_owned(), None, None);
306319
if let Some(env) =
307-
identify_python_environment_using_locators(&env, locators, fallback_category)
320+
identify_python_environment_using_locators(&env, locators, global_env_search_paths)
308321
{
309322
reporter.report_environment(&env);
310323
if let Some(manager) = env.manager {

crates/pet/src/locators.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn create_locators(conda_locator: Arc<Conda>) -> Arc<Vec<Arc<dyn Locator>>>
8383
pub fn identify_python_environment_using_locators(
8484
env: &PythonEnv,
8585
locators: &[Arc<dyn Locator>],
86-
fallback_category: Option<PythonEnvironmentCategory>,
86+
global_env_search_paths: &[PathBuf],
8787
) -> Option<PythonEnvironment> {
8888
let executable = env.executable.clone();
8989
if let Some(env) = locators.iter().fold(
@@ -116,11 +116,25 @@ pub fn identify_python_environment_using_locators(
116116
// We have no idea what this is.
117117
// We have check all of the resolvers.
118118
// Telemetry point, failed to identify env here.
119+
let mut fallback_category = None;
120+
121+
// If one of the symlinks are in the PATH variable, then we can treat this as a GlobalPath category.
122+
let symlinks = [
123+
resolved_env.symlink.clone().unwrap_or_default(),
124+
vec![resolved_env.executable.clone(), executable.clone()],
125+
]
126+
.concat();
127+
for symlink in symlinks {
128+
if let Some(bin) = symlink.parent() {
129+
if global_env_search_paths.contains(&bin.to_path_buf()) {
130+
fallback_category = Some(PythonEnvironmentCategory::GlobalPaths);
131+
break;
132+
}
133+
}
134+
}
119135
info!(
120136
"Unknown Env ({:?}) in Path resolved as {:?} and reported as {:?}",
121-
executable,
122-
resolved_env,
123-
fallback_category.unwrap_or(PythonEnvironmentCategory::Unknown)
137+
executable, resolved_env, fallback_category
124138
);
125139
return Some(create_unknown_env(resolved_env, fallback_category));
126140
}

crates/pet/src/resolve.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33

44
use std::{path::PathBuf, sync::Arc};
55

6-
use log::warn;
7-
use pet_core::{arch::Architecture, python_environment::PythonEnvironment, Locator};
6+
use log::{trace, warn};
7+
use pet_core::{
8+
arch::Architecture, os_environment::EnvironmentApi, python_environment::PythonEnvironment,
9+
Locator,
10+
};
11+
use pet_env_var_path::get_search_paths_from_env_variables;
812
use pet_python_utils::env::{PythonEnv, ResolvedPythonEnv};
913

1014
use crate::locators::identify_python_environment_using_locators;
1115

16+
#[derive(Debug)]
1217
pub struct ResolvedEnvironment {
1318
pub discovered: PythonEnvironment,
1419
pub resolved: Option<PythonEnvironment>,
@@ -20,17 +25,31 @@ pub fn resolve_environment(
2025
) -> Option<ResolvedEnvironment> {
2126
// First check if this is a known environment
2227
let env = PythonEnv::new(executable.to_owned(), None, None);
23-
if let Some(env) = identify_python_environment_using_locators(&env, locators, None) {
28+
let environment = EnvironmentApi::new();
29+
let global_env_search_paths: Vec<PathBuf> = get_search_paths_from_env_variables(&environment);
30+
31+
if let Some(env) =
32+
identify_python_environment_using_locators(&env, locators, &global_env_search_paths)
33+
{
2434
// Ok we got the environment.
2535
// Now try to resolve this fully, by spawning python.
2636
if let Some(ref executable) = env.executable {
2737
if let Some(info) = ResolvedPythonEnv::from(executable) {
38+
trace!(
39+
"In resolve_environment, Resolved Python Exe {:?} as {:?}",
40+
executable,
41+
info
42+
);
2843
let discovered = env.clone();
2944
let mut resolved = env.clone();
3045
let mut symlinks = resolved.symlinks.clone().unwrap_or_default();
3146

3247
symlinks.push(info.executable.clone());
3348
symlinks.append(&mut info.symlink.clone().unwrap_or_default());
49+
symlinks.sort();
50+
symlinks.dedup();
51+
52+
resolved.symlinks = Some(symlinks);
3453
resolved.version = Some(info.version);
3554
resolved.prefix = Some(info.prefix);
3655
resolved.arch = Some(if info.is64_bit {

crates/pet/tests/ci_jupyter_container.rs

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
use std::sync::Once;
5+
46
mod common;
57

8+
static INIT: Once = Once::new();
9+
10+
/// Setup function that is only run once, even if called multiple times.
11+
fn setup() {
12+
INIT.call_once(|| {
13+
env_logger::builder()
14+
.filter(None, log::LevelFilter::Trace)
15+
.init();
16+
});
17+
}
18+
619
#[cfg(unix)]
720
#[cfg_attr(feature = "ci-jupyter-container", test)]
821
#[allow(dead_code)]
@@ -19,6 +32,8 @@ fn verify_python_in_jupyter_contaner() {
1932
use pet_reporter::test;
2033
use std::{path::PathBuf, sync::Arc};
2134

35+
setup();
36+
2237
let reporter = test::create_reporter();
2338
let environment = EnvironmentApi::new();
2439
let conda_locator = Arc::new(Conda::from(&environment));
@@ -121,23 +136,53 @@ fn verify_python_in_jupyter_contaner() {
121136
.iter()
122137
.find(|e| e.executable == env.executable)
123138
.expect(format!("Expected to find python environment {:?}", env.executable).as_str());
124-
assert_eq!(python_env.executable, env.executable);
125-
assert_eq!(python_env.category, env.category);
126-
assert_eq!(python_env.symlinks, env.symlinks);
127-
assert_eq!(python_env.manager, env.manager);
128-
assert_eq!(python_env.name, env.name);
129-
assert_eq!(python_env.version, env.version);
130-
assert_eq!(python_env.arch, env.arch);
139+
assert_eq!(
140+
python_env.executable, env.executable,
141+
"Expected exe to be same when comparing {:?} and {:?}",
142+
python_env, env
143+
);
144+
assert_eq!(
145+
python_env.category, env.category,
146+
"Expected category to be same when comparing {:?} and {:?}",
147+
python_env, env
148+
);
149+
assert_eq!(
150+
python_env.symlinks, env.symlinks,
151+
"Expected symlinks to be same when comparing {:?} and {:?}",
152+
python_env, env
153+
);
154+
assert_eq!(
155+
python_env.manager, env.manager,
156+
"Expected manager to be same when comparing {:?} and {:?}",
157+
python_env, env
158+
);
159+
assert_eq!(
160+
python_env.name, env.name,
161+
"Expected name to be same when comparing {:?} and {:?}",
162+
python_env, env
163+
);
164+
assert_eq!(
165+
python_env.version, env.version,
166+
"Expected version to be same when comparing {:?} and {:?}",
167+
python_env, env
168+
);
169+
assert_eq!(
170+
python_env.arch, env.arch,
171+
"Expected arch to be same when comparing {:?} and {:?}",
172+
python_env, env
173+
);
131174

132175
// known issue https://github.com/microsoft/python-environment-tools/issues/64
133176
if env.executable == Some(PathBuf::from("/home/codespace/.python/current/bin/python")) {
134177
assert!(
135178
python_env.prefix == Some(PathBuf::from("/home/codespace/.python/current"))
136179
|| python_env.prefix == Some(PathBuf::from("/usr/local/python/3.10.13")),
137-
"Expected {:?} to be {:?} or {:?}",
180+
"Expected {:?} to be {:?} or {:?} when comparing {:?} and {:?}",
138181
python_env.prefix,
139182
"/home/codespace/.python/current",
140-
"/usr/local/python/3.10.13"
183+
"/usr/local/python/3.10.13",
184+
python_env,
185+
env
141186
);
142187
}
143188
}

0 commit comments

Comments
 (0)