From c5d3599765b9fb41739231a9efaddcaace4b5e0a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 26 Mar 2026 10:56:38 +0100 Subject: [PATCH] gh-146410: Add --pythoninfo option to regrtest (GH-146413) * Android now runs tests with --pythoninfo to dump build information. * Add display_title() function. (cherry picked from commit b5a27bdcf035c2a1c1ffc746bf3720c284991873) Co-authored-by: Victor Stinner --- Android/android.py | 3 ++- Lib/test/libregrtest/cmdline.py | 2 ++ Lib/test/libregrtest/main.py | 23 ++++++++++++++++------- Lib/test/libregrtest/utils.py | 6 ++++++ Lib/test/pythoninfo.py | 4 ++-- Lib/test/test_regrtest.py | 12 ++++++++++++ 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/Android/android.py b/Android/android.py index 7e9c52452a22b4..246e45de72c407 100755 --- a/Android/android.py +++ b/Android/android.py @@ -659,7 +659,8 @@ async def gradle_task(context): # Randomization is disabled because order-dependent failures are # much less likely to pass on a rerun in single-process mode. "-m", "test", - f"--{context.ci_mode}-ci", "--single-process", "--no-randomize" + f"--{context.ci_mode}-ci", "--single-process", "--no-randomize", + "--pythoninfo", ] if not any(arg in context.args for arg in ["-c", "-m"]): diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 2c404f6d80bcf3..ea26cc849f8182 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -393,6 +393,8 @@ def _create_parser(): help='remove old test_python_* directories') group.add_argument('--bisect', action='store_true', help='if some tests fail, run test.bisect_cmd on them') + group.add_argument('--pythoninfo', action='store_true', + help="run python -m test.pythoninfo before tests") group.add_argument('--dont-add-python-opts', dest='_add_python_opts', action='store_false', help="internal option, don't use it") diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index d26e0e113ced2d..62f66adadebbb1 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -26,7 +26,7 @@ strip_py_suffix, count, format_duration, printlist, get_temp_dir, get_work_dir, exit_timeout, display_header, cleanup_temp_dir, print_warning, - is_cross_compiled, get_host_runner, + is_cross_compiled, get_host_runner, display_title, EXIT_TIMEOUT) @@ -126,6 +126,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): self.coverage: bool = ns.trace self.coverage_dir: StrPath | None = ns.coverdir self._tmp_dir: StrPath | None = ns.tempdir + self.pythoninfo: bool = ns.pythoninfo # Randomize self.randomize: bool = ns.randomize @@ -322,9 +323,7 @@ def _run_bisect(self, runtests: RunTests, test: str, progress: str) -> bool: title = f"Bisect {test}" if progress: title = f"{title} ({progress})" - print(title) - print("#" * len(title)) - print() + display_title(title) cmd = runtests.create_python_cmd() cmd.extend([ @@ -345,9 +344,7 @@ def _run_bisect(self, runtests: RunTests, test: str, progress: str) -> bool: exitcode = proc.returncode title = f"{title}: exit code {exitcode}" - print(title) - print("#" * len(title)) - print(flush=True) + display_title(title) if exitcode: print(f"Bisect failed with exit code {exitcode}") @@ -752,6 +749,15 @@ def tmp_dir(self) -> StrPath: ) return self._tmp_dir + def run_pythoninfo(self): + from test import pythoninfo + try: + pythoninfo.main() + except SystemExit: + # Ignore non-zero exit code on purpose + pass + print() + def main(self, tests: TestList | None = None) -> NoReturn: if self.want_add_python_opts: self._add_python_opts() @@ -765,6 +771,9 @@ def main(self, tests: TestList | None = None) -> NoReturn: if self.want_wait: input("Press any key to continue...") + if self.pythoninfo: + self.run_pythoninfo() + setup_test_dir(self.test_dir) selected, tests = self.find_tests(tests) diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index 68ea34e21af014..cde9f6f8344e6f 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -730,3 +730,9 @@ def _sanitize_xml_replace(regs): def sanitize_xml(text: str) -> str: return ILLEGAL_XML_CHARS_RE.sub(_sanitize_xml_replace, text) + + +def display_title(title): + print(title) + print("#" * len(title)) + print(flush=True) diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index 4b000284526e5f..3e64422fdfbf3a 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -1360,9 +1360,9 @@ def collect_info(info): def dump_info(info, file=None): - title = "Python debug information" + title = "Python build information" print(title) - print("=" * len(title)) + print("#" * len(title)) print() infos = info.get_infos() diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index b578f81c075c94..4c43629b39a046 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -571,6 +571,13 @@ def test_single_process(self): self.assertEqual(regrtest.num_workers, 0) self.assertTrue(regrtest.single_process) + def test_pythoninfo(self): + ns = self.parse_args([]) + self.assertFalse(ns.pythoninfo) + + ns = self.parse_args(['--pythoninfo']) + self.assertTrue(ns.pythoninfo) + @dataclasses.dataclass(slots=True) class Rerun: @@ -2431,6 +2438,11 @@ def test_pgo_exclude(self): self.assertNotIn('test_re', tests) self.assertEqual(len(tests), len(pgo_tests) - 1) + def test_pythoninfo(self): + testname = self.create_test() + output = self.run_tests('--pythoninfo', testname) + self.assertIn("Python build information", output) + class TestUtils(unittest.TestCase): def test_format_duration(self):