Skip to content

Commit 27b67b2

Browse files
authored
🐛 fix(env): strip PYTHONPATH from isolated builds (#1024)
1 parent c1454fd commit 27b67b2

3 files changed

Lines changed: 29 additions & 2 deletions

File tree

docs/changelog/405.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Strip ``PYTHONPATH`` from the environment during isolated builds to prevent host packages from leaking into the build -
2+
by :user:`gaborbernat`

src/build/env.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,11 @@ def make_extra_environ(self) -> dict[str, str]:
154154
return {
155155
'PATH': os.pathsep.join([self._env_backend.scripts_dir, path])
156156
if path is not None
157-
else self._env_backend.scripts_dir
157+
else self._env_backend.scripts_dir,
158+
# Set PYTHONPATH to empty to override any host value. An empty
159+
# PYTHONPATH is treated as unset by CPython's path initialization
160+
# (the ``if pythonpath_env:`` check makes it a no-op).
161+
'PYTHONPATH': '',
158162
}
159163

160164
def install(self, requirements: Collection[str], constraints: Collection[str] = []) -> None:
@@ -383,7 +387,9 @@ def install_dependencies( # pragma: no cover -- uv tests are skipped on PyPy, c
383387

384388
cmd += ['-c', os.path.abspath(constraint_file.name)]
385389

386-
run_subprocess(cmd, env={**os.environ, 'VIRTUAL_ENV': self._env_path})
390+
env = {k: v for k, v in os.environ.items() if k != 'PYTHONPATH'}
391+
env['VIRTUAL_ENV'] = self._env_path
392+
run_subprocess(cmd, env=env)
387393

388394
@property
389395
def display_name(self) -> str:

tests/test_env.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@
2828
MISSING_VIRTUALENV = importlib.util.find_spec('virtualenv') is None
2929

3030

31+
def test_make_extra_environ_overrides_pythonpath() -> None:
32+
with build.env.DefaultIsolatedEnv() as env:
33+
extra = env.make_extra_environ()
34+
assert extra['PYTHONPATH'] == ''
35+
assert env._env_backend.scripts_dir in extra['PATH']
36+
37+
38+
def test_uv_install_strips_pythonpath(
39+
mocker: pytest_mock.MockerFixture,
40+
monkeypatch: pytest.MonkeyPatch,
41+
) -> None:
42+
monkeypatch.setenv('PYTHONPATH', '/some/leaky/path')
43+
run_subprocess = mocker.patch('build.env.run_subprocess')
44+
with build.env.DefaultIsolatedEnv(installer='uv') as env:
45+
env.install(['some-package'])
46+
(install_call,) = run_subprocess.call_args_list
47+
assert 'PYTHONPATH' not in install_call.kwargs['env']
48+
49+
3150
@pytest.mark.isolated
3251
def test_isolation() -> None:
3352
subprocess.check_call([sys.executable, '-c', 'import build.env'])

0 commit comments

Comments
 (0)