@@ -617,6 +617,52 @@ def test_BoundedSemaphore_limit(self):
617617 t .join ()
618618 self .assertRaises (ValueError , bs .release )
619619
620+ def test_locals_at_exit (self ):
621+ # Issue #19466: thread locals must not be deleted before destructors
622+ # are called
623+ rc , out , err = assert_python_ok ("-c" , """if 1:
624+ import threading
625+
626+ class Atexit:
627+ def __del__(self):
628+ print("thread_dict.atexit = %r" % thread_dict.atexit)
629+
630+ thread_dict = threading.local()
631+ thread_dict.atexit = "atexit"
632+
633+ atexit = Atexit()
634+ """ )
635+ self .assertEqual (out .rstrip (), b"thread_dict.atexit = 'atexit'" )
636+
637+ def test_warnings_at_exit (self ):
638+ # Issue #19466: try to call most destructors at Python shutdown before
639+ # destroying Python thread states
640+ filename = __file__
641+ rc , out , err = assert_python_ok ("-Wd" , "-c" , """if 1:
642+ import time
643+ import threading
644+
645+ def open_sleep():
646+ # a warning will be emitted when the open file will be
647+ # destroyed (without being explicitly closed) while the daemon
648+ # thread is destroyed
649+ fileobj = open(%a, 'rb')
650+ start_event.set()
651+ time.sleep(60.0)
652+
653+ start_event = threading.Event()
654+
655+ thread = threading.Thread(target=open_sleep)
656+ thread.daemon = True
657+ thread.start()
658+
659+ # wait until the thread started
660+ start_event.wait()
661+ """ % filename )
662+ self .assertRegex (err .rstrip (),
663+ b"^sys:1: ResourceWarning: unclosed file " )
664+
665+
620666class ThreadJoinOnShutdown (BaseTestCase ):
621667
622668 def _run_and_join (self , script ):
@@ -701,6 +747,10 @@ def test_4_daemon_threads(self):
701747 import sys
702748 import time
703749 import threading
750+ import warnings
751+
752+ # ignore "unclosed file ..." warnings
753+ warnings.filterwarnings('ignore', '', ResourceWarning)
704754
705755 thread_has_run = set()
706756
0 commit comments