11import os
22import random
33import re
4+ import shlex
45import sys
56import time
67
2021 StrPath , StrJSON , TestName , TestList , TestTuple , FilterTuple ,
2122 strip_py_suffix , count , format_duration ,
2223 printlist , get_temp_dir , get_work_dir , exit_timeout ,
23- display_header , cleanup_temp_dir ,
24+ display_header , cleanup_temp_dir , print_warning ,
2425 MS_WINDOWS )
2526
2627
@@ -47,7 +48,7 @@ class Regrtest:
4748 directly to set the values that would normally be set by flags
4849 on the command line.
4950 """
50- def __init__ (self , ns : Namespace ):
51+ def __init__ (self , ns : Namespace , reexec : bool = False ):
5152 # Log verbosity
5253 self .verbose : int = int (ns .verbose )
5354 self .quiet : bool = ns .quiet
@@ -69,6 +70,7 @@ def __init__(self, ns: Namespace):
6970 self .want_cleanup : bool = ns .cleanup
7071 self .want_rerun : bool = ns .rerun
7172 self .want_run_leaks : bool = ns .runleaks
73+ self .want_reexec : bool = (reexec and not ns .no_reexec )
7274
7375 # Select tests
7476 if ns .match_tests :
@@ -95,6 +97,7 @@ def __init__(self, ns: Namespace):
9597 self .worker_json : StrJSON | None = ns .worker_json
9698
9799 # Options to run tests
100+ self .ci_mode : bool = (ns .fast_ci or ns .slow_ci )
98101 self .fail_fast : bool = ns .failfast
99102 self .fail_env_changed : bool = ns .fail_env_changed
100103 self .fail_rerun : bool = ns .fail_rerun
@@ -483,7 +486,37 @@ def run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
483486 # processes.
484487 return self ._run_tests (selected , tests )
485488
489+ def _reexecute_python (self ):
490+ if self .python_cmd :
491+ # Do nothing if --python=cmd option is used
492+ return
493+
494+ python_opts = [
495+ '-u' , # Unbuffered stdout and stderr
496+ '-W' , 'default' , # Add warnings filter 'default'
497+ '-bb' , # Error on bytes/str comparison
498+ '-E' , # Ignore PYTHON* environment variables
499+ ]
500+
501+ cmd = [* sys .orig_argv , "--no-reexec" ]
502+ cmd [1 :1 ] = python_opts
503+
504+ # Make sure that messages before execv() are logged
505+ sys .stdout .flush ()
506+ sys .stderr .flush ()
507+
508+ try :
509+ os .execv (cmd [0 ], cmd )
510+ # execv() do no return and so we don't get to this line on success
511+ except OSError as exc :
512+ cmd_text = shlex .join (cmd )
513+ print_warning (f"Failed to reexecute Python: { exc !r} \n "
514+ f"Command: { cmd_text } " )
515+
486516 def main (self , tests : TestList | None = None ):
517+ if self .want_reexec and self .ci_mode :
518+ self ._reexecute_python ()
519+
487520 if self .junit_filename and not os .path .isabs (self .junit_filename ):
488521 self .junit_filename = os .path .abspath (self .junit_filename )
489522
@@ -515,7 +548,7 @@ def main(self, tests: TestList | None = None):
515548 sys .exit (exitcode )
516549
517550
518- def main (tests = None , ** kwargs ):
551+ def main (tests = None , reexec = False , ** kwargs ):
519552 """Run the Python suite."""
520553 ns = _parse_args (sys .argv [1 :], ** kwargs )
521- Regrtest (ns ).main (tests = tests )
554+ Regrtest (ns , reexec = reexec ).main (tests = tests )
0 commit comments