mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	gh-119727: Add --single-process option to regrtest (#119728)
(cherry picked from commit 4e8aa32245)
Co-authored-by: Victor Stinner <vstinner@python.org>
			
			
This commit is contained in:
		
							parent
							
								
									3822d7cd28
								
							
						
					
					
						commit
						350313e731
					
				
					 4 changed files with 43 additions and 9 deletions
				
			
		|  | @ -174,6 +174,7 @@ def __init__(self, **kwargs) -> None: | ||||||
|         self.tempdir = None |         self.tempdir = None | ||||||
|         self._add_python_opts = True |         self._add_python_opts = True | ||||||
|         self.xmlpath = None |         self.xmlpath = None | ||||||
|  |         self.single_process = False | ||||||
| 
 | 
 | ||||||
|         super().__init__(**kwargs) |         super().__init__(**kwargs) | ||||||
| 
 | 
 | ||||||
|  | @ -307,6 +308,12 @@ def _create_parser(): | ||||||
|     group.add_argument('-j', '--multiprocess', metavar='PROCESSES', |     group.add_argument('-j', '--multiprocess', metavar='PROCESSES', | ||||||
|                        dest='use_mp', type=int, |                        dest='use_mp', type=int, | ||||||
|                        help='run PROCESSES processes at once') |                        help='run PROCESSES processes at once') | ||||||
|  |     group.add_argument('--single-process', action='store_true', | ||||||
|  |                        dest='single_process', | ||||||
|  |                        help='always run all tests sequentially in ' | ||||||
|  |                             'a single process, ignore -jN option, ' | ||||||
|  |                             'and failed tests are also rerun sequentially ' | ||||||
|  |                             'in the same process') | ||||||
|     group.add_argument('-T', '--coverage', action='store_true', |     group.add_argument('-T', '--coverage', action='store_true', | ||||||
|                        dest='trace', |                        dest='trace', | ||||||
|                        help='turn on code coverage tracing using the trace ' |                        help='turn on code coverage tracing using the trace ' | ||||||
|  | @ -433,6 +440,10 @@ def _parse_args(args, **kwargs): | ||||||
|     else: |     else: | ||||||
|         ns._add_python_opts = False |         ns._add_python_opts = False | ||||||
| 
 | 
 | ||||||
|  |     # --singleprocess overrides -jN option | ||||||
|  |     if ns.single_process: | ||||||
|  |         ns.use_mp = None | ||||||
|  | 
 | ||||||
|     # When both --slow-ci and --fast-ci options are present, |     # When both --slow-ci and --fast-ci options are present, | ||||||
|     # --slow-ci has the priority |     # --slow-ci has the priority | ||||||
|     if ns.slow_ci: |     if ns.slow_ci: | ||||||
|  |  | ||||||
|  | @ -89,12 +89,13 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): | ||||||
|         self.cmdline_args: TestList = ns.args |         self.cmdline_args: TestList = ns.args | ||||||
| 
 | 
 | ||||||
|         # Workers |         # Workers | ||||||
|         if ns.use_mp is None: |         self.single_process: bool = ns.single_process | ||||||
|             num_workers = 0  # run sequentially |         if self.single_process or ns.use_mp is None: | ||||||
|  |             num_workers = 0   # run sequentially in a single process | ||||||
|         elif ns.use_mp <= 0: |         elif ns.use_mp <= 0: | ||||||
|             num_workers = -1  # use the number of CPUs |             num_workers = -1  # run in parallel, use the number of CPUs | ||||||
|         else: |         else: | ||||||
|             num_workers = ns.use_mp |             num_workers = ns.use_mp  # run in parallel | ||||||
|         self.num_workers: int = num_workers |         self.num_workers: int = num_workers | ||||||
|         self.worker_json: StrJSON | None = ns.worker_json |         self.worker_json: StrJSON | None = ns.worker_json | ||||||
| 
 | 
 | ||||||
|  | @ -236,7 +237,7 @@ def list_tests(tests: TestTuple): | ||||||
| 
 | 
 | ||||||
|     def _rerun_failed_tests(self, runtests: RunTests): |     def _rerun_failed_tests(self, runtests: RunTests): | ||||||
|         # Configure the runner to re-run tests |         # Configure the runner to re-run tests | ||||||
|         if self.num_workers == 0: |         if self.num_workers == 0 and not self.single_process: | ||||||
|             # Always run tests in fresh processes to have more deterministic |             # Always run tests in fresh processes to have more deterministic | ||||||
|             # initial state. Don't re-run tests in parallel but limit to a |             # initial state. Don't re-run tests in parallel but limit to a | ||||||
|             # single worker process to have side effects (on the system load |             # single worker process to have side effects (on the system load | ||||||
|  | @ -246,7 +247,6 @@ def _rerun_failed_tests(self, runtests: RunTests): | ||||||
|         tests, match_tests_dict = self.results.prepare_rerun() |         tests, match_tests_dict = self.results.prepare_rerun() | ||||||
| 
 | 
 | ||||||
|         # Re-run failed tests |         # Re-run failed tests | ||||||
|         self.log(f"Re-running {len(tests)} failed tests in verbose mode in subprocesses") |  | ||||||
|         runtests = runtests.copy( |         runtests = runtests.copy( | ||||||
|             tests=tests, |             tests=tests, | ||||||
|             rerun=True, |             rerun=True, | ||||||
|  | @ -256,7 +256,15 @@ def _rerun_failed_tests(self, runtests: RunTests): | ||||||
|             match_tests_dict=match_tests_dict, |             match_tests_dict=match_tests_dict, | ||||||
|             output_on_failure=False) |             output_on_failure=False) | ||||||
|         self.logger.set_tests(runtests) |         self.logger.set_tests(runtests) | ||||||
|         self._run_tests_mp(runtests, self.num_workers) | 
 | ||||||
|  |         msg = f"Re-running {len(tests)} failed tests in verbose mode" | ||||||
|  |         if not self.single_process: | ||||||
|  |             msg = f"{msg} in subprocesses" | ||||||
|  |             self.log(msg) | ||||||
|  |             self._run_tests_mp(runtests, self.num_workers) | ||||||
|  |         else: | ||||||
|  |             self.log(msg) | ||||||
|  |             self.run_tests_sequentially(runtests) | ||||||
|         return runtests |         return runtests | ||||||
| 
 | 
 | ||||||
|     def rerun_failed_tests(self, runtests: RunTests): |     def rerun_failed_tests(self, runtests: RunTests): | ||||||
|  | @ -371,7 +379,7 @@ def run_tests_sequentially(self, runtests) -> None: | ||||||
|             tests = count(jobs, 'test') |             tests = count(jobs, 'test') | ||||||
|         else: |         else: | ||||||
|             tests = 'tests' |             tests = 'tests' | ||||||
|         msg = f"Run {tests} sequentially" |         msg = f"Run {tests} sequentially in a single process" | ||||||
|         if runtests.timeout: |         if runtests.timeout: | ||||||
|             msg += " (timeout: %s)" % format_duration(runtests.timeout) |             msg += " (timeout: %s)" % format_duration(runtests.timeout) | ||||||
|         self.log(msg) |         self.log(msg) | ||||||
|  | @ -599,7 +607,7 @@ def _add_cross_compile_opts(self, regrtest_opts): | ||||||
|             keep_environ = True |             keep_environ = True | ||||||
| 
 | 
 | ||||||
|         if cross_compile and hostrunner: |         if cross_compile and hostrunner: | ||||||
|             if self.num_workers == 0: |             if self.num_workers == 0 and not self.single_process: | ||||||
|                 # For now use only two cores for cross-compiled builds; |                 # For now use only two cores for cross-compiled builds; | ||||||
|                 # hostrunner can be expensive. |                 # hostrunner can be expensive. | ||||||
|                 regrtest_opts.extend(['-j', '2']) |                 regrtest_opts.extend(['-j', '2']) | ||||||
|  |  | ||||||
|  | @ -475,6 +475,19 @@ def test_verbose3_huntrleaks(self): | ||||||
|         self.assertEqual(regrtest.hunt_refleak.runs, 10) |         self.assertEqual(regrtest.hunt_refleak.runs, 10) | ||||||
|         self.assertFalse(regrtest.output_on_failure) |         self.assertFalse(regrtest.output_on_failure) | ||||||
| 
 | 
 | ||||||
|  |     def test_single_process(self): | ||||||
|  |         args = ['-j2', '--single-process'] | ||||||
|  |         with support.captured_stderr(): | ||||||
|  |             regrtest = self.create_regrtest(args) | ||||||
|  |         self.assertEqual(regrtest.num_workers, 0) | ||||||
|  |         self.assertTrue(regrtest.single_process) | ||||||
|  | 
 | ||||||
|  |         args = ['--fast-ci', '--single-process'] | ||||||
|  |         with support.captured_stderr(): | ||||||
|  |             regrtest = self.create_regrtest(args) | ||||||
|  |         self.assertEqual(regrtest.num_workers, 0) | ||||||
|  |         self.assertTrue(regrtest.single_process) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| @dataclasses.dataclass(slots=True) | @dataclasses.dataclass(slots=True) | ||||||
| class Rerun: | class Rerun: | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Add ``--single-process`` command line option to Python test runner (regrtest). | ||||||
|  | Patch by Victor Stinner. | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Malcolm Smith
						Malcolm Smith