@@ -261,6 +261,16 @@ def test_subinterpreter(self):
261261 self .assertTrue (interp .is_running ())
262262 self .assertFalse (interp .is_running ())
263263
264+ def test_finished (self ):
265+ r , w = os .pipe ()
266+ interp = interpreters .create ()
267+ interp .run (f"""if True:
268+ import os
269+ os.write({ w } , b'x')
270+ """ )
271+ self .assertFalse (interp .is_running ())
272+ self .assertEqual (os .read (r , 1 ), b'x' )
273+
264274 def test_from_subinterpreter (self ):
265275 interp = interpreters .create ()
266276 out = _run_output (interp , dedent (f"""
@@ -288,6 +298,31 @@ def test_bad_id(self):
288298 with self .assertRaises (ValueError ):
289299 interp .is_running ()
290300
301+ def test_with_only_background_threads (self ):
302+ r_interp , w_interp = os .pipe ()
303+ r_thread , w_thread = os .pipe ()
304+
305+ DONE = b'D'
306+ FINISHED = b'F'
307+
308+ interp = interpreters .create ()
309+ interp .run (f"""if True:
310+ import os
311+ import threading
312+
313+ def task():
314+ v = os.read({ r_thread } , 1)
315+ assert v == { DONE !r}
316+ os.write({ w_interp } , { FINISHED !r} )
317+ t = threading.Thread(target=task)
318+ t.start()
319+ """ )
320+ self .assertFalse (interp .is_running ())
321+
322+ os .write (w_thread , DONE )
323+ interp .run ('t.join()' )
324+ self .assertEqual (os .read (r_interp , 1 ), FINISHED )
325+
291326
292327class TestInterpreterClose (TestBase ):
293328
@@ -389,6 +424,37 @@ def test_still_running(self):
389424 interp .close ()
390425 self .assertTrue (interp .is_running ())
391426
427+ def test_subthreads_still_running (self ):
428+ r_interp , w_interp = os .pipe ()
429+ r_thread , w_thread = os .pipe ()
430+
431+ FINISHED = b'F'
432+
433+ interp = interpreters .create ()
434+ interp .run (f"""if True:
435+ import os
436+ import threading
437+ import time
438+
439+ done = False
440+
441+ def notify_fini():
442+ global done
443+ done = True
444+ t.join()
445+ threading._register_atexit(notify_fini)
446+
447+ def task():
448+ while not done:
449+ time.sleep(0.1)
450+ os.write({ w_interp } , { FINISHED !r} )
451+ t = threading.Thread(target=task)
452+ t.start()
453+ """ )
454+ interp .close ()
455+
456+ self .assertEqual (os .read (r_interp , 1 ), FINISHED )
457+
392458
393459class TestInterpreterRun (TestBase ):
394460
@@ -465,6 +531,37 @@ def test_bytes_for_script(self):
465531 with self .assertRaises (TypeError ):
466532 interp .run (b'print("spam")' )
467533
534+ def test_with_background_threads_still_running (self ):
535+ r_interp , w_interp = os .pipe ()
536+ r_thread , w_thread = os .pipe ()
537+
538+ RAN = b'R'
539+ DONE = b'D'
540+ FINISHED = b'F'
541+
542+ interp = interpreters .create ()
543+ interp .run (f"""if True:
544+ import os
545+ import threading
546+
547+ def task():
548+ v = os.read({ r_thread } , 1)
549+ assert v == { DONE !r}
550+ os.write({ w_interp } , { FINISHED !r} )
551+ t = threading.Thread(target=task)
552+ t.start()
553+ os.write({ w_interp } , { RAN !r} )
554+ """ )
555+ interp .run (f"""if True:
556+ os.write({ w_interp } , { RAN !r} )
557+ """ )
558+
559+ os .write (w_thread , DONE )
560+ interp .run ('t.join()' )
561+ self .assertEqual (os .read (r_interp , 1 ), RAN )
562+ self .assertEqual (os .read (r_interp , 1 ), RAN )
563+ self .assertEqual (os .read (r_interp , 1 ), FINISHED )
564+
468565 # test_xxsubinterpreters covers the remaining Interpreter.run() behavior.
469566
470567
0 commit comments