Skip to content

Commit

Permalink
Fix build isolation on system Pythons
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
minrk committed Sep 23, 2022
1 parent 7979dc0 commit 93a2424
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 4 deletions.
1 change: 1 addition & 0 deletions news/6264.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix build environment isolation on some system Pythons.
26 changes: 23 additions & 3 deletions src/pip/_internal/build_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import os
import pathlib
import site
import sys
import textwrap
from collections import OrderedDict
Expand Down Expand Up @@ -55,6 +56,26 @@ def get_runnable_pip() -> str:
return os.fsdecode(source / "__pip-runner__.py")


def _get_system_sitepackages() -> Set[str]:
"""Get system site packages
Usually from site.getsitepackages,
but fallback on `get_purelib()/get_platlib()` if unavailable
(e.g. in a virtualenv created by virtualenv<20)
Returns normalized set of strings.
"""
if hasattr(site, "getsitepackages"):
system_sites = site.getsitepackages([sys.base_prefix, sys.prefix])
else:
# virtualenv < 20 overwrites site.py without getsitepackages
# fallback on get_purelib/get_platlib.
# this is known to miss things, but shouldn't in the cases
# where getsitepackages() has been removed (inside a virtualenv)
system_sites = [get_purelib(), get_platlib()]
return {os.path.normcase(path) for path in system_sites}


class BuildEnvironment:
"""Creates and manages an isolated environment to install build deps"""

Expand All @@ -75,9 +96,8 @@ def __init__(self) -> None:
# Customize site to:
# - ensure .pth files are honored
# - prevent access to system site packages
system_sites = {
os.path.normcase(site) for site in (get_purelib(), get_platlib())
}
system_sites = _get_system_sitepackages()

self._site_dir = os.path.join(temp_dir.path, "site")
if not os.path.exists(self._site_dir):
os.mkdir(self._site_dir)
Expand Down
15 changes: 14 additions & 1 deletion tests/functional/test_build_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest

from pip._internal.build_env import BuildEnvironment
from pip._internal.build_env import BuildEnvironment, _get_system_sitepackages
from tests.lib import (
PipTestEnvironment,
TestPipResult,
Expand Down Expand Up @@ -226,6 +226,10 @@ def test_build_env_isolation(script: PipTestEnvironment) -> None:
script.pip_install_local("-t", target, pkg_whl)
script.environ["PYTHONPATH"] = target

system_sites = _get_system_sitepackages()
# there should always be something to exclude
assert system_sites

run_with_build_env(
script,
"",
Expand All @@ -247,5 +251,14 @@ def test_build_env_isolation(script: PipTestEnvironment) -> None:
})), file=sys.stderr)
print('sys.path:\n ' + '\n '.join(sys.path), file=sys.stderr)
sys.exit(1)
"""
f"""
# second check: direct check of exclusion of system site packages
import os
normalized_path = [os.path.normcase(path) for path in sys.path]
for system_path in {system_sites!r}:
assert system_path not in normalized_path, \
f"{{system_path}} found in {{normalized_path}}"
""",
)

0 comments on commit 93a2424

Please sign in to comment.