Skip to content

Commit 24773bc

Browse files
committed
Fix build isolation on system Pythons
use site.getsitepackages() where available instead of just purelib/platlib, which is often insufficient on e.g. System Pythons for Debian/macOS handle virtualenv < 20 overwriting site.py without getsitepackages() by preserving current behavior.
1 parent 7979dc0 commit 24773bc

File tree

3 files changed

+38
-4
lines changed

3 files changed

+38
-4
lines changed

news/6264.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix build environment isolation on some system Pythons.

src/pip/_internal/build_env.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import os
66
import pathlib
7+
import site
78
import sys
89
import textwrap
910
from collections import OrderedDict
@@ -55,6 +56,26 @@ def get_runnable_pip() -> str:
5556
return os.fsdecode(source / "__pip-runner__.py")
5657

5758

59+
def _get_system_sitepackages() -> Set[str]:
60+
"""Get system site packages
61+
62+
Usually from site.getsitepackages,
63+
but fallback on `get_purelib()/get_platlib()` if unavailable
64+
(e.g. in a virtualenv created by virtualenv<20)
65+
66+
Returns normalized set of strings.
67+
"""
68+
if hasattr(site, "getsitepackages"):
69+
system_sites = site.getsitepackages()
70+
else:
71+
# virtualenv < 20 overwrites site.py without getsitepackages
72+
# fallback on get_purelib/get_platlib.
73+
# this is known to miss things, but shouldn't in the cases
74+
# where getsitepackages() has been removed (inside a virtualenv)
75+
system_sites = [get_purelib(), get_platlib()]
76+
return {os.path.normcase(path) for path in system_sites}
77+
78+
5879
class BuildEnvironment:
5980
"""Creates and manages an isolated environment to install build deps"""
6081

@@ -75,9 +96,8 @@ def __init__(self) -> None:
7596
# Customize site to:
7697
# - ensure .pth files are honored
7798
# - prevent access to system site packages
78-
system_sites = {
79-
os.path.normcase(site) for site in (get_purelib(), get_platlib())
80-
}
99+
system_sites = _get_system_sitepackages()
100+
81101
self._site_dir = os.path.join(temp_dir.path, "site")
82102
if not os.path.exists(self._site_dir):
83103
os.mkdir(self._site_dir)

tests/functional/test_build_env.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66

7-
from pip._internal.build_env import BuildEnvironment
7+
from pip._internal.build_env import BuildEnvironment, _get_system_sitepackages
88
from tests.lib import (
99
PipTestEnvironment,
1010
TestPipResult,
@@ -226,6 +226,10 @@ def test_build_env_isolation(script: PipTestEnvironment) -> None:
226226
script.pip_install_local("-t", target, pkg_whl)
227227
script.environ["PYTHONPATH"] = target
228228

229+
system_sites = _get_system_sitepackages()
230+
# there should always be something to exclude
231+
assert system_sites
232+
229233
run_with_build_env(
230234
script,
231235
"",
@@ -247,5 +251,14 @@ def test_build_env_isolation(script: PipTestEnvironment) -> None:
247251
})), file=sys.stderr)
248252
print('sys.path:\n ' + '\n '.join(sys.path), file=sys.stderr)
249253
sys.exit(1)
254+
"""
255+
f"""
256+
# second check: direct check of exclusion of system site packages
257+
import os
258+
259+
normalized_path = [os.path.normcase(path) for path in sys.path]
260+
for system_path in {system_sites!r}:
261+
assert system_path not in normalized_path, \
262+
f"{{system_path}} found in {{normalized_path}}"
250263
""",
251264
)

0 commit comments

Comments
 (0)