diff -r b2297669c065 -r 1f9bafeff96c src/eric7/SystemUtilities/PythonUtilities.py --- a/src/eric7/SystemUtilities/PythonUtilities.py Wed Nov 29 11:33:32 2023 +0100 +++ b/src/eric7/SystemUtilities/PythonUtilities.py Wed Nov 29 14:27:25 2023 +0100 @@ -7,10 +7,14 @@ Module implementing Python related utility functions. """ +import contextlib import os +import platform import sys import sysconfig +from .OSUtilities import isWindowsPlatform + def getPythonExecutable(): """ @@ -135,3 +139,135 @@ pyVer = 3 return pyVer + + +def searchInterpreters(environments=None): + """ + Function to determine a list of all Python interpreters available via the + executable search path (i.e. PATH) (Windows variant). + + @param environments list of environment directories to scan for Python interpreters + (defaults to None) + @type list of str (optional) + @return list of found interpreter executables + @rtype list of str + """ + if isWindowsPlatform(): + return __searchInterpreters_Windows(environments=environments) + else: + return __searchInterpreters_Linux(environments=environments) + + +def __searchInterpreters_Windows(environments=None): + """ + Function to determine a list of all Python interpreters available via the + executable search path (i.e. PATH) (Windows variant). + + @param environments list of environment directories to scan for Python interpreters + (defaults to None) + @type list of str (optional) + @return list of found interpreter executables + @rtype list of str + """ + try: + import winreg + except ImportError: + import _winreg as winreg # __IGNORE_WARNING__ + + def getExePath(branch, access, versionStr): + with contextlib.suppress(WindowsError, OSError): + software = winreg.OpenKey(branch, "Software", 0, access) + python = winreg.OpenKey(software, "Python", 0, access) + pcore = winreg.OpenKey(python, "PythonCore", 0, access) + version = winreg.OpenKey(pcore, versionStr, 0, access) + installpath = winreg.QueryValue(version, "InstallPath") + exe = os.path.join(installpath, "python.exe") + if os.access(exe, os.X_OK): + return exe + + return None + + minorVersions = range(8, 16) # Py 3.8 until Py 3.15 + interpreters = set() + + if environments: + for directory in [os.path.join(d, "Scripts") for d in environments]: + exe = os.path.join(directory, "python.exe") + if os.access(exe, os.X_OK): + interpreters.add(exe) + + else: + versionSuffixes = ["", "-32", "-64"] + for minorVersion in minorVersions: + for versionSuffix in versionSuffixes: + versionStr = "{0}.{1}{2}".format( + "3", minorVersion, versionSuffix + ) + exePath = getExePath( + winreg.HKEY_CURRENT_USER, + winreg.KEY_WOW64_32KEY | winreg.KEY_READ, + versionStr, + ) + if exePath: + interpreters.add(exePath) + + exePath = getExePath( + winreg.HKEY_LOCAL_MACHINE, + winreg.KEY_WOW64_32KEY | winreg.KEY_READ, + versionStr, + ) + if exePath: + interpreters.add(exePath) + + # Even on Intel 64-bit machines it's 'AMD64' + if platform.machine() == "AMD64": + exePath = getExePath( + winreg.HKEY_CURRENT_USER, + winreg.KEY_WOW64_64KEY | winreg.KEY_READ, + versionStr, + ) + if exePath: + interpreters.add(exePath) + + exePath = getExePath( + winreg.HKEY_LOCAL_MACHINE, + winreg.KEY_WOW64_64KEY | winreg.KEY_READ, + versionStr, + ) + if exePath: + interpreters.add(exePath) + + return list(interpreters) + + +def __searchInterpreters_Linux(environments=None): + """ + Function to determine a list of all Python interpreters available via the + executable search path (i.e. PATH) (non Windows variant). + + @param environments list of environment directories to scan for Python interpreters + (defaults to None) + @type list of str (optional) + @return list of found interpreter executables + @rtype list of str + """ + from eric7.SystemUtilities import OSUtilities + + minorVersions = range(8, 16) # Py 3.8 until Py 3.15 + interpreters = [] + + if environments: + directories = [os.path.join(d, "bin") for d in environments] + else: + searchpath = OSUtilities.getEnvironmentEntry("PATH") + directories = searchpath.split(os.pathsep) if searchpath else [] + + if directories: + pythonNames = ["python3.{0}".format(v) for v in minorVersions] + for directory in directories: + for interpreter in pythonNames: + exe = os.path.join(directory, interpreter) + if os.access(exe, os.X_OK): + interpreters.append(exe) + + return interpreters