Skip to content

gh-154357: Hide the root window in the tkinter dialog tests#154370

Open
serhiy-storchaka wants to merge 2 commits into
python:mainfrom
serhiy-storchaka:gh-154357-dialog-focus-race
Open

gh-154357: Hide the root window in the tkinter dialog tests#154370
serhiy-storchaka wants to merge 2 commits into
python:mainfrom
serhiy-storchaka:gh-154357-dialog-focus-race

Conversation

@serhiy-storchaka

@serhiy-storchaka serhiy-storchaka commented Jul 21, 2026

Copy link
Copy Markdown
Member

The tkinter dialog tests drive the dialogs with generated keyboard events. Tk delivers such an event to the window which has the keyboard focus, not to the window passed to event_generate(), and drops it if the focus is elsewhere. The binding then never fires, and the test fails with an unset result.

AbstractTkTest.setUp() deiconifies the root window, and the window manager can take the focus back from the dialog. Waiting until the dialog has the focus is not enough, because it is lost again a moment later. Withdraw the root window instead, so that nothing competes for the focus; the dialogs are toplevels of their own and do not need a visible root.

This fixes 9 failing tests in test_simpledialog and a flaky one in test_filedialog on X11 with KWin. The failure does not reproduce without a window manager, which is why the buildbots do not show it.

Tk delivers generated keyboard events to the window which has the focus.
While the root window is visible, the window manager can take the focus
back from the dialog, and the events are dropped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ByteFlowing1337

Copy link
Copy Markdown
Contributor

This fixes the reported issue, but there are still some flaky tests under test_misc.py, for example:

======================================================================
FAIL: test_wm_stackorder (test.test_tkinter.test_misc.WmTest.test_wm_stackorder)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/a/cpython/Lib/test/test_tkinter/test_misc.py", line 1335, in test_wm_stackorder
    self.assertGreater(names.index(str(t1)), names.index(str(t2)))
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 1 not greater than 2

======================================================================
======================================================================
FAIL: test_focus (test.test_tkinter.test_misc.EventTest.test_focus)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/a/cpython/Lib/test/test_tkinter/test_misc.py", line 1364, in test_focus
    self.assertEqual(len(events), 1, events)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 2 != 1 : [<FocusIn event>, <FocusIn event>]

======================================================================

The window manager can reorder the toplevel windows while they are still
being mapped, and can take the focus back while events are processed.
Wait until the windows are mapped before changing the stacking order, and
force the focus right before generating a keyboard event.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@serhiy-storchaka

Copy link
Copy Markdown
Member Author

test_wm_stackorder reproduces here when the whole WmTest class runs: lift() is issued while the toplevels are still being mapped. Waiting until they are mapped fixes it.

I cannot reproduce the duplicate FocusIn in test_focus. Could you print e.detail and e.send_event for both events? The test checks e.detail == 'NotifyAncestor' on events[0], so relaxing the count may not be enough. Which window manager do you use?

@ByteFlowing1337

Copy link
Copy Markdown
Contributor

I tested on WSL2, so my window manager environment may not be standard. It's Weston.


In test_focus, for both events, e.detail is NotifyAncestor, e.send_event is False.


I applied your patch for fixing test_wm_stackorder, but it still fails:

======================================================================
FAIL: test_wm_stackorder (test.test_tkinter.test_misc.WmTest.test_wm_stackorder)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/a/cpython/Lib/test/test_tkinter/test_misc.py", line 1336, in test_wm_stackorder
    self.assertGreater(names.index(str(t1)), names.index(str(t2)))
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 0 not greater than 2

----------------------------------------------------------------------

If I remove that assertion, the following assertions can still fail:

self.assertIs(t1.wm_stackorder('isabove', t2), True)
self.assertIs(t1.wm_stackorder('isbelow', t2), False)
self.assertIs(t2.wm_stackorder('isbelow', t1), True)

And test_focus_methods can also fail:

======================================================================
FAIL: test_focus_methods (test.test_tkinter.test_misc.MiscTest.test_focus_methods)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/a/cpython/Lib/test/test_tkinter/test_misc.py", line 519, in test_focus_methods
    self.assertIs(self.root.focus_get(), b)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <tkinter.Button object .+frame.+button>

----------------------------------------------------------------------

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants