Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test_pyrepl: test_cursor_back_write() blocks on input_hook() when tests are run sequentially #121008

Closed
vstinner opened this issue Jun 25, 2024 · 9 comments
Labels
tests Tests in the Lib/test dir topic-IDLE topic-repl Related to the interactive shell topic-tkinter

Comments

@vstinner
Copy link
Member

vstinner commented Jun 25, 2024

Command:

$ ./python -m test -u all,-gui 
(...)
0:24:10 load avg: 0.92 [326/478] test_pyclbr
0:24:13 load avg: 0.92 [327/478] test_pyexpat
0:24:13 load avg: 0.92 [328/478] test_pyrepl

gdb:

(gdb) where
#0  0x00007f3b0325ec37 in select () from /lib64/libc.so.6
#1  0x00007f3af40f4497 in Sleep (milli=<optimized out>) at ./Modules/_tkinter.c:371
#2  0x00007f3af40f4faa in EventHook () at ./Modules/_tkinter.c:3350
#3  0x0000000000666d86 in os__inputhook_impl (module=<optimized out>) at ./Modules/posixmodule.c:16795
#4  0x0000000000666dad in os__inputhook (module=<optimized out>, _unused_ignored=_unused_ignored@entry=0x0)
    at ./Modules/clinic/posixmodule.c.h:12134
(...)

(gdb) py-bt
Traceback (most recent call first):
  <built-in method _inputhook of module object at remote 0x7f3af53d9850>
  File "/home/vstinner/python/main/Lib/_pyrepl/reader.py", line 719, in handle1
    input_hook()
  File "/home/vstinner/python/main/Lib/test/test_pyrepl/support.py", line 75, in handle_all_events
    reader.handle1()
  File "/home/vstinner/python/main/Lib/test/test_pyrepl/test_unix_console.py", line 197, in test_cursor_back_write
    _, con = handle_events_unix_console(events)
  File "/home/vstinner/python/main/Lib/unittest/mock.py", line 1423, in patched
    return func(*newargs, **newkeywargs)
  File "/home/vstinner/python/main/Lib/unittest/case.py", line 606, in _callTestMethod
    result = method()
  File "/home/vstinner/python/main/Lib/unittest/case.py", line 660, in run
    self._callTestMethod(testMethod)
(...)

Linked PRs

@vstinner vstinner added the tests Tests in the Lib/test dir label Jun 25, 2024
@vstinner
Copy link
Member Author

Reproducer with less tests:

./python -m test -u all,-gui test_idle test_pyrepl

test_idle sets PyOS_InputHook in:

(gdb) where
#0  0x00007fffe96b927a in EnableEventHook () at ./Modules/_tkinter.c:3380
#1  0x00007fffe96be707 in Tkapp_New (screenName=screenName@entry=0x0, className=className@entry=0x7fffe9cbf588 "Tk", 
    interactive=interactive@entry=0, wantobjects=wantobjects@entry=1, wantTk=wantTk@entry=0, sync=sync@entry=0, use=0x0)
    at ./Modules/_tkinter.c:736
#2  0x00007fffe96be8f5 in _tkinter_create_impl (module=module@entry=<module at remote 0x7fffe96f5a90>, screenName=screenName@entry=0x0, 
    baseName=baseName@entry=0x7fffe8389328 "__main__", className=className@entry=0x7fffe9cbf588 "Tk", interactive=interactive@entry=0, 
    wantobjects=wantobjects@entry=1, wantTk=0, sync=0, use=0x0) at ./Modules/_tkinter.c:3176
#3  0x00007fffe96bed69 in _tkinter_create (module=<module at remote 0x7fffe96f5a90>, args=args@entry=0x7ffff7fba430, nargs=nargs@entry=8)
    at ./Modules/clinic/_tkinter.c.h:820

(gdb) py-bt
Traceback (most recent call first):
  <built-in method create of module object at remote 0x7fffe96f5a90>
  File "/home/vstinner/python/main/Lib/tkinter/__init__.py", line 2459, in __init__
    self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
  File "/home/vstinner/python/main/Lib/tkinter/__init__.py", line 2572, in Tcl
    return Tk(screenName, baseName, className, useTk)
  File "/home/vstinner/python/main/Lib/idlelib/run.py", line 94, in <module>
    tcl = tkinter.Tcl()
  <built-in method exec of module object at remote 0x7ffff7c94770>
(...)

@vstinner
Copy link
Member Author

cc @pablogsal @ambv @Yhg1s

@sobolevn sobolevn added topic-IDLE topic-repl Related to the interactive shell topic-tkinter labels Jun 26, 2024
@terryjreedy
Copy link
Member

I cannot reproduce on Windows. After some time, the test call above hung until I hit return, then it was a success.

The apparently offending chain of events is that test_idle runs (idlelib.idle_test.)test_run which imports idlelib.run which calls tkinter.Tcl which at top level calls into _tkinter which sets tcl to get tk events. Idlelib.run is the only place IDLE calls tkinter.Tcl, but I suspect that calls to _tkinter from tkinter.tk do the same. However, IDLE test that call tk() do so explicitly and later call root.destroy(). Since Tcl.destroy exists, a fix might be to add the following top level cleanup function near the top of test_run.

def tearDownModule():
    run.tcl.destroy()

I'm off to bed.

@vstinner
Copy link
Member Author

If I just add tearDownModule() to Lib/idlelib/idle_test/test_run.py, I get:

ERROR: tearDownModule (idlelib.idle_test.test_run)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/vstinner/python/main/Lib/idlelib/idle_test/test_run.py", line 433, in tearDownModule
    run.tcl.destroy()
    ~~~~~~~~~~~~~~~^^
  File "/home/vstinner/python/main/Lib/tkinter/__init__.py", line 2503, in destroy
    self.tk.call('destroy', self._w)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
_tkinter.TclError: invalid command name "destroy"

@terryjreedy
Copy link
Member

Tcl() returns a Tk object created with useTk False instead of True. So the tkinter destroy method is the one called normally.

def Tcl(screenName=None, baseName=None, className='Tk', useTk=False):
    return Tk(screenName, baseName, className, useTk)

But this and perhaps other methods are not valid for such object. This is a tkinter bug.

Alternate try: in idlelib.run, replace lines 94-100

tcl = tkinter.Tcl()

def handle_tk_events(tcl=tcl):
    """Process any tk events that are ready to be dispatched if tkinter
    has been imported, a tcl interpreter has been created and tk has been
    loaded."""
    tcl.eval("update")

with

if idlelib.testing:
    def handle_tk_events(): pass
else:
    tcl = tkinter.Tcl()

    def handle_tk_events(tcl=tcl):
        """Process any tk events that are ready to be dispatched if tkinter
        has been imported, a tcl interpreter has been created and tk has been
        loaded."""
        tcl.eval("update")

This might not be a permanent solution but should work. I cannot test for several hours.

vstinner added a commit to vstinner/cpython that referenced this issue Jun 26, 2024
When testing IDLE, don't create a Tk to avoid side effects such as
installing a PyOS_InputHook hook.
@vstinner
Copy link
Member Author

I wrote a fix based on @terryjreedy's recipe and it works for me!

vstinner added a commit that referenced this issue Jun 26, 2024
When testing IDLE, don't create a Tk to avoid side effects such as
installing a PyOS_InputHook hook.
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jun 26, 2024
When testing IDLE, don't create a Tk to avoid side effects such as
installing a PyOS_InputHook hook.
(cherry picked from commit 44eafd6)

Co-authored-by: Victor Stinner <[email protected]>
vstinner added a commit that referenced this issue Jun 26, 2024
gh-121008: Fix idlelib.run tests (GH-121046)

When testing IDLE, don't create a Tk to avoid side effects such as
installing a PyOS_InputHook hook.
(cherry picked from commit 44eafd6)

Co-authored-by: Victor Stinner <[email protected]>
@vstinner
Copy link
Member Author

Fixed by 44eafd6.

@vstinner
Copy link
Member Author

Thank you @terryjreedy for the fix!

@terryjreedy
Copy link
Member

If I ever want to unittest run's handling of interactive tkinter calls, I will ask Serhiy to add a DisableEventHook to _tkinter, if not there already, and make it accessible from Python code, perhaps by making Tcl a subclass that defines a destroy method that works.

mrahtz pushed a commit to mrahtz/cpython that referenced this issue Jun 30, 2024
When testing IDLE, don't create a Tk to avoid side effects such as
installing a PyOS_InputHook hook.
noahbkim pushed a commit to hudson-trading/cpython that referenced this issue Jul 11, 2024
When testing IDLE, don't create a Tk to avoid side effects such as
installing a PyOS_InputHook hook.
estyxx pushed a commit to estyxx/cpython that referenced this issue Jul 17, 2024
When testing IDLE, don't create a Tk to avoid side effects such as
installing a PyOS_InputHook hook.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tests Tests in the Lib/test dir topic-IDLE topic-repl Related to the interactive shell topic-tkinter
Projects
None yet
Development

No branches or pull requests

3 participants