Skip to content

Commit 2fdb288

Browse files
committed
Enhance pyenv shims directory detection and add unit tests
1 parent 28be1e6 commit 2fdb288

File tree

1 file changed

+66
-2
lines changed

1 file changed

+66
-2
lines changed

crates/pet-python-utils/src/executable.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,11 @@ pub fn find_executable(env_path: &Path) -> Option<PathBuf> {
4545

4646
pub fn find_executables<T: AsRef<Path>>(env_path: T) -> Vec<PathBuf> {
4747
let mut env_path = env_path.as_ref().to_path_buf();
48-
// Never find exes in `.pyenv/shims/` folder, they are not valid exes
49-
if env_path.ends_with(".pyenv/shims") {
48+
// Never find exes in pyenv shims folder, they are not valid exes.
49+
// Pyenv can be installed at custom locations (e.g., ~/.pl/pyenv via PYENV_ROOT),
50+
// not just ~/.pyenv, so we check for any path ending with "shims" that has a
51+
// parent directory containing "pyenv".
52+
if is_pyenv_shims_dir(&env_path) {
5053
return vec![];
5154
}
5255
let mut python_executables = vec![];
@@ -120,6 +123,29 @@ fn is_python_executable_name(exe: &Path) -> bool {
120123
}
121124
}
122125

126+
/// Checks if the given path is a pyenv shims directory.
127+
/// Pyenv shims are not valid Python executables - they are wrapper scripts that
128+
/// redirect to the actual Python installation based on pyenv configuration.
129+
/// Pyenv can be installed at custom locations via PYENV_ROOT (e.g., ~/.pl/pyenv),
130+
/// not just the default ~/.pyenv location.
131+
fn is_pyenv_shims_dir(path: &Path) -> bool {
132+
// Must end with "shims"
133+
if !path.ends_with("shims") {
134+
return false;
135+
}
136+
137+
// Check if parent directory name contains "pyenv" (case-insensitive)
138+
// This handles: ~/.pyenv/shims, ~/.pl/pyenv/shims, /opt/pyenv/shims, etc.
139+
if let Some(parent) = path.parent() {
140+
if let Some(parent_name) = parent.file_name() {
141+
if let Some(name_str) = parent_name.to_str() {
142+
return name_str.to_lowercase().contains("pyenv");
143+
}
144+
}
145+
}
146+
false
147+
}
148+
123149
pub fn should_search_for_environments_in_path<P: AsRef<Path>>(path: &P) -> bool {
124150
// Never search in the .git folder
125151
// Never search in the node_modules folder
@@ -242,4 +268,42 @@ mod tests {
242268
PathBuf::from("pythonw3.exe").as_path()
243269
));
244270
}
271+
272+
#[test]
273+
fn test_is_pyenv_shims_dir() {
274+
// Standard pyenv location
275+
assert!(is_pyenv_shims_dir(
276+
PathBuf::from("/home/user/.pyenv/shims").as_path()
277+
));
278+
279+
// Custom pyenv location (issue #238)
280+
assert!(is_pyenv_shims_dir(
281+
PathBuf::from("/home/user/.pl/pyenv/shims").as_path()
282+
));
283+
284+
// Other custom locations
285+
assert!(is_pyenv_shims_dir(
286+
PathBuf::from("/opt/pyenv/shims").as_path()
287+
));
288+
assert!(is_pyenv_shims_dir(
289+
PathBuf::from("/usr/local/pyenv/shims").as_path()
290+
));
291+
292+
// pyenv-win style (parent contains "pyenv")
293+
assert!(is_pyenv_shims_dir(
294+
PathBuf::from("/home/user/.pyenv/pyenv-win/shims").as_path()
295+
));
296+
297+
// Not pyenv shims (should return false)
298+
assert!(!is_pyenv_shims_dir(
299+
PathBuf::from("/home/user/.pyenv/versions/3.10.0/bin").as_path()
300+
));
301+
assert!(!is_pyenv_shims_dir(PathBuf::from("/usr/bin").as_path()));
302+
assert!(!is_pyenv_shims_dir(
303+
PathBuf::from("/home/user/shims").as_path()
304+
)); // "shims" but parent is not pyenv
305+
assert!(!is_pyenv_shims_dir(
306+
PathBuf::from("/home/user/project/shims").as_path()
307+
));
308+
}
245309
}

0 commit comments

Comments
 (0)