diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 2894da7509110c..9e04138890c257 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -9,6 +9,7 @@ from typing import NoReturn from test.support import (os_helper, MS_WINDOWS, flush_std_streams, + can_use_suppress_immortalization, suppress_immortalization) from .cmdline import _parse_args, Namespace @@ -536,8 +537,16 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: if self.num_workers: self._run_tests_mp(runtests, self.num_workers) else: + # gh-135734: suppress_immortalization() raises SkipTest + # if _testinternalcapi is missing and the -R option is set. + if not can_use_suppress_immortalization(runtests.hunt_refleak): + print("Module '_testinternalcapi' is missing. " + "Did you disable it with --disable-test-modules?", + file=sys.stderr) + raise SystemExit(1) + # gh-117783: don't immortalize deferred objects when tracking - # refleaks. Only releveant for the free-threaded build. + # refleaks. Only relevant for the free-threaded build. with suppress_immortalization(runtests.hunt_refleak): self.run_tests_sequentially(runtests) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index c60fc03064f3c2..279a9ca23ef4df 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -519,25 +519,43 @@ def requires_debug_ranges(reason='requires co_positions / debug_ranges'): reason = e.args[0] if e.args else reason return unittest.skipIf(skip, reason) -@contextlib.contextmanager -def suppress_immortalization(suppress=True): - """Suppress immortalization of deferred objects.""" + +def can_use_suppress_immortalization(suppress=True): + """Check if suppress_immortalization(suppress) can be used. + + Use this helper in code where SkipTest must be eagerly handled. + """ + if not suppress: + return True try: import _testinternalcapi except ImportError: - yield - return + return False + return True + +@contextlib.contextmanager +def suppress_immortalization(suppress=True): + """Suppress immortalization of deferred objects. + + If _testinternalcapi is not available, the decorated test or class + is skipped. Use can_use_suppress_immortalization() outside test cases + to check if this decorator can be used. + """ if not suppress: - yield + yield # no-op return + from .import_helper import import_module + + _testinternalcapi = import_module("_testinternalcapi") _testinternalcapi.suppress_immortalization(True) try: yield finally: _testinternalcapi.suppress_immortalization(False) + def skip_if_suppress_immortalization(): try: import _testinternalcapi diff --git a/Misc/NEWS.d/next/Build/2025-08-20-16-45-34.gh-issue-135734.2hvJCe.rst b/Misc/NEWS.d/next/Build/2025-08-20-16-45-34.gh-issue-135734.2hvJCe.rst new file mode 100644 index 00000000000000..9a835804d8114b --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-08-20-16-45-34.gh-issue-135734.2hvJCe.rst @@ -0,0 +1,4 @@ +Python can correctly be configured and built with +``./configure --enable-optimizations --disable-test-modules``. +Previously, the profile data generation step failed due to PGO tests where +immortalization couldn't be properly suppressed. Patch by Bénédikt Tran.