From f0e2f9b2b7c1bb1e19ed30cfbd8393859b8b6bb4 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 11 Apr 2026 08:55:54 -0500 Subject: [PATCH 01/15] Lazy import modules in tkinter __init__ --- Lib/tkinter/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index ba8365f56c37a7..0c92fa1eb1f49a 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -30,15 +30,15 @@ tk.mainloop() """ -import collections -import enum +lazy import collections +lazy import enum import sys -import types +lazy import types import _tkinter # If this fails your Python may not be configured for Tk TclError = _tkinter.TclError from tkinter.constants import * -import re +lazy import re wantobjects = 1 _debug = False # set to True to print executed Tcl/Tk commands From 4bcb4911a80c01e8cdb6faafa48cc9bcae916e91 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 11 Apr 2026 09:00:21 -0500 Subject: [PATCH 02/15] Add _get_magic_re and _get_space_re functions. --- Lib/tkinter/__init__.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 0c92fa1eb1f49a..a7ea453e407817 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -50,10 +50,20 @@ WRITABLE = _tkinter.WRITABLE EXCEPTION = _tkinter.EXCEPTION - -_magic_re = re.compile(r'([\\{}])') -_space_re = re.compile(r'([\s])', re.ASCII) - +_magic_re = None +_space_re = None + +def _get_magic_re(): + global _magic_re + if _magic_re is None: + _magic_re = re.compile(r'([\\{}])') + return _magic_re + +def _get_space_re(): + global _space_re + if _space_re is None: + _space_re = re.compile(r'([\s])', re.ASCII) + return _space_re def _join(value): """Internal function.""" From 995d6a1e736d7201d2741ad7fd329cad32293cc8 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 11 Apr 2026 09:03:12 -0500 Subject: [PATCH 03/15] Update usage in _stringify --- Lib/tkinter/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index a7ea453e407817..9da383b54a4402 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -75,7 +75,7 @@ def _stringify(value): if isinstance(value, (list, tuple)): if len(value) == 1: value = _stringify(value[0]) - if _magic_re.search(value): + if _get_magic_re().search(value): value = '{%s}' % value else: value = '{%s}' % _join(value) @@ -86,14 +86,14 @@ def _stringify(value): value = str(value) if not value: value = '{}' - elif _magic_re.search(value): + elif _get_magic_re().search(value): # add '\' before special characters and spaces - value = _magic_re.sub(r'\\\1', value) + value = _get_magic_re().sub(r'\\\1', value) value = value.replace('\n', r'\n') - value = _space_re.sub(r'\\\1', value) + value = _get_space_re().sub(r'\\\1', value) if value[0] == '"': value = '\\' + value - elif value[0] == '"' or _space_re.search(value): + elif value[0] == '"' or _get_space_re().search(value): value = '{%s}' % value return value From 44567a4dd02b8320d1202401a7066db139be8d4d Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 11 Apr 2026 09:06:16 -0500 Subject: [PATCH 04/15] Add docstring to new functions --- Lib/tkinter/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 9da383b54a4402..3ae8ce2c33cfec 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -54,12 +54,14 @@ _space_re = None def _get_magic_re(): + """Internal function.""" global _magic_re if _magic_re is None: _magic_re = re.compile(r'([\\{}])') return _magic_re def _get_space_re(): + """Internal function.""" global _space_re if _space_re is None: _space_re = re.compile(r'([\s])', re.ASCII) From 0dfab7876fd3b651801d1c312a128b3b70c6904b Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 11 Apr 2026 09:10:30 -0500 Subject: [PATCH 05/15] Format --- Lib/tkinter/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 3ae8ce2c33cfec..75bc42d092e073 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -29,16 +29,16 @@ button.pack(side=BOTTOM) tk.mainloop() """ +import _tkinter # If this fails your Python may not be configured for Tk +from tkinter.constants import * lazy import collections lazy import enum -import sys +lazy import sys lazy import types +lazy import re -import _tkinter # If this fails your Python may not be configured for Tk TclError = _tkinter.TclError -from tkinter.constants import * -lazy import re wantobjects = 1 _debug = False # set to True to print executed Tcl/Tk commands @@ -71,7 +71,6 @@ def _join(value): """Internal function.""" return ' '.join(map(_stringify, value)) - def _stringify(value): """Internal function.""" if isinstance(value, (list, tuple)): From ce94f9ea6af039ad648380395061f97d447eedb9 Mon Sep 17 00:00:00 2001 From: sharktide Date: Sat, 11 Apr 2026 18:49:57 -0500 Subject: [PATCH 06/15] Add NEWS entry --- .../next/Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst diff --git a/Misc/NEWS.d/next/Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst b/Misc/NEWS.d/next/Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst new file mode 100644 index 00000000000000..0302faf9c88efa --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst @@ -0,0 +1,2 @@ +Reduce the import time of :mod:`tkinter` with lazy imports. Patch by Rihaan +Meher. From 01000ea3176d3310d3b9785cb58f648814a91c23 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 11 Apr 2026 19:18:21 -0500 Subject: [PATCH 07/15] Move lazy import re down in the file --- Lib/tkinter/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 75bc42d092e073..29963768b23dad 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -36,7 +36,6 @@ lazy import enum lazy import sys lazy import types -lazy import re TclError = _tkinter.TclError @@ -46,6 +45,8 @@ TkVersion = float(_tkinter.TK_VERSION) TclVersion = float(_tkinter.TCL_VERSION) +lazy import re + READABLE = _tkinter.READABLE WRITABLE = _tkinter.WRITABLE EXCEPTION = _tkinter.EXCEPTION From 1da6e7b7cc0063fc347b5a24b2d0730761335bf2 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 11 Apr 2026 20:39:44 -0500 Subject: [PATCH 08/15] Reorder imports for clarity --- Lib/tkinter/__init__.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 29963768b23dad..1ceae22a15704c 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -31,12 +31,12 @@ """ import _tkinter # If this fails your Python may not be configured for Tk from tkinter.constants import * +import collections +import enum -lazy import collections -lazy import enum lazy import sys lazy import types - +lazy import re TclError = _tkinter.TclError wantobjects = 1 @@ -45,8 +45,6 @@ TkVersion = float(_tkinter.TK_VERSION) TclVersion = float(_tkinter.TCL_VERSION) -lazy import re - READABLE = _tkinter.READABLE WRITABLE = _tkinter.WRITABLE EXCEPTION = _tkinter.EXCEPTION From f0bd1ebd7c4c6ac7126d7fc8332e59744427d6fc Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sat, 11 Apr 2026 20:46:27 -0500 Subject: [PATCH 09/15] Stop lazy imported modules from appearing in __all__ --- Lib/tkinter/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 1ceae22a15704c..9ba3caeda85d8c 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -5104,6 +5104,7 @@ def _test(): __all__ = [name for name, obj in globals().items() if not name.startswith('_') and not isinstance(obj, types.ModuleType) + and not isinstance(obj, types.LazyImportType) and name not in {'wantobjects'}] if __name__ == '__main__': From e9803f4826f40d9b384f289c1403854633a08d6b Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sun, 12 Apr 2026 09:35:13 -0500 Subject: [PATCH 10/15] Remove lazy import from sys and types --- Lib/tkinter/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 9ba3caeda85d8c..07709a18286dcd 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -33,9 +33,9 @@ from tkinter.constants import * import collections import enum +import sys +import types -lazy import sys -lazy import types lazy import re TclError = _tkinter.TclError From 83500431ba01108e23eec1d6c449eeee21b152fe Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sun, 12 Apr 2026 09:53:29 -0500 Subject: [PATCH 11/15] Improved docstrings for new functions --- Lib/tkinter/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 07709a18286dcd..1ae87cfb9bb33f 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -53,14 +53,18 @@ _space_re = None def _get_magic_re(): - """Internal function.""" + """ + Internal function that acts as a wrapper for the re import to make it lazy + """ global _magic_re if _magic_re is None: _magic_re = re.compile(r'([\\{}])') return _magic_re def _get_space_re(): - """Internal function.""" + """ + Internal function that acts as a wrapper for the re import to make it lazy + """ global _space_re if _space_re is None: _space_re = re.compile(r'([\s])', re.ASCII) From 2558665f1616ee10f76cdb092aed2359d426f1c1 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sun, 12 Apr 2026 11:37:56 -0500 Subject: [PATCH 12/15] Add TestTkImportTime test class in MISC --- Lib/test/test_tkinter/test_misc.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index f017e94a8b3c22..de607280128944 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -11,6 +11,13 @@ support.requires('gui') +class TestTkImportTime(AbstractTkTest, unittest.TestCase): + @support.cpython_only + def test_lazy_imports(self): + import_helper.ensure_lazy_imports( + "tkinter", {"re", "os", "traceback"} + ) + class MiscTest(AbstractTkTest, unittest.TestCase): def test_all(self): From 5674a95e100728c0011b2cd04d74eeeb98756c87 Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sun, 12 Apr 2026 11:46:08 -0500 Subject: [PATCH 13/15] Lazy import os and traceback --- Lib/tkinter/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 1ae87cfb9bb33f..63340aafa7740d 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -37,6 +37,8 @@ import types lazy import re +lazy import traceback +lazy import os TclError = _tkinter.TclError wantobjects = 1 @@ -54,7 +56,8 @@ def _get_magic_re(): """ - Internal function that acts as a wrapper for the re import to make it lazy + Internal function that acts as a wrapper + for the re import to make it lazy. """ global _magic_re if _magic_re is None: @@ -63,7 +66,8 @@ def _get_magic_re(): def _get_space_re(): """ - Internal function that acts as a wrapper for the re import to make it lazy + Internal function that acts as a wrapper + for the re import to make it lazy. """ global _space_re if _space_re is None: @@ -2540,7 +2544,6 @@ def __init__(self, screenName=None, baseName=None, className='Tk', # ensure that self.tk is always _something_. self.tk = None if baseName is None: - import os baseName = os.path.basename(sys.argv[0]) baseName, ext = os.path.splitext(baseName) if ext not in ('.py', '.pyc'): @@ -2600,7 +2603,6 @@ def readprofile(self, baseName, className): """Internal function. It reads .BASENAME.tcl and .CLASSNAME.tcl into the Tcl Interpreter and calls exec on the contents of .BASENAME.py and .CLASSNAME.py if such a file exists in the home directory.""" - import os if 'HOME' in os.environ: home = os.environ['HOME'] else: home = os.curdir class_tcl = os.path.join(home, '.%s.tcl' % className) @@ -2623,7 +2625,6 @@ def report_callback_exception(self, exc, val, tb): Applications may want to override this internal function, and should when sys.stderr is None.""" - import traceback print("Exception in Tkinter callback", file=sys.stderr) sys.last_exc = val sys.last_type = exc From b78daf78272f43769b6489f595121ef68974e5ca Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sun, 12 Apr 2026 11:59:10 -0500 Subject: [PATCH 14/15] Fix import in lazy import test --- Lib/test/test_tkinter/test_misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index de607280128944..11bfefdc1a9169 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -14,7 +14,7 @@ class TestTkImportTime(AbstractTkTest, unittest.TestCase): @support.cpython_only def test_lazy_imports(self): - import_helper.ensure_lazy_imports( + support.import_helper.ensure_lazy_imports( "tkinter", {"re", "os", "traceback"} ) From e269325f3eff804f28748c3590d27fb9008dcfee Mon Sep 17 00:00:00 2001 From: Rihaan Meher Date: Sun, 12 Apr 2026 12:00:56 -0500 Subject: [PATCH 15/15] Update NEWS entry --- .../Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst b/Misc/NEWS.d/next/Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst index 0302faf9c88efa..b428e755a937e4 100644 --- a/Misc/NEWS.d/next/Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst +++ b/Misc/NEWS.d/next/Library/2026-04-11-18-49-40.gh-issue-109653.VXU-w8.rst @@ -1,2 +1,2 @@ -Reduce the import time of :mod:`tkinter` with lazy imports. Patch by Rihaan -Meher. +Reduce the import time of :mod:`tkinter` with lazy imports. Authored by Rihaan +Meher in :gh:`148409`