From 5d6f6c270e52322e413d0b55d9896da45d14f443 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 3 Sep 2026 20:18:35 -0400 Subject: [PATCH] gh-156896: Stop scrubbing tkinker submodules upon idlelib.run import Instead, invoke the scrubbing within run.main, which is called after the import when starting the IDLE user process. To manually test that scrub_tkinter_submodules is called when proper, start (or restart) Shell, enter `import tkinter; dir(tkinter), and check that that font, messagebox, ttk, and the dialog modules are missing. It is obvious from the code that the function is not otherwise called. To test anyway, continue with `import tkinter.ttk; import idlelib.run; tkinter.ttk` and check for proper output. --- Lib/idlelib/run.py | 17 +++++++++-------- ...26-09-03-17-16-26.gh-issue-156896.XTQJlq.rst | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/IDLE/2026-09-03-17-16-26.gh-issue-156896.XTQJlq.rst diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index 802f0248e4e4594..2725043b4ed9253 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -1,6 +1,6 @@ """ idlelib.run -Simplified, pyshell.ModifiedInterpreter spawns a subprocess with +Simplified: pyshell.ModifiedInterpreter spawns a subprocess with f'''{sys.executable} -c "__import__('idlelib.run').run.main()"''' '.run' is needed because __import__ returns idlelib, not idlelib.run. """ @@ -26,11 +26,12 @@ from idlelib import rpc # multiple objects from idlelib import stackviewer # StackTreeItem from idlelib import util # fix_scaling -import __main__ +import __main__ # self.locals in Executive.__init__. import tkinter # Use tcl and, if startup fails, messagebox. -if not hasattr(sys.modules['idlelib.run'], 'firstrun'): - # Undo modifications of tkinter by idlelib imports; see bpo-25507. + +def scrub_tkinter_submodules(): # Call in main when starting user process. + # Undo modifications of tkinter by idlelib imports; see gh-69693. # Which of these submodules got imported (and thus added as a tkinter # attribute) depends on what idlelib pulled in, so tolerate missing # ones rather than assuming a fixed set; see gh-59396. @@ -42,8 +43,6 @@ del sys.modules['tkinter.' + mod] except (AttributeError, KeyError): pass - # Avoid AttributeError if run again; see bpo-37038. - sys.modules['idlelib.run'].firstrun = False LOCALHOST = '127.0.0.1' @@ -139,6 +138,9 @@ def main(del_exitfunc=False): register and unregister themselves. """ + + scrub_tkinter_submodules() + global exit_now global quitting global no_exitfunc @@ -705,8 +707,7 @@ def stackviewer(self, flist_oid=None): item = stackviewer.StackTreeItem(exc, flist) return debugobj_r.remote_object_tree_item(item) - -if __name__ == '__main__': +if __name__ == '__main__': # __name__ is 'idlelib.run' in user subprocess. from unittest import main main('idlelib.idle_test.test_run', verbosity=2) diff --git a/Misc/NEWS.d/next/IDLE/2026-09-03-17-16-26.gh-issue-156896.XTQJlq.rst b/Misc/NEWS.d/next/IDLE/2026-09-03-17-16-26.gh-issue-156896.XTQJlq.rst new file mode 100644 index 000000000000000..fd759cdbe92c2ff --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2026-09-03-17-16-26.gh-issue-156896.XTQJlq.rst @@ -0,0 +1 @@ +Stop deleting tkinter submodules when idlelib.run is imported.