1- """Run tests in isolated subprocesses (the test.support.isolation.isolated decorator).
1+ """Run tests in isolated subprocesses (the test.support.isolation.runInSubprocess decorator).
22
33A failure, error or skip that happens in the subprocess is replayed in the
44parent process so that the test runner records it. The original (subprocess)
1111import sys
1212import unittest
1313
14- # Mark this module's frames as belonging to the test machinery, so that
15- # unittest strips them from reported tracebacks (see TestResult._clean_tracebacks
16- # in Lib/unittest/result.py). Only the original subprocess traceback, attached
17- # as the cause, is then shown -- not the parent-side replay frames.
14+ # Let unittest strip this module's frames from tracebacks, so only the original
15+ # subprocess traceback (attached as the cause) is shown, not the replay frames.
1816__unittest = True
1917
2018# Environment variable set in the child process so that the decorated test
@@ -39,23 +37,23 @@ def _child_config():
3937 return {name : getattr (support , name ) for name in _PROPAGATED_CONFIG }
4038
4139def _apply_child_config ():
42- """Mirror the parent's test.support configuration in the subprocess .
40+ """Set up the child to run the test like a regrtest worker would .
4341
44- Called by subprocess_runner before loading the test, so that import-time
45- decorators (e.g. requires_resource) and runtime checks see the same -u/-M/-v
46- configuration as the parent process .
42+ Mirror the parent's -u/-M/-v config, then suppress the Windows CRT assertion
43+ dialogs that would otherwise block a debug build on a modal dialog and hang
44+ the parent.
4745 """
4846 import json
4947 import test .support as support
5048 data = os .environ .get (_CONFIG_ENV )
5149 if data :
5250 for name , value in json .loads (data ).items ():
5351 setattr (support , name , value )
52+ support .suppress_msvcrt_asserts (support .verbose >= 2 )
5453
55- # True while running inside the isolated subprocess spawned by @isolated().
56- # setUp()/tearDown() and the class- and module-level fixtures can test it to
57- # decide which code to run in the subprocess as opposed to the parent process.
58- running_isolated = bool (os .environ .get (_RUN_IN_SUBPROCESS_ENV ))
54+ # True inside the subprocess spawned by @runInSubprocess(). Fixtures can test
55+ # it to decide what to run in the subprocess as opposed to the parent process.
56+ runningInSubprocess = bool (os .environ .get (_RUN_IN_SUBPROCESS_ENV ))
5957
6058
6159class _RemoteTraceback (Exception ):
@@ -82,8 +80,8 @@ def _remote(detail):
8280
8381
8482def _check_subprocess_support ():
85- # isolated () always runs the test in a subprocess, so skip (in the parent)
86- # on platforms that do not support spawning one.
83+ # runInSubprocess () always runs the test in a subprocess, so skip (in the
84+ # parent) on platforms that do not support spawning one.
8785 import test .support as support
8886 if not support .has_subprocess_support :
8987 raise unittest .SkipTest ('requires subprocess support' )
@@ -127,9 +125,9 @@ def _replay_outcome(test, outcome):
127125 if kind == 'skipped' :
128126 test .skipTest (detail ) # the detail is the skip reason, not a traceback
129127 elif kind in ('failure' , 'expected_failure' ):
130- # An expected failure is replayed like a failure: the wrapper carries
131- # the @expectedFailure marker (copied by functools.wraps), so the parent
132- # records the raised exception as an expectedFailure, not a failure .
128+ # Replay an expected failure like a failure: the wrapper keeps the
129+ # @expectedFailure marker (via functools.wraps), so the parent records
130+ # the raised exception as an expectedFailure.
133131 exc = test .failureException ('test failed in the subprocess' )
134132 raise exc from _remote (detail )
135133 else : # 'error'
@@ -163,7 +161,7 @@ def _raise_fixture_outcome(outcome):
163161def _isolate_method (func ):
164162 @functools .wraps (func )
165163 def wrapper (self , / , * args , ** kwargs ):
166- if running_isolated :
164+ if runningInSubprocess :
167165 # Already running in the subprocess: run the real test.
168166 return func (self , * args , ** kwargs )
169167 _check_subprocess_support ()
@@ -182,17 +180,17 @@ def wrapper(self, /, *args, **kwargs):
182180
183181
184182def _isolate_class (cls ):
185- # Unwrap to the plain functions: the replacements below call them with the
186- # runtime cls, so a subclass of an isolated class runs the fixtures bound to
187- # itself (a bound classmethod would freeze the decoration-time class) .
183+ # Unwrap to the plain functions so the replacements can call them with the
184+ # runtime cls; a bound classmethod would freeze the decoration-time class
185+ # and a subclass would run the fixtures bound to the base class.
188186 orig_setUpClass = cls .setUpClass .__func__
189187 orig_tearDownClass = cls .tearDownClass .__func__
190188 orig_setUp = cls .setUp
191189 orig_tearDown = cls .tearDown
192190 orig_addDuration = getattr (cls , '_addDuration' , None )
193191
194192 def setUpClass (cls ):
195- if running_isolated :
193+ if runningInSubprocess :
196194 orig_setUpClass (cls )
197195 return
198196 _check_subprocess_support ()
@@ -215,26 +213,25 @@ def setUpClass(cls):
215213 cls ._isolated_durations = dict (payload .get ('durations' , ()))
216214
217215 def tearDownClass (cls ):
218- if running_isolated :
216+ if runningInSubprocess :
219217 orig_tearDownClass (cls )
220218 else :
221219 cls ._isolated_outcomes = None
222220 cls ._isolated_durations = None
223221
224222 def setUp (self ):
225223 # In the parent the real test does not run, so neither should setUp().
226- if running_isolated :
224+ if runningInSubprocess :
227225 orig_setUp (self )
228226
229227 def tearDown (self ):
230- if running_isolated :
228+ if runningInSubprocess :
231229 orig_tearDown (self )
232230
233231 def _addDuration (self , result , elapsed ):
234- # In the parent, report the per-test duration measured in the subprocess
235- # rather than the replay time (subprocess startup is paid once, in
236- # setUpClass).
237- if not running_isolated :
232+ # In the parent, report the subprocess timing rather than the (instant)
233+ # replay time; subprocess startup is paid once, in setUpClass.
234+ if not runningInSubprocess :
238235 durations = getattr (type (self ), '_isolated_durations' , None ) or {}
239236 elapsed = durations .get (self .id (), elapsed )
240237 orig_addDuration (self , result , elapsed )
@@ -253,15 +250,15 @@ def replay(self):
253250 method = getattr (cls , name )
254251 @functools .wraps (method )
255252 def wrapper (self , / , * args , __func = method , ** kwargs ):
256- if running_isolated :
253+ if runningInSubprocess :
257254 return __func (self , * args , ** kwargs )
258255 replay (self )
259256 setattr (cls , name , wrapper )
260257 return cls
261258
262259
263- def isolated ():
264- """Decorator to run a test method or class in isolation from the rest .
260+ def runInSubprocess ():
261+ """Decorator to run a test method or class in a fresh subprocess .
265262
266263 The decorated test runs in a separate, fresh Python process, so it does not
267264 share global or interpreter state with the rest of the test run. When a
@@ -274,7 +271,7 @@ def isolated():
274271 individual subtests (:meth:`~unittest.TestCase.subTest`) that fail or are
275272 skipped are reported individually. The original subprocess traceback is
276273 shown as the cause of a reported failure or error. Use
277- :data:`running_isolated ` in fixtures to choose what to run in the subprocess.
274+ :data:`runningInSubprocess ` in fixtures to choose what to run in the subprocess.
278275
279276 The test is skipped on platforms without subprocess support, since it must
280277 spawn one.
0 commit comments