This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: IDLE: Make GUI test teardown less fragile
Type: enhancement Stage: needs patch
Components: IDLE Versions: Python 3.7, Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: terry.reedy Nosy List: cheryl.sabella, terry.reedy
Priority: normal Keywords:

Created on 2017-08-26 16:55 by cheryl.sabella, last changed 2022-04-11 14:58 by admin.

Pull Requests
URL Status Linked Edit
PR 3212 closed cheryl.sabella, 2017-08-26 16:58
Messages (6)
msg300885 - (view) Author: Cheryl Sabella (cheryl.sabella) * (Python committer) Date: 2017-08-26 16:55
When running tests that use an EditorWindow, the following warning message occurs:

warning: callback failed in WindowList <class '_tkinter.TclError'> : invalid command name ".!menu.windows"

This warning comes from `call_callbacks` in WindowsList in windows.py when the callback is `postwindowsmenu()` from editor, which is set for the `windows` menu.  The line `end = menu.index("end")` is the line that fails.
msg300908 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-08-26 22:56
I don't remember ever seeing that message.  Do you have code that reliably reproduces the problem?

    def postwindowsmenu(self):
        # Only called when Windows menu exists
        menu = self.menudict['windows']
        end = menu.index("end")
        ...
        windows.add_windows_to_menu(menu)

".!menu.windows" is the tk Windows Menu name, generated by tkinter and saved in menudict.  The function claims that it is only called when the menu exists, but the error is that it does not exist, even though it is still in the dict.  This is a bug.  It must result from a bug either in IDLE or the test teardown code, or maybe both.  I would rather fix the bug than shoot the messenger ;-)

Side note: the callback handling strikes me as slightly crazy.  Creating a ListedTopLevel calls registry.add, which calls all the callbacks, each of which regenerates the menu.
msg300909 - (view) Author: Cheryl Sabella (cheryl.sabella) * (Python committer) Date: 2017-08-26 23:33
I saw it on my PR for the tests in outwin (issue30617) and I also found a comment from someone else in test_paragraph.py about it.

I can reproduce it with this code:
from idlelib import outwin
from tkinter import Tk, Text
root = Tk()
w =  outwin.OutputWindow(None, None, None, root)
w.text = Text(root)
root.mainloop()


I agree that it would be best to fix the root of the problem.  I thought maybe with the test, it's the way the OutputWindow is created  I tried adding an flist, but that still didn't suppress the warning.
msg300912 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-08-27 02:40
As you gave the code, the two windows just sit there with mainloop running.  Replace it with
root.update()
#w.close()
root.destroy()
and run from IDLE editor and I see the ".!menu.windows" message.  Uncomment the close and it goes away.

Root.destroy recursively calls the .destroy methods of its children.  Here, ListedTopLevel.destroy is called.  It calls TopLevel.destroy and registry.delete.  Its menubar child and its dropdown submenu are also destroyed.  But LTL.destroy does *not* call the close method of the EditorWindow instance (which is *not* a widget).  Without a reference, it cannot.

Registry.delete, which is called for each ListedTopLevel *calls all the callbacks*, which it exactly should not do at shutdown.  It does not unregister the associated callback.  That is part of editor.close.

In IDLE's normal operation, editor.close must somehow get called.  In test setups and teardowns, I am careful to undo things in reverse order of creation.  It is, in a sense, a bug, as things now stand, to create an OutputWindow(root) without calling its close method before destroying root.

In configdialog, the callbacks are the Var tracers.  The configdialog test teardown removes them all.  Closing editorwindows should do the same.  This is not to deny that making shutdown less fragile would be a good thing.

I think having separate EditorWindow and EditorFrame classes that *are* widgets would help, as then better auxilliary shutdowns could be either part of or called from the destroy methods that get called when root.destroy is called.

Aside from the ".!menu.windows" message in the idle shell, messages like the following appear in the command window if there is one, with or without the editor.close() call.

 invalid command name "96611576font_timer_event"
    while executing
"96611576font_timer_event"
    ("after" script)

"89887864font_timer_event"
    ("after" script)
invalid command name "89718392timer_event"
    while executing
Functions timer_event and font_timer_event are CodeContext methods.  The messages don't appear if code_context is not enabled.  I don't know how the messages are prevented (normally) when CondContext is enabled.  The is no obvious graceful shutdown of the loops.  They seem to end when self.text no longer exists so that self.text.after must fail.  I already knew that this class needs work ;-).
msg300919 - (view) Author: Cheryl Sabella (cheryl.sabella) * (Python committer) Date: 2017-08-27 17:36
Thank you for taking the time to research and explain that.

It's funny, but on my code example, I got it to happen every time and you said it didn't happen unless you made the change to use root.update() and root.destroy().  I tried it out again and realized that I was closing the tk window before OutputWindow every time, thus I was getting the warning using root.mainloop().  Of course, if I close OutputWindow first, I don't get the warning.  That's running from the command line.

I suppose this issue needs to be split into the real bugs that you mentioned.  It seems to be at least 2 or 3 different things to look at (EditorWindow and EditorFrame and CodeContext).  I'll delete the current PR for this issue.  

Thanks!
msg300925 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-08-27 20:10
I closed the patch; you can delete the branch.

I changed the title to express the goal, though I am not sure how to get there yet.  One thing is to be more aware of what callbacks and event loops are being set up and possibly not shut down gracefully.

Your code as posted did not reproduce the error *without manual action*.  Tests, including the one for #30617, has better run without user intervention.  Once I made the modification to get the error, or not, just by running it, I was pretty sure of the fix for the test.
History
Date User Action Args
2022-04-11 14:58:51adminsetgithub: 75465
2017-08-27 20:10:56terry.reedysettitle: IDLE: Fix WindowList invalid command name error when running tests -> IDLE: Make GUI test teardown less fragile
stage: needs patch
messages: + msg300925
versions: + Python 3.6
2017-08-27 17:36:49cheryl.sabellasetmessages: + msg300919
2017-08-27 02:40:08terry.reedysetmessages: + msg300912
2017-08-26 23:33:13cheryl.sabellasetmessages: + msg300909
2017-08-26 22:56:35terry.reedysetmessages: + msg300908
2017-08-26 16:58:39cheryl.sabellasetpull_requests: + pull_request3250
2017-08-26 16:55:38cheryl.sabellacreate