Skip to content

Commit 7322c9c

Browse files
authored
🐛 fix(py-info): detect equivalent base prefixes (#219)
1 parent 690fcbf commit 7322c9c

3 files changed

Lines changed: 20 additions & 2 deletions

File tree

‎docs/changelog/219.bugfix.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Stop repeated interpreter queries when a virtual environment reports the same base prefix with different path spellings.

‎src/python_discovery/_py_info.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import sys
1111
from collections import OrderedDict
1212
from itertools import product
13+
from os.path import normcase, realpath
1314
from string import digits
1415
from typing import TYPE_CHECKING, ClassVar, Final, NamedTuple, TypedDict
1516

@@ -462,7 +463,8 @@ def resolve_to_system(cls, cache: PyInfoCache | None, target: PythonInfo) -> Pyt
462463
prefixes = OrderedDict()
463464
while target.system_executable is None:
464465
prefix = target.real_prefix or target.base_prefix or target.prefix
465-
if prefix in prefixes:
466+
prefix_key = normcase(realpath(prefix))
467+
if prefix_key in prefixes:
466468
if len(prefixes) == 1:
467469
_LOGGER.info("%r links back to itself via prefixes", target)
468470
target.system_executable = target.executable
@@ -472,7 +474,7 @@ def resolve_to_system(cls, cache: PyInfoCache | None, target: PythonInfo) -> Pyt
472474
_LOGGER.error("%d: prefix=%s, info=%r", len(prefixes) + 1, prefix, target)
473475
msg = "prefixes are causing a circle {}".format("|".join(prefixes.keys()))
474476
raise RuntimeError(msg)
475-
prefixes[prefix] = target
477+
prefixes[prefix_key] = target
476478
target = target.discover_exe(cache, prefix=prefix, exact=False)
477479
if target.executable != target.system_executable:
478480
resolved = cls.from_exe(target.system_executable, cache)

‎tests/test_py_info_extra.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,21 @@ def test_resolve_to_system_single_prefix_self_link(mocker: MockerFixture, caplog
168168
assert any("links back to itself" in r.message for r in caplog.records)
169169

170170

171+
def test_resolve_to_system_repeated_prefix_with_parent_segments(tmp_path: Path, mocker: MockerFixture) -> None:
172+
(tmp_path / "child").mkdir()
173+
target = PythonInfo()
174+
target.system_executable = target.real_prefix = None
175+
target.base_prefix = str(tmp_path)
176+
repeated = PythonInfo()
177+
repeated.system_executable = repeated.real_prefix = None
178+
repeated.base_prefix = str(tmp_path / "child" / "..")
179+
mocker.patch.object(PythonInfo, "discover_exe", return_value=repeated)
180+
181+
result = PythonInfo.resolve_to_system(None, target)
182+
183+
assert result.system_executable == repeated.executable
184+
185+
171186
def test_discover_exe_cache_hit() -> None:
172187
info = copy.deepcopy(CURRENT)
173188
cached = copy.deepcopy(CURRENT)

0 commit comments

Comments
 (0)