Skip to content

Commit 9dbcb31

Browse files
[3.13] gh-69134: Harden NotebookTest.test_traversal (GH-153403)
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 53b0498) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 89e2a5e commit 9dbcb31

2 files changed

Lines changed: 26 additions & 8 deletions

File tree

Lib/test/test_tkinter/support.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def destroy_default_root():
9393
tkinter._default_root.destroy()
9494
tkinter._default_root = None
9595

96-
def wait_until_mapped(widget, timeout=None):
96+
def wait_until_mapped(widget, timeout=None, *, full_size=False):
9797
"""Wait until *widget* is actually mapped and laid out by the window
9898
manager, so that realized-geometry queries (winfo_width(), identify(),
9999
coords(), ...) return meaningful values.
@@ -103,17 +103,26 @@ def wait_until_mapped(widget, timeout=None):
103103
``support.LOOPBACK_TIMEOUT``). Unlike Misc.wait_visibility(), this
104104
never blocks indefinitely, so it is safe under a window manager that
105105
never maps the window (see gh-69134, gh-74941, bpo-40722).
106+
107+
If *full_size* is true, also wait until the realized size reaches the
108+
requested size, so that per-pixel queries near an edge (e.g. identify())
109+
are reliable even under load.
106110
"""
107111
if timeout is None:
108112
timeout = support.LOOPBACK_TIMEOUT
109113
deadline = time.monotonic() + timeout
110114
widget.update_idletasks()
111115
while True:
112116
widget.update() # drain pending Map/Configure events
113-
if (widget.winfo_ismapped()
114-
and widget.winfo_width() > 1
115-
and widget.winfo_height() > 1):
116-
return True
117+
if widget.winfo_ismapped():
118+
if full_size:
119+
w_ok = widget.winfo_width() >= widget.winfo_reqwidth() > 1
120+
h_ok = widget.winfo_height() >= widget.winfo_reqheight() > 1
121+
else:
122+
w_ok = widget.winfo_width() > 1
123+
h_ok = widget.winfo_height() > 1
124+
if w_ok and h_ok:
125+
return True
117126
if time.monotonic() >= deadline:
118127
return False
119128
time.sleep(0.01)

Lib/test/test_ttk/test_widgets.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,8 +1192,16 @@ def test_traversal(self):
11921192

11931193
self.nb.select(0)
11941194

1195-
focus_identify_as = 'focus' if sys.platform != 'darwin' else ''
1196-
self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
1195+
if sys.platform == 'darwin':
1196+
focus_identify_as = ''
1197+
elif sys.platform == 'win32':
1198+
focus_identify_as = 'focus'
1199+
else:
1200+
focus_identify_as = 'focus' if tk_version < (8, 7) else 'padding'
1201+
# identify() at (5, 5) needs the tab realized there; under focus
1202+
# contention the mapped size can lag, so wait for the full size.
1203+
if wait_until_mapped(self.nb, full_size=True):
1204+
self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
11971205
simulate_mouse_click(self.nb, 5, 5)
11981206
self.nb.focus_force()
11991207
self.nb.event_generate('<Control-Tab>')
@@ -1209,7 +1217,8 @@ def test_traversal(self):
12091217
self.nb.tab(self.child2, text='e', underline=0)
12101218
self.nb.enable_traversal()
12111219
self.nb.focus_force()
1212-
self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
1220+
if wait_until_mapped(self.nb, full_size=True):
1221+
self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
12131222
simulate_mouse_click(self.nb, 5, 5)
12141223
# on macOS Emacs-style keyboard shortcuts are region-dependent;
12151224
# let's use the regular arrow keys instead

0 commit comments

Comments
 (0)