Skip to content

Commit 5b7303e

Browse files
authored
gh-109162: Refactor Regrtest.main() (#109163)
* main() now calls _parse_args() and pass 'ns' to Regrtest constructor. Remove kwargs argument from Regrtest.main(). * _parse_args() checks ns.huntrleaks. * set_temp_dir() is now responsible to call expanduser(). * Regrtest.main() sets self.tests earlier. * Add TestTuple and TestList types. * Rename MatchTests to FilterTuple and rename MatchTestsDict to FilterTestDict. * TestResult.get_rerun_match_tests() return type is now FilterTuple: return a tuple instead of a list. RunTests.tests type becomes TestTuple.
1 parent bcb2ab5 commit 5b7303e

File tree

4 files changed

+64
-62
lines changed

4 files changed

+64
-62
lines changed

Lib/test/libregrtest/cmdline.py

+9
Original file line numberDiff line numberDiff line change
@@ -448,4 +448,13 @@ def _parse_args(args, **kwargs):
448448
# --forever implies --failfast
449449
ns.failfast = True
450450

451+
if ns.huntrleaks:
452+
warmup, repetitions, _ = ns.huntrleaks
453+
if warmup < 1 or repetitions < 1:
454+
msg = ("Invalid values for the --huntrleaks/-R parameters. The "
455+
"number of warmups and repetitions must be at least 1 "
456+
"each (1:1).")
457+
print(msg, file=sys.stderr, flush=True)
458+
sys.exit(2)
459+
451460
return ns

Lib/test/libregrtest/main.py

+37-50
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
import tempfile
1010
import time
1111
import unittest
12-
from test.libregrtest.cmdline import _parse_args
12+
from test.libregrtest.cmdline import _parse_args, Namespace
1313
from test.libregrtest.runtest import (
1414
findtests, split_test_packages, runtest, abs_module_name,
15-
PROGRESS_MIN_TIME, State, MatchTestsDict, RunTests)
15+
PROGRESS_MIN_TIME, State, FilterDict, RunTests, TestResult, TestList)
1616
from test.libregrtest.setup import setup_tests
1717
from test.libregrtest.pgo import setup_pgo_tests
1818
from test.libregrtest.utils import (strip_py_suffix, count, format_duration,
@@ -58,24 +58,24 @@ class Regrtest:
5858
directly to set the values that would normally be set by flags
5959
on the command line.
6060
"""
61-
def __init__(self):
61+
def __init__(self, ns: Namespace):
6262
# Namespace of command line options
63-
self.ns = None
63+
self.ns: Namespace = ns
6464

6565
# tests
6666
self.tests = []
6767
self.selected = []
6868
self.all_runtests: list[RunTests] = []
6969

7070
# test results
71-
self.good: list[str] = []
72-
self.bad: list[str] = []
73-
self.rerun_bad: list[str] = []
74-
self.skipped: list[str] = []
75-
self.resource_denied: list[str] = []
76-
self.environment_changed: list[str] = []
77-
self.run_no_tests: list[str] = []
78-
self.rerun: list[str] = []
71+
self.good: TestList = []
72+
self.bad: TestList = []
73+
self.rerun_bad: TestList = []
74+
self.skipped: TestList = []
75+
self.resource_denied: TestList = []
76+
self.environment_changed: TestList = []
77+
self.run_no_tests: TestList = []
78+
self.rerun: TestList = []
7979

8080
self.need_rerun: list[TestResult] = []
8181
self.first_state: str | None = None
@@ -184,29 +184,7 @@ def display_progress(self, test_index, text):
184184
line = f"{line}/{fails}"
185185
self.log(f"[{line}] {text}")
186186

187-
def parse_args(self, kwargs):
188-
ns = _parse_args(sys.argv[1:], **kwargs)
189-
190-
if ns.xmlpath:
191-
support.junit_xml_list = self.testsuite_xml = []
192-
193-
strip_py_suffix(ns.args)
194-
195-
if ns.huntrleaks:
196-
warmup, repetitions, _ = ns.huntrleaks
197-
if warmup < 1 or repetitions < 1:
198-
msg = ("Invalid values for the --huntrleaks/-R parameters. The "
199-
"number of warmups and repetitions must be at least 1 "
200-
"each (1:1).")
201-
print(msg, file=sys.stderr, flush=True)
202-
sys.exit(2)
203-
204-
if ns.tempdir:
205-
ns.tempdir = os.path.expanduser(ns.tempdir)
206-
207-
self.ns = ns
208-
209-
def find_tests(self, tests):
187+
def find_tests(self):
210188
ns = self.ns
211189
single = ns.single
212190
fromfile = ns.fromfile
@@ -216,8 +194,6 @@ def find_tests(self, tests):
216194
starting_test = ns.start
217195
randomize = ns.randomize
218196

219-
self.tests = tests
220-
221197
if single:
222198
self.next_single_filename = os.path.join(self.tmp_dir, 'pynexttest')
223199
try:
@@ -321,7 +297,7 @@ def list_cases(self):
321297
print(count(len(skipped), "test"), "skipped:", file=stderr)
322298
printlist(skipped, file=stderr)
323299

324-
def get_rerun_match(self, rerun_list) -> MatchTestsDict:
300+
def get_rerun_match(self, rerun_list) -> FilterDict:
325301
rerun_match_tests = {}
326302
for result in rerun_list:
327303
match_tests = result.get_rerun_match_tests()
@@ -352,7 +328,7 @@ def _rerun_failed_tests(self, need_rerun):
352328

353329
# Re-run failed tests
354330
self.log(f"Re-running {len(tests)} failed tests in verbose mode in subprocesses")
355-
runtests = RunTests(tests, match_tests=match_tests, rerun=True)
331+
runtests = RunTests(tuple(tests), match_tests=match_tests, rerun=True)
356332
self.all_runtests.append(runtests)
357333
self._run_tests_mp(runtests)
358334

@@ -624,7 +600,7 @@ def run_tests(self):
624600

625601
tests = self.selected
626602
self.set_tests(tests)
627-
runtests = RunTests(tests, forever=self.ns.forever)
603+
runtests = RunTests(tuple(tests), forever=self.ns.forever)
628604
self.all_runtests.append(runtests)
629605
if self.ns.use_mp:
630606
self._run_tests_mp(runtests)
@@ -737,8 +713,12 @@ def fix_umask(self):
737713
os.umask(old_mask)
738714

739715
def set_temp_dir(self):
740-
if self.ns.tempdir:
741-
self.tmp_dir = self.ns.tempdir
716+
ns = self.ns
717+
if ns.tempdir:
718+
ns.tempdir = os.path.expanduser(ns.tempdir)
719+
720+
if ns.tempdir:
721+
self.tmp_dir = ns.tempdir
742722

743723
if not self.tmp_dir:
744724
# When tests are run from the Python build directory, it is best practice
@@ -795,14 +775,20 @@ def cleanup(self):
795775
print("Remove file: %s" % name)
796776
os_helper.unlink(name)
797777

798-
def main(self, tests=None, **kwargs):
799-
self.parse_args(kwargs)
778+
def main(self, tests: TestList | None = None):
779+
ns = self.ns
780+
self.tests = tests
781+
782+
if ns.xmlpath:
783+
support.junit_xml_list = self.testsuite_xml = []
784+
785+
strip_py_suffix(ns.args)
800786

801787
self.set_temp_dir()
802788

803789
self.fix_umask()
804790

805-
if self.ns.cleanup:
791+
if ns.cleanup:
806792
self.cleanup()
807793
sys.exit(0)
808794

@@ -817,9 +803,9 @@ def main(self, tests=None, **kwargs):
817803
# When using multiprocessing, worker processes will use test_cwd
818804
# as their parent temporary directory. So when the main process
819805
# exit, it removes also subdirectories of worker processes.
820-
self.ns.tempdir = test_cwd
806+
ns.tempdir = test_cwd
821807

822-
self._main(tests, kwargs)
808+
self._main()
823809
except SystemExit as exc:
824810
# bpo-38203: Python can hang at exit in Py_Finalize(), especially
825811
# on threading._shutdown() call: put a timeout
@@ -862,7 +848,7 @@ def action_run_tests(self):
862848
self.display_summary()
863849
self.finalize()
864850

865-
def _main(self, tests, kwargs):
851+
def _main(self):
866852
if self.is_worker():
867853
from test.libregrtest.runtest_mp import run_tests_worker
868854
run_tests_worker(self.ns.worker_args)
@@ -872,7 +858,7 @@ def _main(self, tests, kwargs):
872858
input("Press any key to continue...")
873859

874860
setup_tests(self.ns)
875-
self.find_tests(tests)
861+
self.find_tests()
876862

877863
exitcode = 0
878864
if self.ns.list_tests:
@@ -888,4 +874,5 @@ def _main(self, tests, kwargs):
888874

889875
def main(tests=None, **kwargs):
890876
"""Run the Python suite."""
891-
Regrtest().main(tests=tests, **kwargs)
877+
ns = _parse_args(sys.argv[1:], **kwargs)
878+
Regrtest(ns).main(tests=tests)

Lib/test/libregrtest/runtest.py

+16-10
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@
1919
from test.libregrtest.utils import clear_caches, format_duration, print_warning
2020

2121

22-
MatchTests = list[str]
23-
MatchTestsDict = dict[str, MatchTests]
22+
TestTuple = list[str]
23+
TestList = list[str]
24+
25+
# --match and --ignore options: list of patterns
26+
# ('*' joker character can be used)
27+
FilterTuple = tuple[str, ...]
28+
FilterDict = dict[str, FilterTuple]
2429

2530

2631
# Avoid enum.Enum to reduce the number of imports when tests are run
@@ -174,7 +179,7 @@ def must_stop(self, fail_fast: bool, fail_env_changed: bool) -> bool:
174179
return True
175180
return False
176181

177-
def get_rerun_match_tests(self):
182+
def get_rerun_match_tests(self) -> FilterTuple | None:
178183
match_tests = []
179184

180185
errors = self.errors or []
@@ -195,29 +200,30 @@ def get_rerun_match_tests(self):
195200
return None
196201
match_tests.append(match_name)
197202

198-
return match_tests
203+
if not match_tests:
204+
return None
205+
return tuple(match_tests)
199206

200207

201208
@dataclasses.dataclass(slots=True, frozen=True)
202209
class RunTests:
203-
tests: list[str]
204-
match_tests: MatchTestsDict | None = None
210+
tests: TestTuple
211+
match_tests: FilterDict | None = None
205212
rerun: bool = False
206213
forever: bool = False
207214

208-
def get_match_tests(self, test_name) -> MatchTests | None:
215+
def get_match_tests(self, test_name) -> FilterTuple | None:
209216
if self.match_tests is not None:
210217
return self.match_tests.get(test_name, None)
211218
else:
212219
return None
213220

214221
def iter_tests(self):
215-
tests = tuple(self.tests)
216222
if self.forever:
217223
while True:
218-
yield from tests
224+
yield from self.tests
219225
else:
220-
yield from tests
226+
yield from self.tests
221227

222228

223229
# Minimum duration of a test to display its duration or to mention that

Lib/test/libregrtest/runtest_mp.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from test.libregrtest.main import Regrtest
2121
from test.libregrtest.runtest import (
2222
runtest, TestResult, State, PROGRESS_MIN_TIME,
23-
MatchTests, RunTests)
23+
FilterTuple, RunTests)
2424
from test.libregrtest.setup import setup_tests
2525
from test.libregrtest.utils import format_duration, print_warning
2626

@@ -49,7 +49,7 @@ class WorkerJob:
4949
test_name: str
5050
namespace: Namespace
5151
rerun: bool = False
52-
match_tests: MatchTests | None = None
52+
match_tests: FilterTuple | None = None
5353

5454

5555
class _EncodeWorkerJob(json.JSONEncoder):

0 commit comments

Comments
 (0)