diff --git a/docs/changelog/2504.bugfix.rst b/docs/changelog/2504.bugfix.rst new file mode 100644 index 000000000..31fdc8265 --- /dev/null +++ b/docs/changelog/2504.bugfix.rst @@ -0,0 +1 @@ +Discover CPython implementations distributed on Windows by any organization - by :user:`faph`. diff --git a/src/virtualenv/discovery/windows/__init__.py b/src/virtualenv/discovery/windows/__init__.py index 71d82ce89..068a18771 100644 --- a/src/virtualenv/discovery/windows/__init__.py +++ b/src/virtualenv/discovery/windows/__init__.py @@ -2,6 +2,12 @@ from ..py_spec import PythonSpec from .pep514 import discover_pythons +# Map of well-known organizations (as per PEP 514 Company Windows Registry key part) versus Python implementation +_IMPLEMENTATION_BY_ORG = { + "ContinuumAnalytics": "CPython", + "PythonCore": "CPython", +} + class Pep514PythonInfo(PythonInfo): """A Python information acquired from PEP-514""" @@ -19,13 +25,17 @@ def propose_interpreters(spec, cache_dir, env): ) for name, major, minor, arch, exe, _ in existing: - # pre-filter - if name in ("PythonCore", "ContinuumAnalytics"): - name = "CPython" - registry_spec = PythonSpec(None, name, major, minor, None, arch, exe) - if registry_spec.satisfies(spec): + # Map well-known/most common organizations to a Python implementation, use the org name as a fallback for + # backwards compatibility. + implementation = _IMPLEMENTATION_BY_ORG.get(name, name) + + # Pre-filtering based on Windows Registry metadata, for CPython only + skip_pre_filter = implementation.lower() != "cpython" + registry_spec = PythonSpec(None, implementation, major, minor, None, arch, exe) + if skip_pre_filter or registry_spec.satisfies(spec): interpreter = Pep514PythonInfo.from_exe(exe, cache_dir, env=env, raise_on_error=False) if interpreter is not None: + # Final filtering/matching using interpreter metadata if interpreter.satisfies(spec, impl_must_match=True): yield interpreter diff --git a/tests/unit/discovery/windows/conftest.py b/tests/unit/discovery/windows/conftest.py new file mode 100644 index 000000000..58da626d2 --- /dev/null +++ b/tests/unit/discovery/windows/conftest.py @@ -0,0 +1,100 @@ +from contextlib import contextmanager +from pathlib import Path + +import pytest + + +@pytest.fixture() +def _mock_registry(mocker): + from virtualenv.discovery.windows.pep514 import winreg + + loc, glob = {}, {} + mock_value_str = (Path(__file__).parent / "winreg-mock-values.py").read_text() + exec(mock_value_str, glob, loc) + enum_collect = loc["enum_collect"] + value_collect = loc["value_collect"] + key_open = loc["key_open"] + hive_open = loc["hive_open"] + + def _enum_key(key, at): + key_id = key.value if isinstance(key, Key) else key + result = enum_collect[key_id][at] + if isinstance(result, OSError): + raise result + return result + + mocker.patch.object(winreg, "EnumKey", side_effect=_enum_key) + + def _query_value_ex(key, value_name): + key_id = key.value if isinstance(key, Key) else key + result = value_collect[key_id][value_name] + if isinstance(result, OSError): + raise result + return result + + mocker.patch.object(winreg, "QueryValueEx", side_effect=_query_value_ex) + + class Key: + def __init__(self, value): + self.value = value + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): # noqa: U100 + return None + + @contextmanager + def _open_key_ex(*args): + if len(args) == 2: + key, value = args + key_id = key.value if isinstance(key, Key) else key + result = Key(key_open[key_id][value]) # this needs to be something that can be with-ed, so let's wrap it + elif len(args) == 4: + result = hive_open[args] + else: + raise RuntimeError + value = result.value if isinstance(result, Key) else result + if isinstance(value, OSError): + raise value + yield result + + mocker.patch.object(winreg, "OpenKeyEx", side_effect=_open_key_ex) + mocker.patch("os.path.exists", return_value=True) + + +def _mock_pyinfo(major, minor, arch, exe): + """Return PythonInfo objects with essential metadata set for the given args""" + from virtualenv.discovery.py_info import PythonInfo, VersionInfo + + info = PythonInfo() + info.base_prefix = str(Path(exe).parent) + info.executable = info.original_executable = info.system_executable = exe + info.implementation = "CPython" + info.architecture = arch + info.version_info = VersionInfo(major, minor, 0, "final", 0) + return info + + +@pytest.fixture() +def _populate_pyinfo_cache(monkeypatch): + """Add metadata to virtualenv.discovery.cached_py_info._CACHE for all (mocked) registry entries""" + import virtualenv.discovery.cached_py_info + + # Data matches _mock_registry fixture + interpreters = [ + ("ContinuumAnalytics", 3, 10, 32, "C:\\Users\\user\\Miniconda3\\python.exe", None), + ("ContinuumAnalytics", 3, 10, 64, "C:\\Users\\user\\Miniconda3-64\\python.exe", None), + ("PythonCore", 3, 9, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", None), + ("PythonCore", 3, 9, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", None), + ("PythonCore", 3, 5, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python35\\python.exe", None), + ("PythonCore", 3, 9, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", None), + ("PythonCore", 3, 7, 32, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python37-32\\python.exe", None), + ("PythonCore", 3, 12, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\python.exe", None), + ("PythonCore", 2, 7, 64, "C:\\Python27\\python.exe", None), + ("PythonCore", 3, 4, 64, "C:\\Python34\\python.exe", None), + ("CompanyA", 3, 6, 64, "Z:\\CompanyA\\Python\\3.6\\python.exe", None), + ] + for _, major, minor, arch, exe, _ in interpreters: + info = _mock_pyinfo(major, minor, arch, exe) + monkeypatch.setitem(virtualenv.discovery.cached_py_info._CACHE, Path(info.executable), info) diff --git a/tests/unit/discovery/windows/test_windows.py b/tests/unit/discovery/windows/test_windows.py new file mode 100644 index 000000000..65ff9cab2 --- /dev/null +++ b/tests/unit/discovery/windows/test_windows.py @@ -0,0 +1,33 @@ +import sys + +import pytest + +from virtualenv.discovery.py_spec import PythonSpec + + +@pytest.mark.skipif(sys.platform != "win32", reason="no Windows registry") +@pytest.mark.usefixtures("_mock_registry") +@pytest.mark.usefixtures("_populate_pyinfo_cache") +@pytest.mark.parametrize( + ("string_spec", "expected_exe"), + [ + # 64-bit over 32-bit + ("python3.10", "C:\\Users\\user\\Miniconda3-64\\python.exe"), + ("cpython3.10", "C:\\Users\\user\\Miniconda3-64\\python.exe"), + # 1 installation of 3.9 available + ("python3.12", "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"), + ("cpython3.12", "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"), + # resolves to highest available version + ("python", "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"), + ("cpython", "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"), + # Non-standard org name + ("python3.6", "Z:\\CompanyA\\Python\\3.6\\python.exe"), + ("cpython3.6", "Z:\\CompanyA\\Python\\3.6\\python.exe"), + ], +) +def test_propose_interpreters(string_spec, expected_exe): + from virtualenv.discovery.windows import propose_interpreters + + spec = PythonSpec.from_string_spec(string_spec) + interpreter = next(propose_interpreters(spec=spec, cache_dir=None, env=None)) + assert interpreter.executable == expected_exe diff --git a/tests/unit/discovery/windows/test_windows_pep514.py b/tests/unit/discovery/windows/test_windows_pep514.py index 3d1ca0051..c02db382e 100644 --- a/tests/unit/discovery/windows/test_windows_pep514.py +++ b/tests/unit/discovery/windows/test_windows_pep514.py @@ -1,8 +1,5 @@ import sys import textwrap -from collections import defaultdict -from contextlib import contextmanager -from pathlib import Path import pytest @@ -14,16 +11,17 @@ def test_pep514(): interpreters = list(discover_pythons()) assert interpreters == [ - ("ContinuumAnalytics", 3, 7, 32, "C:\\Users\\user\\Miniconda3\\python.exe", None), - ("ContinuumAnalytics", 3, 7, 64, "C:\\Users\\user\\Miniconda3-64\\python.exe", None), - ("PythonCore", 3, 6, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", None), - ("PythonCore", 3, 6, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", None), - ("PythonCore", 3, 5, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python35\\python.exe", None), - ("PythonCore", 3, 6, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", None), - ("PythonCore", 3, 7, 32, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python37-32\\python.exe", None), - ("PythonCore", 3, 9, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", None), + ("ContinuumAnalytics", 3, 10, 32, "C:\\Users\\user\\Miniconda3\\python.exe", None), + ("ContinuumAnalytics", 3, 10, 64, "C:\\Users\\user\\Miniconda3-64\\python.exe", None), + ("PythonCore", 3, 9, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe", None), + ("PythonCore", 3, 9, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe", None), + ("PythonCore", 3, 8, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python38\\python.exe", None), + ("PythonCore", 3, 9, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe", None), + ("PythonCore", 3, 10, 32, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python310-32\\python.exe", None), + ("PythonCore", 3, 12, 64, "C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\python.exe", None), + ("CompanyA", 3, 6, 64, "Z:\\CompanyA\\Python\\3.6\\python.exe", None), ("PythonCore", 2, 7, 64, "C:\\Python27\\python.exe", None), - ("PythonCore", 3, 4, 64, "C:\\Python34\\python.exe", None), + ("PythonCore", 3, 7, 64, "C:\\Python37\\python.exe", None), ] @@ -36,16 +34,17 @@ def test_pep514_run(capsys, caplog): out, err = capsys.readouterr() expected = textwrap.dedent( r""" - ('ContinuumAnalytics', 3, 7, 32, 'C:\\Users\\user\\Miniconda3\\python.exe', None) - ('ContinuumAnalytics', 3, 7, 64, 'C:\\Users\\user\\Miniconda3-64\\python.exe', None) + ('CompanyA', 3, 6, 64, 'Z:\\CompanyA\\Python\\3.6\\python.exe', None) + ('ContinuumAnalytics', 3, 10, 32, 'C:\\Users\\user\\Miniconda3\\python.exe', None) + ('ContinuumAnalytics', 3, 10, 64, 'C:\\Users\\user\\Miniconda3-64\\python.exe', None) ('PythonCore', 2, 7, 64, 'C:\\Python27\\python.exe', None) - ('PythonCore', 3, 4, 64, 'C:\\Python34\\python.exe', None) - ('PythonCore', 3, 5, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python35\\python.exe', None) - ('PythonCore', 3, 6, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe', None) - ('PythonCore', 3, 6, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe', None) - ('PythonCore', 3, 6, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe', None) - ('PythonCore', 3, 7, 32, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python37-32\\python.exe', None) - ('PythonCore', 3, 9, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe', None) + ('PythonCore', 3, 10, 32, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python310-32\\python.exe', None) + ('PythonCore', 3, 12, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\python.exe', None) + ('PythonCore', 3, 7, 64, 'C:\\Python37\\python.exe', None) + ('PythonCore', 3, 8, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python38\\python.exe', None) + ('PythonCore', 3, 9, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe', None) + ('PythonCore', 3, 9, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe', None) + ('PythonCore', 3, 9, 64, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe', None) """, ).strip() assert out.strip() == expected @@ -56,136 +55,9 @@ def test_pep514_run(capsys, caplog): f"{prefix}HKEY_CURRENT_USER/PythonCore/3.2/SysArchitecture error: arch is not string: 100", f"{prefix}HKEY_CURRENT_USER/PythonCore/3.3 error: no ExecutablePath or default for it", f"{prefix}HKEY_CURRENT_USER/PythonCore/3.3 error: could not load exe with value None", - f"{prefix}HKEY_CURRENT_USER/PythonCore/3.8/InstallPath error: missing", - f"{prefix}HKEY_CURRENT_USER/PythonCore/3.9/SysVersion error: invalid format magic", + f"{prefix}HKEY_CURRENT_USER/PythonCore/3.11/InstallPath error: missing", + f"{prefix}HKEY_CURRENT_USER/PythonCore/3.12/SysVersion error: invalid format magic", f"{prefix}HKEY_CURRENT_USER/PythonCore/3.X/SysVersion error: version is not string: 2778", f"{prefix}HKEY_CURRENT_USER/PythonCore/3.X error: invalid format 3.X", ] assert caplog.messages == expected_logs - - -@pytest.fixture() -def _mock_registry(mocker): - from virtualenv.discovery.windows.pep514 import winreg - - loc, glob = {}, {} - mock_value_str = (Path(__file__).parent / "winreg-mock-values.py").read_text() - exec(mock_value_str, glob, loc) - enum_collect = loc["enum_collect"] - value_collect = loc["value_collect"] - key_open = loc["key_open"] - hive_open = loc["hive_open"] - - def _e(key, at): - key_id = key.value if isinstance(key, Key) else key - result = enum_collect[key_id][at] - if isinstance(result, OSError): - raise result - return result - - mocker.patch.object(winreg, "EnumKey", side_effect=_e) - - def _v(key, value_name): - key_id = key.value if isinstance(key, Key) else key - result = value_collect[key_id][value_name] - if isinstance(result, OSError): - raise result - return result - - mocker.patch.object(winreg, "QueryValueEx", side_effect=_v) - - class Key: - def __init__(self, value): - self.value = value - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): # noqa: U100 - return None - - @contextmanager - def _o(*args): - if len(args) == 2: - key, value = args - key_id = key.value if isinstance(key, Key) else key - result = Key(key_open[key_id][value]) # this needs to be something that can be with-ed, so let's wrap it - elif len(args) == 4: - result = hive_open[args] - else: - raise RuntimeError - value = result.value if isinstance(result, Key) else result - if isinstance(value, OSError): - raise value - yield result - - mocker.patch.object(winreg, "OpenKeyEx", side_effect=_o) - mocker.patch("os.path.exists", return_value=True) - - -@pytest.fixture() -def _collect_winreg_access(mocker): - # noinspection PyUnresolvedReferences - from winreg import EnumKey, OpenKeyEx, QueryValueEx - - from virtualenv.discovery.windows.pep514 import winreg - - hive_open = {} - key_open = defaultdict(dict) - - @contextmanager - def _c(*args): - res = None - key_id = id(args[0]) if len(args) == 2 else None - try: - with OpenKeyEx(*args) as c: - res = id(c) - yield c - except Exception as exception: - res = exception - raise exception - finally: - if len(args) == 4: - hive_open[args] = res - elif len(args) == 2: - key_open[key_id][args[1]] = res - - enum_collect = defaultdict(list) - - def _e(key, at): - result = None - key_id = id(key) - try: - result = EnumKey(key, at) - return result - except Exception as exception: - result = exception - raise result - finally: - enum_collect[key_id].append(result) - - value_collect = defaultdict(dict) - - def _v(key, value_name): - result = None - key_id = id(key) - try: - result = QueryValueEx(key, value_name) - return result - except Exception as exception: - result = exception - raise result - finally: - value_collect[key_id][value_name] = result - - mocker.patch.object(winreg, "EnumKey", side_effect=_e) - mocker.patch.object(winreg, "QueryValueEx", side_effect=_v) - mocker.patch.object(winreg, "OpenKeyEx", side_effect=_c) - - yield - - print("") - print(f"hive_open = {hive_open}") - print(f"key_open = {dict(key_open.items())}") - print(f"value_collect = {dict(value_collect.items())}") - print(f"enum_collect = {dict(enum_collect.items())}") diff --git a/tests/unit/discovery/windows/winreg-mock-values.py b/tests/unit/discovery/windows/winreg-mock-values.py index 2ac362fa8..11ec0b430 100644 --- a/tests/unit/discovery/windows/winreg-mock-values.py +++ b/tests/unit/discovery/windows/winreg-mock-values.py @@ -10,12 +10,12 @@ } key_open = { 78701152: { - "Anaconda37-32\\InstallPath": 78703200, - "Anaconda37-32": 78703568, - "Anaconda37-64\\InstallPath": 78703520, - "Anaconda37-64": 78702368, + "Anaconda310-32\\InstallPath": 78703200, + "Anaconda310-32": 78703568, + "Anaconda310-64\\InstallPath": 78703520, + "Anaconda310-64": 78702368, }, - 78701856: {"ContinuumAnalytics": 78701152, "PythonCore": 78702656}, + 78701856: {"ContinuumAnalytics": 78701152, "PythonCore": 78702656, "CompanyA": 88800000}, 78702656: { "3.1\\InstallPath": 78701824, "3.1": 78700704, @@ -23,43 +23,47 @@ "3.2": 78704368, "3.3\\InstallPath": 78701936, "3.3": 78703024, - "3.5\\InstallPath": 78703792, - "3.5": 78701792, - "3.6\\InstallPath": 78701888, - "3.6": 78703424, - "3.7-32\\InstallPath": 78703600, - "3.7-32": 78704512, - "3.8\\InstallPath": OSError(2, "The system cannot find the file specified"), - "3.8": 78700656, - "3.9\\InstallPath": 78703632, - "3.9": 78702608, + "3.8\\InstallPath": 78703792, + "3.8": 78701792, + "3.9\\InstallPath": 78701888, + "3.9": 78703424, + "3.10-32\\InstallPath": 78703600, + "3.10-32": 78704512, + "3.11\\InstallPath": OSError(2, "The system cannot find the file specified"), + "3.11": 78700656, + "3.12\\InstallPath": 78703632, + "3.12": 78702608, "3.X": 78703088, }, - 78702960: {"2.7\\InstallPath": 78700912, "2.7": 78703136, "3.4\\InstallPath": 78703648, "3.4": 78704032}, + 78702960: {"2.7\\InstallPath": 78700912, "2.7": 78703136, "3.7\\InstallPath": 78703648, "3.7": 78704032}, 78701840: {"PythonCore": 78702960}, + 88800000: { + "3.6\\InstallPath": 88810000, + "3.6": 88820000, + }, } value_collect = { - 78703568: {"SysVersion": ("3.7", 1), "SysArchitecture": ("32bit", 1)}, + 78703568: {"SysVersion": ("3.10", 1), "SysArchitecture": ("32bit", 1)}, 78703200: { "ExecutablePath": ("C:\\Users\\user\\Miniconda3\\python.exe", 1), "ExecutableArguments": OSError(2, "The system cannot find the file specified"), }, - 78702368: {"SysVersion": ("3.7", 1), "SysArchitecture": ("64bit", 1)}, + 78702368: {"SysVersion": ("3.10", 1), "SysArchitecture": ("64bit", 1)}, 78703520: { "ExecutablePath": ("C:\\Users\\user\\Miniconda3-64\\python.exe", 1), "ExecutableArguments": OSError(2, "The system cannot find the file specified"), }, - 78700704: {"SysVersion": ("3.6", 1), "SysArchitecture": ("magic", 1)}, + 78700704: {"SysVersion": ("3.9", 1), "SysArchitecture": ("magic", 1)}, 78701824: { - "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", 1), + "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe", 1), "ExecutableArguments": OSError(2, "The system cannot find the file specified"), }, - 78704368: {"SysVersion": ("3.6", 1), "SysArchitecture": (100, 4)}, + 78704368: {"SysVersion": ("3.9", 1), "SysArchitecture": (100, 4)}, 78704048: { - "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", 1), + "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe", 1), "ExecutableArguments": OSError(2, "The system cannot find the file specified"), }, - 78703024: {"SysVersion": ("3.6", 1), "SysArchitecture": ("64bit", 1)}, + 78703024: {"SysVersion": ("3.9", 1), "SysArchitecture": ("64bit", 1)}, 78701936: { "ExecutablePath": OSError(2, "The system cannot find the file specified"), None: OSError(2, "The system cannot find the file specified"), @@ -69,17 +73,17 @@ "SysArchitecture": OSError(2, "The system cannot find the file specified"), }, 78703792: { - "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python35\\python.exe", 1), + "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python38\\python.exe", 1), "ExecutableArguments": OSError(2, "The system cannot find the file specified"), }, - 78703424: {"SysVersion": ("3.6", 1), "SysArchitecture": ("64bit", 1)}, + 78703424: {"SysVersion": ("3.9", 1), "SysArchitecture": ("64bit", 1)}, 78701888: { - "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", 1), + "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe", 1), "ExecutableArguments": OSError(2, "The system cannot find the file specified"), }, - 78704512: {"SysVersion": ("3.7", 1), "SysArchitecture": ("32bit", 1)}, + 78704512: {"SysVersion": ("3.10", 1), "SysArchitecture": ("32bit", 1)}, 78703600: { - "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python37-32\\python.exe", 1), + "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python310-32\\python.exe", 1), "ExecutableArguments": OSError(2, "The system cannot find the file specified"), }, 78700656: { @@ -88,7 +92,7 @@ }, 78702608: {"SysVersion": ("magic", 1), "SysArchitecture": ("64bit", 1)}, 78703632: { - "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36\\python.exe", 1), + "ExecutablePath": ("C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python312\\python.exe", 1), "ExecutableArguments": OSError(2, "The system cannot find the file specified"), }, 78703088: {"SysVersion": (2778, 11)}, @@ -107,25 +111,36 @@ }, 78703648: { "ExecutablePath": OSError(2, "The system cannot find the file specified"), - None: ("C:\\Python34\\", 1), + None: ("C:\\Python37\\", 1), + "ExecutableArguments": OSError(2, "The system cannot find the file specified"), + }, + 88810000: { + "ExecutablePath": ("Z:\\CompanyA\\Python\\3.6\\python.exe", 1), "ExecutableArguments": OSError(2, "The system cannot find the file specified"), }, + 88820000: {"SysVersion": ("3.6", 1), "SysArchitecture": ("64bit", 1)}, } enum_collect = { - 78701856: ["ContinuumAnalytics", "PythonCore", OSError(22, "No more data is available", None, 259, None)], - 78701152: ["Anaconda37-32", "Anaconda37-64", OSError(22, "No more data is available", None, 259, None)], + 78701856: [ + "ContinuumAnalytics", + "PythonCore", + "CompanyA", + OSError(22, "No more data is available", None, 259, None), + ], + 78701152: ["Anaconda310-32", "Anaconda310-64", OSError(22, "No more data is available", None, 259, None)], 78702656: [ "3.1", "3.2", "3.3", - "3.5", - "3.6", - "3.7-32", "3.8", "3.9", + "3.10-32", + "3.11", + "3.12", "3.X", OSError(22, "No more data is available", None, 259, None), ], 78701840: ["PyLauncher", "PythonCore", OSError(22, "No more data is available", None, 259, None)], - 78702960: ["2.7", "3.4", OSError(22, "No more data is available", None, 259, None)], + 78702960: ["2.7", "3.7", OSError(22, "No more data is available", None, 259, None)], + 88800000: ["3.6", OSError(22, "No more data is available", None, 259, None)], }