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]:
5253return (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 :
5657ns_dict = vars (ns )
5758worker_args = (ns_dict ,testname )
5859worker_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 }
7378if USE_PROCESS_GROUP :
7479kw ['start_new_session' ]= True
7580return subprocess .Popen (cmd ,
@@ -206,12 +211,12 @@ def mp_result_error(
206211test_result .duration_sec = time .monotonic ()- self .start_time
207212return 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 ]:
210215self .start_time = time .monotonic ()
211216
212217self .current_test_name = test_name
213218try :
214- popen = run_test_in_subprocess (test_name ,self .ns )
219+ popen = run_test_in_subprocess (test_name ,self .ns , tmp_dir )
215220
216221self ._killed = False
217222self ._popen = popen
@@ -266,7 +271,17 @@ def _run_process(self, test_name: str) -> tuple[int, str, str]:
266271self .current_test_name = None
267272
268273def _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
271286if retcode is None :
272287return self .mp_result_error (Timeout (test_name ),stdout )
@@ -289,6 +304,14 @@ def _runtest(self, test_name: str) -> MultiprocessResult:
289304if err_msg is not None :
290305return 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+
292315return MultiprocessResult (result ,stdout ,err_msg )
293316
294317def run (self )-> None :