|
1 | 1 | import functools |
2 | 2 | import platform |
3 | 3 | import sys |
| 4 | +import textwrap |
4 | 5 | import unittest |
5 | 6 | import weakref |
6 | 7 | import tkinter |
7 | 8 | from tkinter import TclError |
8 | 9 | import enum |
9 | 10 | from test import support |
10 | 11 | from test.support import os_helper |
| 12 | +from test.support.script_helper import assert_python_ok |
11 | 13 | from test.test_tkinter.support import setUpModule # noqa: F401 |
12 | 14 | from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest, |
13 | 15 | requires_tk, get_tk_patchlevel, |
@@ -52,6 +54,33 @@ class Button2(tkinter.Button): |
52 | 54 | b4 = Button2(f2) |
53 | 55 | self.assertEqual(len({str(b), str(b2), str(b3), str(b4)}), 4) |
54 | 56 |
|
| 57 | + def test_dealloc_in_wrong_thread(self): |
| 58 | + # gh-83274: deallocating the interpreter in the wrong thread must not |
| 59 | + # crash. |
| 60 | + script = textwrap.dedent(""" |
| 61 | + import threading |
| 62 | + import tkinter |
| 63 | + root = tkinter.Tk() |
| 64 | + root.destroy() |
| 65 | + # Let another thread drop the last reference. |
| 66 | + ready = threading.Event() |
| 67 | + t = threading.Thread(target=lambda obj: ready.wait(), args=(root,)) |
| 68 | + t.start() |
| 69 | + del root |
| 70 | + ready.set() |
| 71 | + t.join() |
| 72 | + print('ok') |
| 73 | + """) |
| 74 | + rc, out, err = assert_python_ok('-c', script) |
| 75 | + self.assertEqual(out.strip(), b'ok') |
| 76 | + if not support.Py_GIL_DISABLED: |
| 77 | + # On the free-threaded build the interpreter may instead be |
| 78 | + # deallocated in its own thread (deferred reference counting), so |
| 79 | + # the warning is not necessarily emitted. The crucial guarantee -- |
| 80 | + # no crash -- is already checked by assert_python_ok() above. |
| 81 | + self.assertIn(b'RuntimeWarning', err) |
| 82 | + self.assertIn(b'gh-83274', err) |
| 83 | + |
55 | 84 | @requires_tk(8, 6, 6) |
56 | 85 | def test_tk_busy(self): |
57 | 86 | root = self.root |
|
0 commit comments