11use miette:: { miette, Context , IntoDiagnostic } ;
22use std:: env;
3+ use std:: env:: VarError ;
34use std:: fs;
45use std:: io:: { self , BufRead } ;
56use std:: os:: unix:: process:: CommandExt ;
@@ -45,7 +46,7 @@ fn parse_version_info(version_str: &str) -> Option<String> {
4546
4647fn 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
7979fn 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