Skip to content
Open
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
7 changes: 5 additions & 2 deletions Lib/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -1031,11 +1031,14 @@ def _venv(state):
)
break

if sys.prefix != site_prefix:
# site_prefix is already normalised by the abspath() above, but sys.prefix
# isn't. Normalise before comparing, or two spellings of the same directory
# look like a mismatch.
if os.path.normpath(sys.prefix) != site_prefix:
_warn(
f'Unexpected value in sys.prefix, expected {site_prefix}, got {sys.prefix}',
RuntimeWarning)
if sys.exec_prefix != site_prefix:
if os.path.normpath(sys.exec_prefix) != site_prefix:
_warn(
f'Unexpected value in sys.exec_prefix, expected {site_prefix}, got {sys.exec_prefix}',
RuntimeWarning)
Expand Down
56 changes: 54 additions & 2 deletions Lib/test/test_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
is_wasm32,
requires_venv_with_pip, TEST_HOME_DIR,
requires_resource, copy_python_src_ignore)
from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree,
TESTFN, FakePath)
from test.support.os_helper import (can_symlink, change_cwd, EnvironmentVarGuard,
rmtree, TESTFN, FakePath)
import unittest
import venv
from unittest.mock import patch, Mock
Expand Down Expand Up @@ -302,6 +302,58 @@ def test_prefixes(self):
self.assertEqual(pathlib.Path(out.strip().decode()),
pathlib.Path(expected), prefix)

@requireVenvCreate
def test_prefixes_with_non_normalised_executable(self):
"""
Test that a non-normalised executable path isn't counted as a mismatch.
"""
# gh-156495: sys.prefix isn't normalised, but the prefix derived from
# sys.executable is, so a string comparison flagged two spellings of the
# same directory.
rmtree(self.env_dir)
self.run_with_capture(venv.create, self.env_dir)
# Run from a directory next to the env so argv[0] starts with '..',
# which normpath() can't collapse.
subdir = tempfile.mkdtemp(dir=os.path.dirname(self.env_dir))
self.addCleanup(rmtree, subdir)
relative_exe = os.path.join(
os.pardir, os.path.basename(self.env_dir), self.bindir, self.exe)
# Windows does not resolve a relative executable against subprocess's
# cwd argument, so change directory in this process instead.
with change_cwd(subdir):
p = subprocess.run(
[relative_exe, '-c',
'import sys; print(sys.prefix); print(sys.exec_prefix)'],
capture_output=True, encoding='utf-8',
env={**os.environ, 'PYTHONHOME': ''})
self.assertEqual(p.returncode, 0, p.stderr)
self.assertNotIn('Unexpected value in sys.prefix', p.stderr)
self.assertNotIn('Unexpected value in sys.exec_prefix', p.stderr)
prefix, exec_prefix = p.stdout.splitlines()
for name, value in (('prefix', prefix), ('exec_prefix', exec_prefix)):
self.assertEqual(os.path.realpath(value),
os.path.realpath(self.env_dir), name)

@requireVenvCreate
def test_prefixes_mismatch_is_still_reported(self):
"""
Test that a genuine prefix mismatch is still reported.
"""
rmtree(self.env_dir)
self.run_with_capture(venv.create, self.env_dir)
# Move pyvenv.cfg next to the interpreter instead of leaving it in the
# prefix, so that sys.prefix and the prefix derived from sys.executable
# differ for real and not only in spelling.
os.rename(os.path.join(self.env_dir, 'pyvenv.cfg'),
os.path.join(self.env_dir, self.bindir, 'pyvenv.cfg'))
p = subprocess.run(
[self.envpy(), '-c', 'import sys; print(sys.prefix)'],
capture_output=True, encoding='utf-8',
env={**os.environ, 'PYTHONHOME': ''})
self.assertEqual(p.returncode, 0, p.stderr)
self.assertIn('Unexpected value in sys.prefix', p.stderr)
self.assertIn('Unexpected value in sys.exec_prefix', p.stderr)

@requireVenvCreate
def test_sysconfig(self):
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a false-positive :exc:`RuntimeWarning` from :mod:`site` when a virtual
environment interpreter is started through a non-normalised path such as
``../.venv/bin/python``. ``sys.prefix`` and the prefix derived from
:data:`sys.executable` are now compared in normalised form.
Loading