Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions mypy/pyinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ def getsyspath() -> list[str]:
)
stdlib = sysconfig.get_path("stdlib")
stdlib_ext = os.path.join(stdlib, "lib-dynload")
excludes = {stdlib_zip, stdlib, stdlib_ext}
# sysconfig may return a symlinked path while sys.path contains its resolved
# target (for example, with Homebrew Python installations).
excludes = {os.path.realpath(p) for p in (stdlib_zip, stdlib, stdlib_ext)}

# Drop the first entry of sys.path
# - If pyinfo.py is executed as a script (in a subprocess), this is the directory
Expand All @@ -63,7 +65,9 @@ def getsyspath() -> list[str]:
offset = 0 if sys.version_info >= (3, 11) and sys.flags.safe_path else 1

abs_sys_path = (os.path.abspath(p) for p in sys.path[offset:])
return [p for p in abs_sys_path if p not in excludes]
# Keep the original absolute spelling in the result; only resolve paths
# for the standard-library exclusion comparison.
return [p for p in abs_sys_path if os.path.realpath(p) not in excludes]


def getsearchdirs() -> tuple[list[str], list[str]]:
Expand Down
39 changes: 39 additions & 0 deletions mypy/test/testpyinfo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from __future__ import annotations

import os
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

from mypy import pyinfo


class TestPyInfo(unittest.TestCase):
def test_getsyspath_excludes_symlinked_stdlib(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
real_stdlib = root / "real" / "lib" / "python3.13"
real_stdlib.mkdir(parents=True)
symlinked_stdlib = root / "link" / "lib" / "python3.13"
symlinked_stdlib.parent.parent.symlink_to(root / "real", target_is_directory=True)
site_packages = root / "site-packages"
site_packages.mkdir()

with (
patch.object(pyinfo.sys, "base_exec_prefix", str(root)),
patch.object(
pyinfo.sysconfig, "get_path", autospec=True, return_value=str(symlinked_stdlib)
),
patch.object(
pyinfo.sys, "path", [str(root), str(real_stdlib), str(site_packages)]
),
):
search_path = pyinfo.getsyspath()

assert os.path.abspath(real_stdlib) not in search_path
assert os.path.abspath(site_packages) in search_path


if __name__ == "__main__":
unittest.main()
Loading