]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-145273: warn when we can't find the standard library (#145274)
authorFilipe Laíns <lains@riseup.net>
Mon, 2 Mar 2026 18:51:45 +0000 (18:51 +0000)
committerGitHub <noreply@github.com>
Mon, 2 Mar 2026 18:51:45 +0000 (18:51 +0000)
Lib/test/support/__init__.py
Lib/test/test_embed.py
Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-20-51-54.gh-issue-145273.B5QcUp.rst [new file with mode: 0644]
Modules/getpath.py

index 307bac65ae50a85e2d618ff4efadb836c9f6b6d3..d4d3c7f1aefa6606a331bb5fd79493317b91ae5b 100644 (file)
@@ -1716,9 +1716,10 @@ class PythonSymlink:
                 ))
 
             self._env = {k.upper(): os.getenv(k) for k in os.environ}
-            self._env["PYTHONHOME"] = os.path.dirname(self.real)
+            home = os.path.dirname(self.real)
             if sysconfig.is_python_build():
-                self._env["PYTHONPATH"] = STDLIB_DIR
+                home = os.path.join(home, sysconfig.get_config_var('VPATH'))
+            self._env["PYTHONHOME"] = home
     else:
         def _platform_specific(self):
             pass
index 35246d7c4844396d8360cffaabb07375634a71a4..b3f0cb5d35de5d9da89a8f2a9efd744b9aa59690 100644 (file)
@@ -1491,8 +1491,12 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
         }
         self.default_program_name(config)
         env = {'TESTHOME': home, 'PYTHONPATH': paths_str}
+        # When running from source, TESTHOME will be the build directory, which
+        # isn't a valid home unless _is_python_build is set. getpath will then
+        # fail to find the standard library and show a warning, so we need to
+        # ignore stderr.
         self.check_all_configs("test_init_setpythonhome", config,
-                               api=API_COMPAT, env=env)
+                               api=API_COMPAT, env=env, ignore_stderr=True)
 
     def test_init_is_python_build_with_home(self):
         # Test _Py_path_config._is_python_build configuration (gh-91985)
@@ -1528,15 +1532,26 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             'exec_prefix': exec_prefix,
             'base_exec_prefix': exec_prefix,
             'pythonpath_env': paths_str,
-            'stdlib_dir': stdlib,
+            'stdlib_dir': stdlib,  # Only correct on _is_python_build==0!
         }
         # The code above is taken from test_init_setpythonhome()
         env = {'TESTHOME': home, 'PYTHONPATH': paths_str}
 
         env['NEGATIVE_ISPYTHONBUILD'] = '1'
         config['_is_python_build'] = 0
+        # This configuration doesn't set a valid stdlibdir/plststdlibdir because
+        # with _is_python_build=0 getpath doesn't check for the build directory
+        # landmarks in PYTHONHOME/Py_SetPythonHome.
+        # getpath correctly shows a warning, which messes up check_all_configs,
+        # so we need to ignore stderr.
         self.check_all_configs("test_init_is_python_build", config,
-                               api=API_COMPAT, env=env)
+                               api=API_COMPAT, env=env, ignore_stderr=True)
+
+        # config['stdlib_dir'] = os.path.join(home, 'Lib')
+        # FIXME: This test does not check if stdlib_dir is calculated correctly.
+        #        test_init_is_python_build runs the initialization twice,
+        #        setting stdlib_dir in _Py_path_config on the first run, which
+        #        then overrides the stdlib_dir calculation (as of GH-108730).
 
         env['NEGATIVE_ISPYTHONBUILD'] = '0'
         config['_is_python_build'] = 1
@@ -1551,8 +1566,14 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             expected_paths[0] = self.module_search_paths(prefix=prefix)[0]
             config.update(prefix=prefix, base_prefix=prefix,
                           exec_prefix=exec_prefix, base_exec_prefix=exec_prefix)
+        # This also shows the bad stdlib warning, getpath is run twice. The
+        # first time with _is_python_build=0, which results in the warning just
+        # as explained above. However, the second time a valid standard library
+        # should be found, but the stdlib_dir is cached in _Py_path_config from
+        # the first run, which ovewrites it, so it also shows the warning.
+        # Also ignore stderr.
         self.check_all_configs("test_init_is_python_build", config,
-                               api=API_COMPAT, env=env)
+                               api=API_COMPAT, env=env, ignore_stderr=True)
 
     def copy_paths_by_env(self, config):
         all_configs = self._get_expected_config()
@@ -1612,6 +1633,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             prefix = os.path.normpath(os.path.join(tmpdir, vpath))
             # The stdlib dir is dirname(executable) + VPATH + 'Lib'
             stdlibdir = os.path.normpath(os.path.join(tmpdir, vpath, 'Lib'))
+            os.mkdir(stdlibdir)
 
             filename = os.path.join(tmpdir, 'pybuilddir.txt')
             with open(filename, "w", encoding="utf8") as fp:
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-20-51-54.gh-issue-145273.B5QcUp.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-20-51-54.gh-issue-145273.B5QcUp.rst
new file mode 100644 (file)
index 0000000..8d9e4a8
--- /dev/null
@@ -0,0 +1,2 @@
+A warning is now shown during :ref:`sys-path-init` if it can't find a valid
+standard library.
index ceb605a75c85f44f6e56e8a9f6ad9b3775ae3136..e06297b7b63a7b30da1f9ad3701c8ddf7d52e074 100644 (file)
@@ -236,6 +236,7 @@ stdlib_dir_was_set_in_config = bool(stdlib_dir)
 
 real_executable_dir = None
 platstdlib_dir = None
+stdlib_zip = None
 
 # ******************************************************************************
 # CALCULATE program_name
@@ -697,12 +698,13 @@ elif not pythonpath_was_set:
             library_dir = dirname(library)
         else:
             library_dir = executable_dir
-        pythonpath.append(joinpath(library_dir, ZIP_LANDMARK))
+        stdlib_zip = joinpath(library_dir, ZIP_LANDMARK)
     elif build_prefix:
         # QUIRK: POSIX uses the default prefix when in the build directory
-        pythonpath.append(joinpath(PREFIX, ZIP_LANDMARK))
+        stdlib_zip = joinpath(PREFIX, ZIP_LANDMARK)
     else:
-        pythonpath.append(joinpath(base_prefix, ZIP_LANDMARK))
+        stdlib_zip = joinpath(base_prefix, ZIP_LANDMARK)
+    pythonpath.append(stdlib_zip)
 
     if os_name == 'nt' and use_environment and winreg:
         # QUIRK: Windows also lists paths in the registry. Paths are stored
@@ -767,6 +769,21 @@ elif not pythonpath_was_set:
     config['module_search_paths_set'] = 1
 
 
+# ******************************************************************************
+# SANITY CHECKS
+# ******************************************************************************
+
+# Warn if the standard library is missing
+if not stdlib_zip or not isfile(stdlib_zip):
+    home_hint = f"The Python 'home' directory was set to {home!r}, is this correct?"
+    if not stdlib_dir or not isdir(stdlib_dir):
+        hint = home_hint if home else f'sys.prefix is set to {prefix}, is this correct?'
+        warn('WARN: Could not find the standard library directory! ' + hint)
+    elif (not platstdlib_dir and not build_prefix) or not isdir(platstdlib_dir):
+        hint = home_hint if home else f'sys.exec_prefix is set to {exec_prefix}, is this correct?'
+        warn('WARN: Could not find the platform standard library directory! ' + hint)
+
+
 # ******************************************************************************
 # POSIX prefix/exec_prefix QUIRKS
 # ******************************************************************************