Skip to content

Commit b09cfcb

Browse files
committed
Rework the shim to use env state
1 parent 8fd20eb commit b09cfcb

File tree

1 file changed

+33
-21
lines changed

1 file changed

+33
-21
lines changed

py/tools/venv_shim/src/main.rs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use miette::{miette, Context, IntoDiagnostic};
22
use std::env;
3+
use std::env::VarError;
34
use std::fs;
45
use std::io::{self, BufRead};
56
use std::os::unix::process::CommandExt;
@@ -45,7 +46,7 @@ fn parse_version_info(version_str: &str) -> Option<String> {
4546

4647
fn compare_versions(version_from_cfg: &str, executable_path: &Path) -> bool {
4748
if let Some(file_name) = executable_path.file_name().and_then(|n| n.to_str()) {
48-
return file_name.ends_with(&format!("python{}", version_from_cfg));
49+
file_name.ends_with(&format!("python{}", version_from_cfg))
4950
} else {
5051
false
5152
}
@@ -64,26 +65,41 @@ fn find_python_executables(version_from_cfg: &str, exclude_dir: &Path) -> Option
6465
None
6566
}
6667
})
67-
.filter_map(|path| path.canonicalize().ok())
6868
.filter(|potential_executable| potential_executable.parent() != Some(exclude_dir))
69-
.filter(|potential_executable| compare_versions(version_from_cfg, &potential_executable))
69+
.filter(|potential_executable| compare_versions(version_from_cfg, potential_executable))
7070
.collect();
7171

72-
if binaries.len() > 0 {
72+
if !binaries.is_empty() {
7373
Some(binaries)
7474
} else {
7575
None
7676
}
7777
}
7878

7979
fn main() -> miette::Result<()> {
80-
let current_exe = env::current_exe().unwrap();
80+
let venv_home_path = PathBuf::from(
81+
env::var("VIRTUAL_ENV")
82+
.into_diagnostic()
83+
.wrap_err("[aspect] $VIRTUAL_ENV was unbound! A venv must be activated.")
84+
.unwrap(),
85+
);
86+
87+
let venv_interpreter_path: PathBuf = env::var("PYTHONEXECUTABLE")
88+
.map(PathBuf::from)
89+
.map_err(|_| Ok::<PathBuf, VarError>(venv_home_path.join("bin/python")))
90+
.unwrap();
91+
92+
let excluded_interpreters_dir = &venv_home_path.join("bin");
93+
8194
let args: Vec<_> = env::args().collect();
8295

8396
#[cfg(feature = "debug")]
84-
eprintln!("[aspect] Current executable path: {:?}", current_exe);
97+
eprintln!(
98+
"[aspect] Current executable path: {:?}",
99+
&venv_interpreter_path
100+
);
85101

86-
let Some(pyvenv_cfg_path) = find_pyvenv_cfg(&current_exe) else {
102+
let Some(pyvenv_cfg_path) = find_pyvenv_cfg(&venv_interpreter_path) else {
87103
return Err(miette!("pyvenv.cfg not found one directory level up."));
88104
};
89105
#[cfg(feature = "debug")]
@@ -102,7 +118,7 @@ fn main() -> miette::Result<()> {
102118
};
103119

104120
#[cfg(feature = "debug")]
105-
eprintln!("[aspect] version_info from pyvenv.cfg: {}", &version_info);
121+
eprintln!("[aspect] version_info from pyvenv.cfg: {:?}", &version_info);
106122

107123
let Some(target_python_version) = parse_version_info(&version_info) else {
108124
return Err(miette!("Could not parse version_info as x.y."));
@@ -114,12 +130,11 @@ fn main() -> miette::Result<()> {
114130
&target_python_version
115131
);
116132

117-
let exclude_dir = current_exe.parent().unwrap().canonicalize().unwrap();
118-
119133
#[cfg(feature = "debug")]
120-
eprintln!("[aspect] Ignoring dir {:?}", &exclude_dir);
134+
eprintln!("[aspect] Ignoring dir {:?}", &excluded_interpreters_dir);
121135

122-
let Some(python_executables) = find_python_executables(&target_python_version, &exclude_dir)
136+
let Some(python_executables) =
137+
find_python_executables(&target_python_version, excluded_interpreters_dir)
123138
else {
124139
return Err(miette!(
125140
"No suitable Python interpreter found in PATH matching version '{}'.",
@@ -148,30 +163,27 @@ fn main() -> miette::Result<()> {
148163
}
149164
};
150165

151-
let Some(interpreter_path) = python_executables.get(index) else {
166+
let Some(actual_interpreter_path) = python_executables.get(index) else {
152167
return Err(miette!(
153168
"Unable to find another interpreter at index {}",
154169
index
155170
));
156171
};
157172

158-
let exe_path = current_exe.to_string_lossy().into_owned();
159173
let exec_args = &args[1..];
160174

161175
#[cfg(feature = "debug")]
162176
eprintln!(
163177
"[aspect] Attempting to execute: {:?} with argv[0] as {:?} and args as {:?}",
164-
interpreter_path, exe_path, exec_args,
178+
&actual_interpreter_path, &venv_interpreter_path, exec_args,
165179
);
166180

167-
let mut cmd = Command::new(&interpreter_path);
181+
let mut cmd = Command::new(actual_interpreter_path);
168182
cmd.args(exec_args);
169183

170184
// Lie about the value of argv0 to hoodwink the interpreter as to its
171185
// location on Linux-based platforms.
172-
if cfg!(target_os = "linux") {
173-
cmd.arg0(&exe_path);
174-
}
186+
cmd.arg0(&venv_interpreter_path);
175187

176188
// On MacOS however, there are facilities for asking the C runtime/OS
177189
// what the real name of the interpreter executable is, and that value
@@ -181,13 +193,13 @@ fn main() -> miette::Result<()> {
181193
// https://github.com/python/cpython/blob/68e72cf3a80362d0a2d57ff0c9f02553c378e537/Modules/getpath.c#L778
182194
// https://docs.python.org/3/using/cmdline.html#envvar-PYTHONEXECUTABLE
183195
if cfg!(target_os = "macos") {
184-
cmd.env("PYTHONEXECUTABLE", &exe_path);
196+
cmd.env("PYTHONEXECUTABLE", &venv_interpreter_path);
185197
}
186198

187199
// Re-export the counter so it'll go up
188200
cmd.env(COUNTER_VAR, index.to_string());
189201

190202
let _ = cmd.exec();
191203

192-
return Ok(());
204+
Ok(())
193205
}

0 commit comments

Comments
 (0)