From cd26ee400782616c836951397a22727ba65602e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Tue, 3 Nov 2026 06:37:35 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(discovery):=20reject=20unrel?= =?UTF-8?q?ated=20Python?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelog/238.bugfix.rst | 1 + src/python_discovery/_discovery.py | 4 ++-- tests/test_discovery.py | 7 +++++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/238.bugfix.rst diff --git a/docs/changelog/238.bugfix.rst b/docs/changelog/238.bugfix.rst new file mode 100644 index 0000000..4814788 --- /dev/null +++ b/docs/changelog/238.bugfix.rst @@ -0,0 +1 @@ +Reject generic Python executables when a requested implementation is unavailable. diff --git a/src/python_discovery/_discovery.py b/src/python_discovery/_discovery.py index 3a51d78..07d8684 100644 --- a/src/python_discovery/_discovery.py +++ b/src/python_discovery/_discovery.py @@ -501,8 +501,8 @@ def path_exes(path: Path) -> Generator[tuple[Path, bool], None, None]: yield direct_path, impl_must_match for exe in path.iterdir(): - if pat.fullmatch(exe.name): - yield exe.absolute(), impl_must_match + if match := pat.fullmatch(exe.name): + yield exe.absolute(), impl_must_match or match["impl"].lower() == "python" return path_exes diff --git a/tests/test_discovery.py b/tests/test_discovery.py index 4ef500b..d498fa9 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -74,6 +74,13 @@ def test_discovery_via_path_not_found(tmp_path: Path, monkeypatch: pytest.Monkey assert interpreter is None +def test_unknown_implementation_rejects_python_on_path(tmp_path: Path, session_cache: DiskCache) -> None: + env = {"PATH": str(Path(sys.executable).parent), "UV_PYTHON_INSTALL_DIR": str(tmp_path / "missing")} + + assert get_interpreter("missing", [], cache=session_cache, env=env) is None + assert list(iter_interpreters("missing", cache=session_cache, env=env)) == [] + + @pytest.mark.skipif(sys.platform == "win32", reason="POSIX symlink names") def test_discovery_via_path_literal_symlink(tmp_path: Path) -> None: link = tmp_path / "python_custom"