Skip to content

Commit 3ab39d2

Browse files
authored
GH-145273: warn when we can't find the standard library (#145274)
1 parent 5c3a47b commit 3ab39d2

File tree

4 files changed

+51
-9
lines changed

4 files changed

+51
-9
lines changed

Lib/test/support/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,9 +1716,10 @@ def _platform_specific(self):
17161716
))
17171717

17181718
self._env = {k.upper(): os.getenv(k) for k in os.environ}
1719-
self._env["PYTHONHOME"] = os.path.dirname(self.real)
1719+
home = os.path.dirname(self.real)
17201720
if sysconfig.is_python_build():
1721-
self._env["PYTHONPATH"] = STDLIB_DIR
1721+
home = os.path.join(home, sysconfig.get_config_var('VPATH'))
1722+
self._env["PYTHONHOME"] = home
17221723
else:
17231724
def _platform_specific(self):
17241725
pass

Lib/test/test_embed.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,8 +1491,12 @@ def test_init_setpythonhome(self):
14911491
}
14921492
self.default_program_name(config)
14931493
env = {'TESTHOME': home, 'PYTHONPATH': paths_str}
1494+
# When running from source, TESTHOME will be the build directory, which
1495+
# isn't a valid home unless _is_python_build is set. getpath will then
1496+
# fail to find the standard library and show a warning, so we need to
1497+
# ignore stderr.
14941498
self.check_all_configs("test_init_setpythonhome", config,
1495-
api=API_COMPAT, env=env)
1499+
api=API_COMPAT, env=env, ignore_stderr=True)
14961500

14971501
def test_init_is_python_build_with_home(self):
14981502
# Test _Py_path_config._is_python_build configuration (gh-91985)
@@ -1528,15 +1532,26 @@ def test_init_is_python_build_with_home(self):
15281532
'exec_prefix': exec_prefix,
15291533
'base_exec_prefix': exec_prefix,
15301534
'pythonpath_env': paths_str,
1531-
'stdlib_dir': stdlib,
1535+
'stdlib_dir': stdlib, # Only correct on _is_python_build==0!
15321536
}
15331537
# The code above is taken from test_init_setpythonhome()
15341538
env = {'TESTHOME': home, 'PYTHONPATH': paths_str}
15351539

15361540
env['NEGATIVE_ISPYTHONBUILD'] = '1'
15371541
config['_is_python_build'] = 0
1542+
# This configuration doesn't set a valid stdlibdir/plststdlibdir because
1543+
# with _is_python_build=0 getpath doesn't check for the build directory
1544+
# landmarks in PYTHONHOME/Py_SetPythonHome.
1545+
# getpath correctly shows a warning, which messes up check_all_configs,
1546+
# so we need to ignore stderr.
15381547
self.check_all_configs("test_init_is_python_build", config,
1539-
api=API_COMPAT, env=env)
1548+
api=API_COMPAT, env=env, ignore_stderr=True)
1549+
1550+
# config['stdlib_dir'] = os.path.join(home, 'Lib')
1551+
# FIXME: This test does not check if stdlib_dir is calculated correctly.
1552+
# test_init_is_python_build runs the initialization twice,
1553+
# setting stdlib_dir in _Py_path_config on the first run, which
1554+
# then overrides the stdlib_dir calculation (as of GH-108730).
15401555

15411556
env['NEGATIVE_ISPYTHONBUILD'] = '0'
15421557
config['_is_python_build'] = 1
@@ -1551,8 +1566,14 @@ def test_init_is_python_build_with_home(self):
15511566
expected_paths[0] = self.module_search_paths(prefix=prefix)[0]
15521567
config.update(prefix=prefix, base_prefix=prefix,
15531568
exec_prefix=exec_prefix, base_exec_prefix=exec_prefix)
1569+
# This also shows the bad stdlib warning, getpath is run twice. The
1570+
# first time with _is_python_build=0, which results in the warning just
1571+
# as explained above. However, the second time a valid standard library
1572+
# should be found, but the stdlib_dir is cached in _Py_path_config from
1573+
# the first run, which ovewrites it, so it also shows the warning.
1574+
# Also ignore stderr.
15541575
self.check_all_configs("test_init_is_python_build", config,
1555-
api=API_COMPAT, env=env)
1576+
api=API_COMPAT, env=env, ignore_stderr=True)
15561577

15571578
def copy_paths_by_env(self, config):
15581579
all_configs = self._get_expected_config()
@@ -1612,6 +1633,7 @@ def test_init_pybuilddir_win32(self):
16121633
prefix = os.path.normpath(os.path.join(tmpdir, vpath))
16131634
# The stdlib dir is dirname(executable) + VPATH + 'Lib'
16141635
stdlibdir = os.path.normpath(os.path.join(tmpdir, vpath, 'Lib'))
1636+
os.mkdir(stdlibdir)
16151637

16161638
filename = os.path.join(tmpdir, 'pybuilddir.txt')
16171639
with open(filename, "w", encoding="utf8") as fp:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
A warning is now shown during :ref:`sys-path-init` if it can't find a valid
2+
standard library.

Modules/getpath.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ def search_up(prefix, *landmarks, test=isfile):
236236

237237
real_executable_dir = None
238238
platstdlib_dir = None
239+
stdlib_zip = None
239240

240241
# ******************************************************************************
241242
# CALCULATE program_name
@@ -697,12 +698,13 @@ def search_up(prefix, *landmarks, test=isfile):
697698
library_dir = dirname(library)
698699
else:
699700
library_dir = executable_dir
700-
pythonpath.append(joinpath(library_dir, ZIP_LANDMARK))
701+
stdlib_zip = joinpath(library_dir, ZIP_LANDMARK)
701702
elif build_prefix:
702703
# QUIRK: POSIX uses the default prefix when in the build directory
703-
pythonpath.append(joinpath(PREFIX, ZIP_LANDMARK))
704+
stdlib_zip = joinpath(PREFIX, ZIP_LANDMARK)
704705
else:
705-
pythonpath.append(joinpath(base_prefix, ZIP_LANDMARK))
706+
stdlib_zip = joinpath(base_prefix, ZIP_LANDMARK)
707+
pythonpath.append(stdlib_zip)
706708

707709
if os_name == 'nt' and use_environment and winreg:
708710
# QUIRK: Windows also lists paths in the registry. Paths are stored
@@ -767,6 +769,21 @@ def search_up(prefix, *landmarks, test=isfile):
767769
config['module_search_paths_set'] = 1
768770

769771

772+
# ******************************************************************************
773+
# SANITY CHECKS
774+
# ******************************************************************************
775+
776+
# Warn if the standard library is missing
777+
if not stdlib_zip or not isfile(stdlib_zip):
778+
home_hint = f"The Python 'home' directory was set to {home!r}, is this correct?"
779+
if not stdlib_dir or not isdir(stdlib_dir):
780+
hint = home_hint if home else f'sys.prefix is set to {prefix}, is this correct?'
781+
warn('WARN: Could not find the standard library directory! ' + hint)
782+
elif (not platstdlib_dir and not build_prefix) or not isdir(platstdlib_dir):
783+
hint = home_hint if home else f'sys.exec_prefix is set to {exec_prefix}, is this correct?'
784+
warn('WARN: Could not find the platform standard library directory! ' + hint)
785+
786+
770787
# ******************************************************************************
771788
# POSIX prefix/exec_prefix QUIRKS
772789
# ******************************************************************************

0 commit comments

Comments
 (0)