11import faulthandler
22import json
3- import os
3+ import os . path
44import queue
55import shlex
66import signal
1717from test .libregrtest .cmdline import Namespace
1818from test .libregrtest .main import Regrtest
1919from test .libregrtest .runtest import (
20- runtest , is_failed , TestResult , Interrupted , Timeout , ChildError , PROGRESS_MIN_TIME )
20+ runtest , is_failed , TestResult , Interrupted , Timeout , ChildError ,
21+ PROGRESS_MIN_TIME , Passed , EnvChanged )
2122from test .libregrtest .setup import setup_tests
2223from test .libregrtest .utils import format_duration , print_warning
2324
@@ -52,7 +53,7 @@ def parse_worker_args(worker_args) -> tuple[Namespace, str]:
5253 return (ns , test_name )
5354
5455
55- def run_test_in_subprocess (testname : str , ns : Namespace ) -> subprocess .Popen :
56+ def run_test_in_subprocess (testname : str , ns : Namespace , tmp_dir : str ) -> subprocess .Popen :
5657 ns_dict = vars (ns )
5758 worker_args = (ns_dict , testname )
5859 worker_args = json .dumps (worker_args )
@@ -66,10 +67,14 @@ def run_test_in_subprocess(testname: str, ns: Namespace) -> subprocess.Popen:
6667 '-m' , 'test.regrtest' ,
6768 '--worker-args' , worker_args ]
6869
70+ env = dict (os .environ )
71+ env ['TMPDIR' ] = tmp_dir
72+ env ['TEMPDIR' ] = tmp_dir
73+
6974 # Running the child from the same working directory as regrtest's original
7075 # invocation ensures that TEMPDIR for the child is the same when
7176 # sysconfig.is_python_build() is true. See issue 15300.
72- kw = {}
77+ kw = {'env' : env }
7378 if USE_PROCESS_GROUP :
7479 kw ['start_new_session' ] = True
7580 return subprocess .Popen (cmd ,
@@ -206,12 +211,12 @@ def mp_result_error(
206211 test_result .duration_sec = time .monotonic () - self .start_time
207212 return MultiprocessResult (test_result , stdout , err_msg )
208213
209- def _run_process (self , test_name : str ) -> tuple [int , str , str ]:
214+ def _run_process (self , test_name : str , tmp_dir : str ) -> tuple [int , str , str ]:
210215 self .start_time = time .monotonic ()
211216
212217 self .current_test_name = test_name
213218 try :
214- popen = run_test_in_subprocess (test_name , self .ns )
219+ popen = run_test_in_subprocess (test_name , self .ns , tmp_dir )
215220
216221 self ._killed = False
217222 self ._popen = popen
@@ -266,7 +271,17 @@ def _run_process(self, test_name: str) -> tuple[int, str, str]:
266271 self .current_test_name = None
267272
268273 def _runtest (self , test_name : str ) -> MultiprocessResult :
269- retcode , stdout = self ._run_process (test_name )
274+ # gh-93353: Check for leaked temporary files in the parent process,
275+ # since the deletion of temporary files can happen late during
276+ # Python finalization: too late for libregrtest.
277+ tmp_dir = os .getcwd () + '_tmpdir'
278+ tmp_dir = os .path .abspath (tmp_dir )
279+ try :
280+ os .mkdir (tmp_dir )
281+ retcode , stdout = self ._run_process (test_name , tmp_dir )
282+ finally :
283+ tmp_files = os .listdir (tmp_dir )
284+ os_helper .rmtree (tmp_dir )
270285
271286 if retcode is None :
272287 return self .mp_result_error (Timeout (test_name ), stdout )
@@ -289,6 +304,14 @@ def _runtest(self, test_name: str) -> MultiprocessResult:
289304 if err_msg is not None :
290305 return self .mp_result_error (ChildError (test_name ), stdout , err_msg )
291306
307+ if tmp_files :
308+ msg = (f'\n \n '
309+ f'Warning -- Test leaked temporary files ({ len (tmp_files )} ): '
310+ f'{ ", " .join (sorted (tmp_files ))} ' )
311+ stdout += msg
312+ if isinstance (result , Passed ):
313+ result = EnvChanged .from_passed (result )
314+
292315 return MultiprocessResult (result , stdout , err_msg )
293316
294317 def run (self ) -> None :
0 commit comments