diff --git a/docs/changelog/215.bugfix.rst b/docs/changelog/215.bugfix.rst new file mode 100644 index 0000000..d2f6ca7 --- /dev/null +++ b/docs/changelog/215.bugfix.rst @@ -0,0 +1 @@ +Prevent an earlier virtual-environment lookup from reporting an executable under the wrong Python version. diff --git a/src/python_discovery/_py_info.py b/src/python_discovery/_py_info.py index 64acb8f..09733de 100644 --- a/src/python_discovery/_py_info.py +++ b/src/python_discovery/_py_info.py @@ -484,7 +484,7 @@ def resolve_to_system(cls, cache: PyInfoCache | None, target: PythonInfo) -> Pyt target.executable = start_executable return target - _cache_exe_discovery: ClassVar[dict[tuple[str, bool], PythonInfo]] = {} + _cache_exe_discovery: ClassVar[dict[tuple[str, bool, VersionInfo], PythonInfo]] = {} def discover_exe( self, @@ -502,7 +502,7 @@ def discover_exe( :param exact: when ``True``, require an exact version match. :param env: environment mapping; defaults to :data:`os.environ`. """ - key = prefix, exact + key = prefix, exact, self.version_info if key in self._cache_exe_discovery and prefix: _LOGGER.debug("discover exe from cache %s - exact %s: %r", prefix, exact, self._cache_exe_discovery[key]) return self._cache_exe_discovery[key] diff --git a/tests/test_py_info_extra.py b/tests/test_py_info_extra.py index 84d75af..016636a 100644 --- a/tests/test_py_info_extra.py +++ b/tests/test_py_info_extra.py @@ -171,12 +171,28 @@ def test_resolve_to_system_single_prefix_self_link(mocker: MockerFixture, caplog def test_discover_exe_cache_hit() -> None: info = copy.deepcopy(CURRENT) cached = copy.deepcopy(CURRENT) - PythonInfo._cache_exe_discovery["/some/prefix", True] = cached + PythonInfo._cache_exe_discovery["/some/prefix", True, info.version_info] = cached try: result = info.discover_exe(MagicMock(), prefix="/some/prefix", exact=True) assert result is cached finally: - del PythonInfo._cache_exe_discovery["/some/prefix", True] + del PythonInfo._cache_exe_discovery["/some/prefix", True, info.version_info] + + +def test_discover_exe_cache_keeps_requested_version(tmp_path: Path) -> None: + prefix = CURRENT.prefix + assert prefix is not None + cache = DiskCache(tmp_path) + PythonInfo.clear_cache(cache) + try: + found = CURRENT.discover_exe(cache, prefix) + assert found.version_info == CURRENT.version_info + other = copy.deepcopy(CURRENT) + other.version_info = other.version_info._replace(major=0) + with pytest.raises(RuntimeError): + other.discover_exe(cache, prefix) + finally: + PythonInfo.clear_cache(cache) def test_check_exe_none_path(tmp_path: Path) -> None: