https://github.com/python/cpython/commit/cee72326ffc2ec8d98f8d435c62e13a2f5c17edf
commit: cee72326ffc2ec8d98f8d435c62e13a2f5c17edf
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-06-26T23:31:09Z
summary:

[3.13] gh-88758: Handle non-tkinter widgets in tkinter focus methods 
(GH-152337) (GH-152348)

focus_get(), focus_displayof(), focus_lastfor() and winfo_containing()
now return None instead of raising KeyError when the focused widget was
not created by tkinter (for example a torn-off menu).
(cherry picked from commit 5fed5ce85d9c862673cc68294f757f345bbcc9b1)

Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-06-26-15-30-00.gh-issue-88758.Qw7nLp.rst
M Lib/test/test_tkinter/test_misc.py
M Lib/tkinter/__init__.py

diff --git a/Lib/test/test_tkinter/test_misc.py 
b/Lib/test/test_tkinter/test_misc.py
index 4434ed08270d76..d7b0ee3d0c2b25 100644
--- a/Lib/test/test_tkinter/test_misc.py
+++ b/Lib/test/test_tkinter/test_misc.py
@@ -416,6 +416,22 @@ def test_focus_methods(self):
         self.root.update()
         self.assertIs(self.root.focus_get(), b)
 
+    def test_focus_methods_unresolvable(self):
+        # The focus may be on a widget that tkinter did not create and so
+        # cannot map to an instance (e.g. a torn-off menu).  The focus
+        # methods return None instead of raising KeyError (gh-88758).
+        menu = tkinter.Menu(self.root, tearoff=1)
+        menu.add_command(label='Hello')
+        tearoff = self.root.tk.call('tk::TearOffMenu', str(menu), 0, 0)
+        self.addCleanup(self.root.tk.call, 'destroy', tearoff)
+        self.root.update()
+        self.assertRaises(KeyError, self.root.nametowidget, tearoff)
+
+        self.root.tk.call('focus', '-force', tearoff)
+        self.root.update()
+        self.assertIsNone(self.root.focus_get())
+        self.assertIsNone(self.root.focus_displayof())
+
     def test_grab(self):
         f = tkinter.Frame(self.root)
         f.pack()
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index 7b3f4bea7ce2c8..2dfe1b7bf82e36 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -803,7 +803,10 @@ def focus_get(self):
         the focus."""
         name = self.tk.call('focus')
         if name == 'none' or not name: return None
-        return self._nametowidget(name)
+        try:
+            return self._nametowidget(name)
+        except KeyError:
+            return None
 
     def focus_displayof(self):
         """Return the widget which has currently the focus on the
@@ -812,14 +815,20 @@ def focus_displayof(self):
         Return None if the application does not have the focus."""
         name = self.tk.call('focus', '-displayof', self._w)
         if name == 'none' or not name: return None
-        return self._nametowidget(name)
+        try:
+            return self._nametowidget(name)
+        except KeyError:
+            return None
 
     def focus_lastfor(self):
         """Return the widget which would have the focus if top level
         for this widget gets the focus from the window manager."""
         name = self.tk.call('focus', '-lastfor', self._w)
         if name == 'none' or not name: return None
-        return self._nametowidget(name)
+        try:
+            return self._nametowidget(name)
+        except KeyError:
+            return None
 
     def tk_focusFollowsMouse(self):
         """The widget under mouse will get automatically focus. Can not
@@ -1222,7 +1231,10 @@ def winfo_containing(self, rootX, rootY, displayof=0):
                + self._displayof(displayof) + (rootX, rootY)
         name = self.tk.call(args)
         if not name: return None
-        return self._nametowidget(name)
+        try:
+            return self._nametowidget(name)
+        except KeyError:
+            return None
 
     def winfo_depth(self):
         """Return the number of bits per pixel."""
diff --git 
a/Misc/NEWS.d/next/Library/2026-06-26-15-30-00.gh-issue-88758.Qw7nLp.rst 
b/Misc/NEWS.d/next/Library/2026-06-26-15-30-00.gh-issue-88758.Qw7nLp.rst
new file mode 100644
index 00000000000000..31d567194fb903
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-26-15-30-00.gh-issue-88758.Qw7nLp.rst
@@ -0,0 +1,4 @@
+:meth:`!tkinter.Misc.focus_get`, :meth:`!focus_displayof`,
+:meth:`!focus_lastfor` and :meth:`!winfo_containing` now return ``None``
+instead of raising :exc:`KeyError` when the widget was not created by
+:mod:`tkinter` (for example a torn-off menu).

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to