@@ -192,34 +192,57 @@ def _get_tcl_tk_libs():
192192
193193 def _fast_get_system_executable (self ):
194194 """Try to get the system executable by just looking at properties."""
195- if self .real_prefix or ( # noqa: PLR1702
196- self .base_prefix is not None and self .base_prefix != self .prefix
197- ): # if this is a virtual environment
198- if self .real_prefix is None :
199- base_executable = getattr (sys , "_base_executable" , None ) # some platforms may set this to help us
200- if base_executable is not None : # noqa: SIM102 # use the saved system executable if present
201- if sys .executable != base_executable : # we know we're in a virtual environment, cannot be us
202- if os .path .exists (base_executable ):
203- return base_executable
204- # Python may return "python" because it was invoked from the POSIX virtual environment
205- # however some installs/distributions do not provide a version-less "python" binary in
206- # the system install location (see PEP 394) so try to fallback to a versioned binary.
207- #
208- # Gate this to Python 3.11 as `sys._base_executable` path resolution is now relative to
209- # the 'home' key from pyvenv.cfg which often points to the system install location.
210- major , minor = self .version_info .major , self .version_info .minor
211- if self .os == "posix" and (major , minor ) >= (3 , 11 ):
212- # search relative to the directory of sys._base_executable
213- base_dir = os .path .dirname (base_executable )
214- for base_executable in [
215- os .path .join (base_dir , exe ) for exe in (f"python{ major } " , f"python{ major } .{ minor } " )
216- ]:
217- if os .path .exists (base_executable ):
218- return base_executable
219- return None # in this case we just can't tell easily without poking around FS and calling them, bail
220195 # if we're not in a virtual environment, this is already a system python, so return the original executable
221196 # note we must choose the original and not the pure executable as shim scripts might throw us off
222- return self .original_executable
197+ if not (self .real_prefix or (self .base_prefix is not None and self .base_prefix != self .prefix )):
198+ return self .original_executable
199+
200+ # if this is NOT a virtual environment, can't determine easily, bail out
201+ if self .real_prefix is not None :
202+ return None
203+
204+ base_executable = getattr (sys , "_base_executable" , None ) # some platforms may set this to help us
205+ if base_executable is None : # use the saved system executable if present
206+ return None
207+
208+ # we know we're in a virtual environment, can not be us
209+ if sys .executable == base_executable :
210+ return None
211+
212+ # We're not in a venv and base_executable exists; use it directly
213+ if os .path .exists (base_executable ):
214+ return base_executable
215+
216+ # Try fallback for POSIX virtual environments
217+ return self ._try_posix_fallback_executable (base_executable )
218+
219+ def _try_posix_fallback_executable (self , base_executable ):
220+ """
221+ Try to find a versioned Python binary as fallback for POSIX virtual environments.
222+
223+ Python may return "python" because it was invoked from the POSIX virtual environment
224+ however some installs/distributions do not provide a version-less "python" binary in
225+ the system install location (see PEP 394) so try to fallback to a versioned binary.
226+
227+ Gate this to Python 3.11 as `sys._base_executable` path resolution is now relative to
228+ the 'home' key from pyvenv.cfg which often points to the system install location.
229+ """
230+ major , minor = self .version_info .major , self .version_info .minor
231+ if self .os != "posix" or (major , minor ) < (3 , 11 ):
232+ return None
233+
234+ # search relative to the directory of sys._base_executable
235+ base_dir = os .path .dirname (base_executable )
236+ candidates = [f"python{ major } " , f"python{ major } .{ minor } " ]
237+ if self .implementation == "PyPy" :
238+ candidates .extend (["pypy" , "pypy3" , f"pypy{ major } " , f"pypy{ major } .{ minor } " ])
239+
240+ for candidate in candidates :
241+ full_path = os .path .join (base_dir , candidate )
242+ if os .path .exists (full_path ):
243+ return full_path
244+
245+ return None # in this case we just can't tell easily without poking around FS and calling them, bail
223246
224247 def install_path (self , key ):
225248 result = self .distutils_install .get (key )
0 commit comments