]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.15] gh-69134: Harden NotebookTest.test_traversal (GH-153403) (GH-153408)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 9 Jul 2026 10:27:08 +0000 (12:27 +0200)
committerGitHub <noreply@github.com>
Thu, 9 Jul 2026 10:27:08 +0000 (10:27 +0000)
identify(5, 5) could run before the notebook reached its requested size,
so the pixel fell outside the first tab and returned ''.  Guard it with a
new opt-in wait_until_mapped(full_size=True).
(cherry picked from commit 53b04980284b668c27fc6d5adb0de19d38efe95b)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Lib/test/test_tkinter/support.py
Lib/test/test_ttk/test_widgets.py

index df0cca95a33a18064c99e5910312cda10cc2061c..31feee2b8a40ee6d26ff57c26ba1b35e79056b02 100644 (file)
@@ -93,7 +93,7 @@ def destroy_default_root():
         tkinter._default_root.destroy()
         tkinter._default_root = None
 
-def wait_until_mapped(widget, timeout=None):
+def wait_until_mapped(widget, timeout=None, *, full_size=False):
     """Wait until *widget* is actually mapped and laid out by the window
     manager, so that realized-geometry queries (winfo_width(), identify(),
     coords(), ...) return meaningful values.
@@ -103,6 +103,10 @@ def wait_until_mapped(widget, timeout=None):
     ``support.LOOPBACK_TIMEOUT``).  Unlike Misc.wait_visibility(), this
     never blocks indefinitely, so it is safe under a window manager that
     never maps the window (see gh-69134, gh-74941, bpo-40722).
+
+    If *full_size* is true, also wait until the realized size reaches the
+    requested size, so that per-pixel queries near an edge (e.g. identify())
+    are reliable even under load.
     """
     if timeout is None:
         timeout = support.LOOPBACK_TIMEOUT
@@ -110,10 +114,15 @@ def wait_until_mapped(widget, timeout=None):
     widget.update_idletasks()
     while True:
         widget.update()  # drain pending Map/Configure events
-        if (widget.winfo_ismapped()
-                and widget.winfo_width() > 1
-                and widget.winfo_height() > 1):
-            return True
+        if widget.winfo_ismapped():
+            if full_size:
+                w_ok = widget.winfo_width() >= widget.winfo_reqwidth() > 1
+                h_ok = widget.winfo_height() >= widget.winfo_reqheight() > 1
+            else:
+                w_ok = widget.winfo_width() > 1
+                h_ok = widget.winfo_height() > 1
+            if w_ok and h_ok:
+                return True
         if time.monotonic() >= deadline:
             return False
         time.sleep(0.01)
index b2cc7f16f8944648d861d19191184cf6b43a2647..c833058d3bf87d4090dda1a899d625259bb3a132 100644 (file)
@@ -1224,7 +1224,10 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
             focus_identify_as = 'focus'
         else:
             focus_identify_as = 'focus' if tk_version < (8, 7) else 'padding'
-        self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
+        # identify() at (5, 5) needs the tab realized there; under focus
+        # contention the mapped size can lag, so wait for the full size.
+        if wait_until_mapped(self.nb, full_size=True):
+            self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
         simulate_mouse_click(self.nb, 5, 5)
         self.nb.focus_force()
         self.nb.event_generate('<Control-Tab>')
@@ -1240,7 +1243,8 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
         self.nb.tab(self.child2, text='e', underline=0)
         self.nb.enable_traversal()
         self.nb.focus_force()
-        self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
+        if wait_until_mapped(self.nb, full_size=True):
+            self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
         simulate_mouse_click(self.nb, 5, 5)
         # on macOS Emacs-style keyboard shortcuts are region-dependent;
         # let's use the regular arrow keys instead