Skip to content

Commit 15a9e5c

Browse files
Fix env duplication on merged-usr systems (#200)
On modern (merged-usr) Linux systems, `/bin`, `/sbin` and `/usr/sbin` are symlinks to `/usr/bin`. There is no point in reporting the same Python installation four times, so canonicalize search paths before searching. Before: Breakdown for finding Environments: ----------------------------------- GlobalVirtualEnvs : 17.412201ms Locators : 225.284494ms Path : 433.162905ms Workspaces : 2.161556ms Environments (41): ------------------ GlobalPaths : 14 LinuxGlobal : 16 VirtualEnvWrapper : 11 After: Breakdown for finding Environments: ----------------------------------- GlobalVirtualEnvs : 16.595382ms Locators : 223.759511ms Path : 313.276036ms Workspaces : 1.418024ms Environments (21): ------------------ GlobalPaths : 2 LinuxGlobal : 8 VirtualEnvWrapper : 11 --------- Co-authored-by: Karthik Nadig <[email protected]>
1 parent d5b5bb0 commit 15a9e5c

File tree

3 files changed

+23
-47
lines changed

3 files changed

+23
-47
lines changed

crates/pet-env-var-path/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the MIT License.
33

44
use pet_core::os_environment::Environment;
5+
use std::collections::HashSet;
6+
use std::fs;
57
use std::path::PathBuf;
68

79
pub fn get_search_paths_from_env_variables(environment: &dyn Environment) -> Vec<PathBuf> {
@@ -16,10 +18,12 @@ pub fn get_search_paths_from_env_variables(environment: &dyn Environment) -> Vec
1618

1719
environment
1820
.get_know_global_search_locations()
19-
.clone()
21+
.into_iter()
22+
.map(|p| fs::canonicalize(&p).unwrap_or(p))
23+
.collect::<HashSet<PathBuf>>()
2024
.into_iter()
2125
.filter(|p| !p.starts_with(apps_path.clone()))
22-
.collect::<Vec<PathBuf>>()
26+
.collect()
2327
} else {
2428
Vec::new()
2529
}

crates/pet-linux-global-python/src/lib.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License.
33

44
use std::{
5-
collections::HashMap,
5+
collections::{HashMap, HashSet},
66
fs,
77
path::{Path, PathBuf},
88
sync::{Arc, Mutex},
@@ -38,10 +38,17 @@ impl LinuxGlobalPython {
3838
return;
3939
}
4040
// Look through the /bin, /usr/bin, /usr/local/bin directories
41+
let bin_dirs: HashSet<_> = [
42+
Path::new("/bin"),
43+
Path::new("/usr/bin"),
44+
Path::new("/usr/local/bin"),
45+
]
46+
.map(|p| fs::canonicalize(p).unwrap_or(p.to_path_buf()))
47+
.into();
4148
thread::scope(|s| {
42-
for bin in ["/bin", "/usr/bin", "/usr/local/bin"] {
49+
for bin in bin_dirs {
4350
s.spawn(move || {
44-
find_and_report_global_pythons_in(bin, reporter, &self.reported_executables);
51+
find_and_report_global_pythons_in(&bin, reporter, &self.reported_executables);
4552
});
4653
}
4754
});
@@ -103,11 +110,11 @@ impl Locator for LinuxGlobalPython {
103110
}
104111

105112
fn find_and_report_global_pythons_in(
106-
bin: &str,
113+
bin: &Path,
107114
reporter: Option<&dyn Reporter>,
108115
reported_executables: &Arc<Mutex<HashMap<PathBuf, PythonEnvironment>>>,
109116
) {
110-
let python_executables = find_executables(Path::new(bin));
117+
let python_executables = find_executables(bin);
111118

112119
for exe in python_executables.clone().iter() {
113120
if reported_executables.lock().unwrap().contains_key(exe) {

crates/pet/tests/ci_jupyter_container.rs

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -71,30 +71,16 @@ fn verify_python_in_jupyter_contaner() {
7171
}),
7272
..Default::default()
7373
};
74-
let codespace_python = PythonEnvironment {
75-
kind: Some(PythonEnvironmentKind::GlobalPaths),
76-
executable: Some(PathBuf::from("/home/codespace/.python/current/bin/python")),
77-
prefix: Some(PathBuf::from("/usr/local/python/3.10.13")),
78-
version: Some("3.10.13.final.0".to_string()),
79-
arch: Some(Architecture::X64),
80-
symlinks: Some(vec![
81-
PathBuf::from("/home/codespace/.python/current/bin/python"),
82-
PathBuf::from("/home/codespace/.python/current/bin/python3"),
83-
PathBuf::from("/home/codespace/.python/current/bin/python3.10"),
84-
]),
85-
manager: None,
86-
..Default::default()
87-
};
8874
let current_python = PythonEnvironment {
8975
kind: Some(PythonEnvironmentKind::GlobalPaths),
90-
executable: Some(PathBuf::from("/usr/local/python/current/bin/python")),
76+
executable: Some(PathBuf::from("/usr/local/python/3.10.13/bin/python")),
9177
prefix: Some(PathBuf::from("/usr/local/python/3.10.13")),
9278
version: Some("3.10.13.final.0".to_string()),
9379
arch: Some(Architecture::X64),
9480
symlinks: Some(vec![
95-
PathBuf::from("/usr/local/python/current/bin/python"),
96-
PathBuf::from("/usr/local/python/current/bin/python3"),
97-
PathBuf::from("/usr/local/python/current/bin/python3.10"),
81+
PathBuf::from("/usr/local/python/3.10.13/bin/python"),
82+
PathBuf::from("/usr/local/python/3.10.13/bin/python3"),
83+
PathBuf::from("/usr/local/python/3.10.13/bin/python3.10"),
9884
]),
9985
manager: None,
10086
..Default::default()
@@ -112,29 +98,8 @@ fn verify_python_in_jupyter_contaner() {
11298
manager: None,
11399
..Default::default()
114100
};
115-
let bin_python = PythonEnvironment {
116-
kind: Some(PythonEnvironmentKind::LinuxGlobal),
117-
executable: Some(PathBuf::from("/bin/python3")),
118-
prefix: Some(PathBuf::from("/usr")),
119-
version: Some("3.8.10.final.0".to_string()),
120-
arch: Some(Architecture::X64),
121-
symlinks: Some(vec![
122-
PathBuf::from("/bin/python3"),
123-
PathBuf::from("/bin/python3.8"),
124-
]),
125-
manager: None,
126-
..Default::default()
127-
};
128101

129-
for env in [
130-
conda,
131-
codespace_python,
132-
current_python,
133-
usr_bin_python,
134-
bin_python,
135-
]
136-
.iter()
137-
{
102+
for env in [conda, current_python, usr_bin_python].iter() {
138103
let python_env = environments
139104
.iter()
140105
.find(|e| e.executable == env.executable)

0 commit comments

Comments
 (0)