From 08846b2c6f6c72c86f764bc07767ef9d49bad742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Thu, 26 Feb 2026 20:52:02 +0000 Subject: [PATCH 01/10] GH-145273: warn when we can't find the standard library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- ...-02-26-20-51-54.gh-issue-145273.B5QcUp.rst | 2 ++ Modules/getpath.py | 23 ++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-20-51-54.gh-issue-145273.B5QcUp.rst 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 index 00000000000000..8d9e4a872d0d7a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-20-51-54.gh-issue-145273.B5QcUp.rst @@ -0,0 +1,2 @@ +A warning is now shown during :ref:`sys-path-init` if it can't find a valid +standard library. diff --git a/Modules/getpath.py b/Modules/getpath.py index ceb605a75c85f4..e06297b7b63a7b 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -236,6 +236,7 @@ def search_up(prefix, *landmarks, test=isfile): real_executable_dir = None platstdlib_dir = None +stdlib_zip = None # ****************************************************************************** # CALCULATE program_name @@ -697,12 +698,13 @@ def search_up(prefix, *landmarks, test=isfile): 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 @@ def search_up(prefix, *landmarks, test=isfile): 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 # ****************************************************************************** From d38285df901d98c08e1db76fd6a7ae021baced24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Thu, 26 Feb 2026 22:19:51 +0000 Subject: [PATCH 02/10] Fix test_embed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/test/test_embed.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 35246d7c484439..c8d2816b2d98cb 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1492,7 +1492,8 @@ def test_init_setpythonhome(self): self.default_program_name(config) env = {'TESTHOME': home, 'PYTHONPATH': paths_str} self.check_all_configs("test_init_setpythonhome", config, - api=API_COMPAT, env=env) + api=API_COMPAT, env=env, + ignore_stderr=True) # ignore missing stdlib warning def test_init_is_python_build_with_home(self): # Test _Py_path_config._is_python_build configuration (gh-91985) @@ -1536,7 +1537,8 @@ def test_init_is_python_build_with_home(self): env['NEGATIVE_ISPYTHONBUILD'] = '1' config['_is_python_build'] = 0 self.check_all_configs("test_init_is_python_build", config, - api=API_COMPAT, env=env) + api=API_COMPAT, env=env, + ignore_stderr=True) # ignore missing stdlib warning env['NEGATIVE_ISPYTHONBUILD'] = '0' config['_is_python_build'] = 1 @@ -1552,7 +1554,8 @@ def test_init_is_python_build_with_home(self): config.update(prefix=prefix, base_prefix=prefix, exec_prefix=exec_prefix, base_exec_prefix=exec_prefix) self.check_all_configs("test_init_is_python_build", config, - api=API_COMPAT, env=env) + api=API_COMPAT, env=env, + ignore_stderr=True) # ignore missing stdlib warning) def copy_paths_by_env(self, config): all_configs = self._get_expected_config() From c55803d035b05e736833a8198a19fb61837da3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Thu, 26 Feb 2026 23:46:52 +0000 Subject: [PATCH 03/10] Create stdlib dir on test_init_pybuilddir{,_win32} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/test/test_embed.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index c8d2816b2d98cb..f61ca58a743f9a 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1555,7 +1555,7 @@ def test_init_is_python_build_with_home(self): exec_prefix=exec_prefix, base_exec_prefix=exec_prefix) self.check_all_configs("test_init_is_python_build", config, api=API_COMPAT, env=env, - ignore_stderr=True) # ignore missing stdlib warning) + ignore_stderr=True) # ignore missing stdlib warning def copy_paths_by_env(self, config): all_configs = self._get_expected_config() @@ -1578,6 +1578,8 @@ def test_init_pybuilddir(self): # The stdlib dir is dirname(executable) + VPATH + 'Lib' stdlibdir = os.path.normpath(os.path.join(tmpdir, vpath, 'Lib')) os.mkdir(libdir) + # Create the directory to avoid the bad stdlib dir warning + os.makedirs(stdlibdir) filename = os.path.join(tmpdir, 'pybuilddir.txt') with open(filename, "w", encoding="utf8") as fp: @@ -1616,6 +1618,9 @@ def test_init_pybuilddir_win32(self): # The stdlib dir is dirname(executable) + VPATH + 'Lib' stdlibdir = os.path.normpath(os.path.join(tmpdir, vpath, 'Lib')) + # Create the directory to avoid the bad stdlib dir warning + os.makedirs(stdlibdir) + filename = os.path.join(tmpdir, 'pybuilddir.txt') with open(filename, "w", encoding="utf8") as fp: fp.write(tmpdir) From 3a67467c2a8a927b5ab275aab8d13de0027ea535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Thu, 26 Feb 2026 23:52:56 +0000 Subject: [PATCH 04/10] Resolve symlinks before file check (needed for Windows) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Modules/getpath.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/getpath.py b/Modules/getpath.py index e06297b7b63a7b..eb5737fe2196cf 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -774,12 +774,12 @@ def search_up(prefix, *landmarks, test=isfile): # ****************************************************************************** # Warn if the standard library is missing -if not stdlib_zip or not isfile(stdlib_zip): +if not stdlib_zip or not isfile(realpath(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): + if not stdlib_dir or not isdir(realpath(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): + elif (not platstdlib_dir and not build_prefix) or not isdir(realpath(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) From aa6014ac56fd5906983d599864e3b9134cfc6a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 27 Feb 2026 00:29:54 +0000 Subject: [PATCH 05/10] Revert "Resolve symlinks before file check (needed for Windows)" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3a67467c2a8a927b5ab275aab8d13de0027ea535. Signed-off-by: Filipe Laíns --- Modules/getpath.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/getpath.py b/Modules/getpath.py index eb5737fe2196cf..e06297b7b63a7b 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -774,12 +774,12 @@ def search_up(prefix, *landmarks, test=isfile): # ****************************************************************************** # Warn if the standard library is missing -if not stdlib_zip or not isfile(realpath(stdlib_zip)): +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(realpath(stdlib_dir)): + 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(realpath(platstdlib_dir)): + 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) From 540d965a58ae6651376ae6332d49c955b324e77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 27 Feb 2026 01:09:01 +0000 Subject: [PATCH 06/10] Fix test.support.PythonSymlink on Windows source builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/test/support/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 307bac65ae50a8..d4d3c7f1aefa66 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1716,9 +1716,10 @@ def _platform_specific(self): )) 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 From 9cb1166493fc5b0116c5e96bc56becc2e98c2bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 27 Feb 2026 01:36:55 +0000 Subject: [PATCH 07/10] Fix test_embed by creating expected paths instead of ignoring stderr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/test/test_embed.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index f61ca58a743f9a..a8ff40222ae46a 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1479,6 +1479,10 @@ def test_init_setpythonhome(self): stdlib = os.path.join(home, sys.platlibdir, f'python{version}{ABI_THREAD}') expected_paths = self.module_search_paths(prefix=home, exec_prefix=home) + # Create the expected paths to avoid the bad stdlib dir warning + for entry in expected_paths: + os.makedirs(entry, exist_ok=True) + config = { 'home': home, 'module_search_paths': expected_paths, @@ -1492,8 +1496,7 @@ def test_init_setpythonhome(self): self.default_program_name(config) env = {'TESTHOME': home, 'PYTHONPATH': paths_str} self.check_all_configs("test_init_setpythonhome", config, - api=API_COMPAT, env=env, - ignore_stderr=True) # ignore missing stdlib warning + api=API_COMPAT, env=env) def test_init_is_python_build_with_home(self): # Test _Py_path_config._is_python_build configuration (gh-91985) @@ -1521,6 +1524,10 @@ def test_init_is_python_build_with_home(self): stdlib = os.path.join(home, sys.platlibdir, f'python{version}{ABI_THREAD}') expected_paths = self.module_search_paths(prefix=home, exec_prefix=home) + # Create the expected paths to avoid the bad stdlib dir warning + for entry in expected_paths: + os.makedirs(entry, exist_ok=True) + config = { 'home': home, 'module_search_paths': expected_paths, @@ -1537,8 +1544,7 @@ def test_init_is_python_build_with_home(self): env['NEGATIVE_ISPYTHONBUILD'] = '1' config['_is_python_build'] = 0 self.check_all_configs("test_init_is_python_build", config, - api=API_COMPAT, env=env, - ignore_stderr=True) # ignore missing stdlib warning + api=API_COMPAT, env=env) env['NEGATIVE_ISPYTHONBUILD'] = '0' config['_is_python_build'] = 1 @@ -1554,8 +1560,7 @@ def test_init_is_python_build_with_home(self): config.update(prefix=prefix, base_prefix=prefix, exec_prefix=exec_prefix, base_exec_prefix=exec_prefix) self.check_all_configs("test_init_is_python_build", config, - api=API_COMPAT, env=env, - ignore_stderr=True) # ignore missing stdlib warning + api=API_COMPAT, env=env) def copy_paths_by_env(self, config): all_configs = self._get_expected_config() From d8b52804c76f9727a7e70d4d0fc2cf10f99602f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Sat, 28 Feb 2026 18:52:36 +0000 Subject: [PATCH 08/10] Don't blindly create paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/test/test_embed.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index a8ff40222ae46a..f3638b2b8d2579 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1479,10 +1479,6 @@ def test_init_setpythonhome(self): stdlib = os.path.join(home, sys.platlibdir, f'python{version}{ABI_THREAD}') expected_paths = self.module_search_paths(prefix=home, exec_prefix=home) - # Create the expected paths to avoid the bad stdlib dir warning - for entry in expected_paths: - os.makedirs(entry, exist_ok=True) - config = { 'home': home, 'module_search_paths': expected_paths, @@ -1524,10 +1520,6 @@ def test_init_is_python_build_with_home(self): stdlib = os.path.join(home, sys.platlibdir, f'python{version}{ABI_THREAD}') expected_paths = self.module_search_paths(prefix=home, exec_prefix=home) - # Create the expected paths to avoid the bad stdlib dir warning - for entry in expected_paths: - os.makedirs(entry, exist_ok=True) - config = { 'home': home, 'module_search_paths': expected_paths, From 36e43a795a3cfa0b28912329249e6c9685581ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Sat, 28 Feb 2026 22:40:25 +0000 Subject: [PATCH 09/10] Fix test_init_is_python_build_with_home MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/test/test_embed.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index f3638b2b8d2579..e83327078029df 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1528,15 +1528,26 @@ def test_init_is_python_build_with_home(self): '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 +1562,14 @@ def test_init_is_python_build_with_home(self): 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() From 8d63f14e69506338ebf9b00a2da532d6fc1c8bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Sat, 28 Feb 2026 22:59:14 +0000 Subject: [PATCH 10/10] Fix test_init_setpythonhome MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/test/test_embed.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index e83327078029df..39f5fd1d3822b1 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1491,8 +1491,12 @@ def test_init_setpythonhome(self): } 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)