46
46
47
47
@dataclasses .dataclass (slots = True )
48
48
class WorkerJob :
49
- test_name : str
49
+ runtests : RunTests
50
50
namespace : Namespace
51
- rerun : bool = False
52
51
match_tests : FilterTuple | None = None
53
52
54
53
@@ -70,6 +69,7 @@ def default(self, o: Any) -> dict[str, Any]:
70
69
def _decode_worker_job (d : dict [str , Any ]) -> WorkerJob | dict [str , Any ]:
71
70
if "__worker_job__" in d :
72
71
d .pop ('__worker_job__' )
72
+ d ['runtests' ] = RunTests (** d ['runtests' ])
73
73
return WorkerJob (** d )
74
74
if "__namespace__" in d :
75
75
d .pop ('__namespace__' )
@@ -78,17 +78,16 @@ def _decode_worker_job(d: dict[str, Any]) -> WorkerJob | dict[str, Any]:
78
78
return d
79
79
80
80
81
- def _parse_worker_args (worker_json : str ) -> tuple [Namespace , str ]:
82
- return json .loads (worker_json ,
83
- object_hook = _decode_worker_job )
81
+ def _parse_worker_json (worker_json : str ) -> tuple [Namespace , str ]:
82
+ return json .loads (worker_json , object_hook = _decode_worker_job )
84
83
85
84
86
- def run_test_in_subprocess (worker_job : WorkerJob ,
87
- output_file : TextIO ,
88
- tmp_dir : str | None = None ) -> subprocess .Popen :
85
+ def create_worker_process (worker_job : WorkerJob ,
86
+ output_file : TextIO ,
87
+ tmp_dir : str | None = None ) -> subprocess .Popen :
89
88
ns = worker_job .namespace
90
89
python = ns .python
91
- worker_args = json .dumps (worker_job , cls = _EncodeWorkerJob )
90
+ worker_json = json .dumps (worker_job , cls = _EncodeWorkerJob )
92
91
93
92
if python is not None :
94
93
executable = python
@@ -97,7 +96,7 @@ def run_test_in_subprocess(worker_job: WorkerJob,
97
96
cmd = [* executable , * support .args_from_interpreter_flags (),
98
97
'-u' , # Unbuffered stdout and stderr
99
98
'-m' , 'test.regrtest' ,
100
- '--worker-args ' , worker_args ]
99
+ '--worker-json ' , worker_json ]
101
100
102
101
env = dict (os .environ )
103
102
if tmp_dir is not None :
@@ -122,31 +121,32 @@ def run_test_in_subprocess(worker_job: WorkerJob,
122
121
return subprocess .Popen (cmd , ** kw )
123
122
124
123
125
- def run_tests_worker (worker_json : str ) -> NoReturn :
126
- worker_job = _parse_worker_args (worker_json )
124
+ def worker_process (worker_json : str ) -> NoReturn :
125
+ worker_job = _parse_worker_json (worker_json )
126
+ runtests = worker_job .runtests
127
127
ns = worker_job .namespace
128
- test_name = worker_job .test_name
129
- rerun = worker_job .rerun
130
- match_tests = worker_job .match_tests
128
+ test_name = runtests .tests [0 ]
129
+ match_tests : FilterTuple | None = worker_job .match_tests
131
130
132
131
setup_tests (ns )
133
132
134
- if rerun :
133
+ if runtests . rerun :
135
134
if match_tests :
136
135
matching = "matching: " + ", " .join (match_tests )
137
136
print (f"Re-running { test_name } in verbose mode ({ matching } )" , flush = True )
138
137
else :
139
138
print (f"Re-running { test_name } in verbose mode" , flush = True )
140
139
ns .verbose = True
141
140
142
- if match_tests is not None :
143
- ns .match_tests = match_tests
141
+ if match_tests is not None :
142
+ ns .match_tests = match_tests
144
143
145
144
result = runtest (ns , test_name )
146
145
print () # Force a newline (just in case)
147
146
148
147
# Serialize TestResult as dict in JSON
149
- print (json .dumps (result , cls = EncodeTestResult ), flush = True )
148
+ json .dump (result , sys .stdout , cls = EncodeTestResult )
149
+ sys .stdout .flush ()
150
150
sys .exit (0 )
151
151
152
152
@@ -173,7 +173,8 @@ def stop(self):
173
173
self .tests_iter = None
174
174
175
175
176
- class MultiprocessResult (NamedTuple ):
176
+ @dataclasses .dataclass (slots = True , frozen = True )
177
+ class MultiprocessResult :
177
178
result : TestResult
178
179
# bpo-45410: stderr is written into stdout to keep messages order
179
180
worker_stdout : str | None = None
@@ -198,7 +199,6 @@ def __init__(self, worker_id: int, runner: "MultiprocessTestRunner") -> None:
198
199
self .ns = runner .ns
199
200
self .timeout = runner .worker_timeout
200
201
self .regrtest = runner .regrtest
201
- self .rerun = runner .rerun
202
202
self .current_test_name = None
203
203
self .start_time = None
204
204
self ._popen = None
@@ -264,9 +264,8 @@ def mp_result_error(
264
264
265
265
def _run_process (self , worker_job , output_file : TextIO ,
266
266
tmp_dir : str | None = None ) -> int :
267
- self .current_test_name = worker_job .test_name
268
267
try :
269
- popen = run_test_in_subprocess (worker_job , output_file , tmp_dir )
268
+ popen = create_worker_process (worker_job , output_file , tmp_dir )
270
269
271
270
self ._killed = False
272
271
self ._popen = popen
@@ -316,6 +315,8 @@ def _run_process(self, worker_job, output_file: TextIO,
316
315
self .current_test_name = None
317
316
318
317
def _runtest (self , test_name : str ) -> MultiprocessResult :
318
+ self .current_test_name = test_name
319
+
319
320
if sys .platform == 'win32' :
320
321
# gh-95027: When stdout is not a TTY, Python uses the ANSI code
321
322
# page for the sys.stdout encoding. If the main process runs in a
@@ -324,15 +325,20 @@ def _runtest(self, test_name: str) -> MultiprocessResult:
324
325
else :
325
326
encoding = sys .stdout .encoding
326
327
327
- match_tests = self .runtests .get_match_tests (test_name )
328
+ tests = (test_name ,)
329
+ if self .runtests .rerun :
330
+ match_tests = self .runtests .get_match_tests (test_name )
331
+ else :
332
+ match_tests = None
333
+ worker_runtests = self .runtests .copy (tests = tests )
334
+ worker_job = WorkerJob (
335
+ worker_runtests ,
336
+ namespace = self .ns ,
337
+ match_tests = match_tests )
328
338
329
339
# gh-94026: Write stdout+stderr to a tempfile as workaround for
330
340
# non-blocking pipes on Emscripten with NodeJS.
331
341
with tempfile .TemporaryFile ('w+' , encoding = encoding ) as stdout_file :
332
- worker_job = WorkerJob (test_name ,
333
- namespace = self .ns ,
334
- rerun = self .rerun ,
335
- match_tests = match_tests )
336
342
# gh-93353: Check for leaked temporary files in the parent process,
337
343
# since the deletion of temporary files can happen late during
338
344
# Python finalization: too late for libregrtest.
0 commit comments