77import time
88import trace
99
10- from test import support
1110from test .support import os_helper , MS_WINDOWS
1211
1312from .cmdline import _parse_args , Namespace
@@ -73,6 +72,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False):
7372 self .want_cleanup : bool = ns .cleanup
7473 self .want_rerun : bool = ns .rerun
7574 self .want_run_leaks : bool = ns .runleaks
75+ self .want_bisect : bool = ns .bisect
7676
7777 self .ci_mode : bool = (ns .fast_ci or ns .slow_ci )
7878 self .want_add_python_opts : bool = (_add_python_opts
@@ -273,6 +273,55 @@ def rerun_failed_tests(self, runtests: RunTests):
273273
274274 self .display_result (rerun_runtests )
275275
276+ def _run_bisect (self , runtests : RunTests , test : str , progress : str ) -> bool :
277+ print ()
278+ title = f"Bisect { test } "
279+ if progress :
280+ title = f"{ title } ({ progress } )"
281+ print (title )
282+ print ("#" * len (title ))
283+ print ()
284+
285+ if self .python_cmd :
286+ # Temp patch for https://github.com/python/cpython/issues/94052
287+ self .log (
288+ "Bisect failed tests is not supported with --python "
289+ "host runner option."
290+ )
291+ return
292+
293+ cmd = runtests .create_python_cmd ()
294+ cmd .extend ([
295+ "-m" , "test.bisect_cmd" ,
296+ # Limit to 25 iterations (instead of 100) to not abuse CI resources
297+ "--max-iter" , "25" ,
298+ "-v" ,
299+ # runtests.match_tests is not used (yet) for bisect_cmd -i arg
300+ ])
301+ cmd .extend (runtests .bisect_cmd_args ())
302+ cmd .append (test )
303+ print ("+" , shlex .join (cmd ), flush = True )
304+
305+ import subprocess
306+ proc = subprocess .run (cmd , timeout = runtests .timeout )
307+ exitcode = proc .returncode
308+ if exitcode :
309+ print (f"Bisect failed with exit code { exitcode } " )
310+ return False
311+
312+ return True
313+
314+ def run_bisect (self , runtests : RunTests ) -> None :
315+ tests , _ = self .results .prepare_rerun (clear = False )
316+
317+ for index , name in enumerate (tests , 1 ):
318+ if len (tests ) > 1 :
319+ progress = f"{ index } /{ len (tests )} "
320+ else :
321+ progress = ""
322+ if not self ._run_bisect (runtests , name , progress ):
323+ return
324+
276325 def display_result (self , runtests ):
277326 # If running the test suite for PGO then no one cares about results.
278327 if runtests .pgo :
@@ -461,7 +510,7 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
461510
462511 setup_process ()
463512
464- if self .hunt_refleak and not self .num_workers :
513+ if ( runtests .hunt_refleak is not None ) and ( not self .num_workers ) :
465514 # gh-109739: WindowsLoadTracker thread interfers with refleak check
466515 use_load_tracker = False
467516 else :
@@ -481,6 +530,9 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
481530
482531 if self .want_rerun and self .results .need_rerun ():
483532 self .rerun_failed_tests (runtests )
533+
534+ if self .want_bisect and self .results .need_rerun ():
535+ self .run_bisect (runtests )
484536 finally :
485537 if use_load_tracker :
486538 self .logger .stop_load_tracker ()
0 commit comments