2

When the following code is run from terminal, it shows a tkinter frame with three buttons. The test button simply prints "testing". The sleep() button calls sleep in the button's callback and makes the UI non-responsive (expected) for the duration of sleep. The input() button calls Python's input() function and just echoes the text entered. What is surprising to me is that while the input() function when called by itself is a blocking function, it still keeps the UI responsive (moving window, resizing window, clicking test button etc.)

import tkinter as tk
import time

btn_input = None

def cb_input():
    global btn_input
    btn_input.config(state="disabled")
    print("Enter text to echo")
    while True:
        cmd = input()
        print(">> " + cmd)
    
root = tk.Tk()
root.geometry("200x100")
btn_input = tk.Button(root, text = "input()", command = cb_input)
btn_input.pack()
tk.Button(root, text = "test", command = lambda: print("testing")).pack()
tk.Button(root, text = "sleep()", command = lambda : time.sleep(5)).pack()

root.mainloop()

I tried replacing root.mainloop() with the following codeblock

### root.mainloop()
count = 0
while True:
    print(count)
    count += 1
    root.update()

As you would expect, it continuously prints incremental count. When you click "input()" button, the printing of the count variable stops, which leads me to believe that root.update() is not being called anymore, but the UI still remains responsive.

I ran strace on it and saw something like the following (truncated).

futex(0x562fbdacf5c0, FUTEX_WAIT_PRIVATE, 0, NULL) = 0
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 0
recvmsg(4, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\7\4|\1\256e\250\218\5\0\0\16\0`\4\r\0`\4'\1\265\3\222\0\2\0\0\0\0\2"..., iov_len=4096}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 96
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, 8) = 0
poll([{fd=4, events=POLLIN|POLLOUT}], 1, -1) = 1 ([{fd=4, revents=POLLOUT}])
writev(4, [{iov_base="\177\0\1\0", iov_len=4}, {iov_base=NULL, iov_len=0}, {iov_base="", iov_len=0}], 3) = 4
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, 8) = 0
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
write(6, "\0", 1)                       = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 0
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
write(6, "\0", 1)                       = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x562fbdacf5c0, FUTEX_WAIT_PRIVATE, 0, NULL) = 0
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 0
recvmsg(4, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\6\0}\1\266e\250\218\5\0\0\r\0`\4\0\0\0\0&\1\265\3\221\0\2\0\0\0\1\0", iov_len=4096}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 32
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, 8) = 0
poll([{fd=4, events=POLLIN|POLLOUT}], 1, -1) = 1 ([{fd=4, revents=POLLOUT}])
writev(4, [{iov_base="\177\0\1\0", iov_len=4}, {iov_base=NULL, iov_len=0}, {iov_base="", iov_len=0}], 3) = 4
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, 8) = 0
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
write(6, "\0", 1)                       = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 0
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
write(6, "\0", 1)                       = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x562fbdacf5c0, FUTEX_WAIT_PRIVATE, 0, NULL) = 0
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 0
recvmsg(4, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\6\0~\1\276e\250\218\5\0\0\r\0`\4\0\0\0\0!\1\265\3\214\0\2\0\0\0\1\0", iov_len=4096}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 32
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, 8) = 0
poll([{fd=4, events=POLLIN|POLLOUT}], 1, -1) = 1 ([{fd=4, revents=POLLOUT}])
writev(4, [{iov_base="\177\0\1\0", iov_len=4}, {iov_base=NULL, iov_len=0}, {iov_base="", iov_len=0}], 3) = 4
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, 8) = 0
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
write(6, "\0", 1)                       = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 0
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
write(6, "\0", 1)                       = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x562fbdacf5c0, FUTEX_WAIT_PRIVATE, 0, NULL) = 0
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 0
recvmsg(4, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\6\0\177\1\305e\250\218\5\0\0\r\0`\4\0\0\0\0\35\1\266\3\210\0\3\0\0\0\1\0", iov_len=4096}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 32
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, 8) = 0
poll([{fd=4, events=POLLIN|POLLOUT}], 1, -1) = 1 ([{fd=4, revents=POLLOUT}])
writev(4, [{iov_base="\177\0\1\0", iov_len=4}, {iov_base=NULL, iov_len=0}, {iov_base="", iov_len=0}], 3) = 4
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fd168e24b80}, 8) = 0
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
write(6, "\0", 1)                       = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x7fd15b3372e0, FUTEX_WAKE_PRIVATE, 1) = 0
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(4, {msg_namelen=0}, 0)          = -1 EAGAIN (Resource temporarily unavailable)

Hoping to learn from smarter and more experienced developers to make sense of this behavior.

nkk
  • 21
  • 1
  • 2
  • 4
    `input()` repeatedly calls a C-level function pointer called `PyOS_InputHook`, under circumstances I'm not entirely clear on (this might require that `input()` is being provided by GNU `readline`, rather than simply reading from stdin). Tkinter supplies such a function, which calls the equivalent of `update_idletasks()`. – jasonharper Jan 06 '23 at 19:44
  • @jasonharper you should post that as an answer. – Ahmed AEK Jan 06 '23 at 19:52
  • Thank you @jasonharper for pointing me in the right direction. After googling for PyOS_InputHook and reading more about it I have a much better understanding now. I found [this](https://stackoverflow.com/questions/44101598/how-input-can-substitute-tkinter-mainloop) and [this](https://stackoverflow.com/questions/20891710/what-magic-prevents-tkinter-programs-from-blocking-in-interactive-shell) the most relevant. Funnily enough these did not show up in suggested previous questions when I was posting this question. – nkk Jan 06 '23 at 20:03
  • @nkk +1 Thanks for the good question, I learned something new today. Although I don't use `input()` when using `tkinter`, it's still good to know. – TheLizzard Jan 06 '23 at 21:48

0 Answers0