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: signal.set_wakeup_fd() doesn't work if the signal don't have handler
Type: Stage:
Components: Documentation Versions: Python 3.5
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: benjamin.peterson, docs@python, ishimoto, vstinner
Priority: normal Keywords:

Created on 2015-10-26 11:34 by ishimoto, last changed 2022-04-11 14:58 by admin.

Messages (12)
msg253468 - (view) Author: Atsuo Ishimoto (ishimoto) * Date: 2015-10-26 11:34
I expect following code prints '\x1c' when I resize terminal window on Linix/Mac terminal.

--------------------------------
import select, os, signal

rfd, wfd = os.pipe()
os.set_blocking(wfd, False)

signal.set_wakeup_fd(wfd)
select.select([rfd], [], [])
print(os.read(rfd, 2))
--------------------------------

Resizing terminal window make SIGWINCH signal sent to Python process. But nothing written to fd I specified to set_wakeup_fd().

I checked Modules/signalmodule.c and found I should assign signal handler for the signal I want to wakeup fd. So following code works as I expected.

--------------------------------
import select, os, signal

rfd, wfd = os.pipe()
os.set_blocking(wfd, False)

signal.set_wakeup_fd(wfd)

signal.signal(signal.SIGWINCH, lambda *args:0)
select.select([rfd], [], [])
print(os.read(rfd, 2))
--------------------------------

Is this an expected behavior of signal.set_wakeup_fd()?
msg253517 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2015-10-27 04:49
Yes, you need to tell the kernel to not ignore SIGWINCH automatically for you.
msg253520 - (view) Author: Atsuo Ishimoto (ishimoto) * Date: 2015-10-27 05:27
Okay, so I think this needed to be documented in signal.set_wakeup_fd(). 

IMO this reduces usefulness of set_wakeup_fd(). If I need to install custom handler by my self, I can write to fd in my own custom handler.

And,  installing custom handler omits existing handler, but we can not execute existing signal handler from python script. In my real-world case, SIGWINCH signal does not delivered to curses library. 

pep-475(https://www.python.org/dev/peps/pep-0475/#backward-compatibility) claims that 

    "For applications using event loops, signal.set_wakeup_fd() is the recommanded option to handle signals. "

But I think side effect of signal.set_wakeup_fd() is lager than pep-475 authors expected.
msg253602 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-10-28 14:54
> IMO this reduces usefulness of set_wakeup_fd(). (...)

Sorry, I don't understand your use case. Could you elaborate. What do you need?

--

According to the GNU libc doc, the signal SIGWINCH is *ignored* by default:
"Window size change. This is generated on some systems (including GNU) when the terminal driver’s record of the number of rows and columns on the screen is changed. The default action is to ignore it."
https://www.gnu.org/software/libc/manual/html_node/Miscellaneous-Signals.html

For signal.set_wakeup_fd() I agree that the doc can be enhanced. It's not explicit that only signals with a *Python* signal handler (at least, a signal handler registered by signal.signal) write into the "wakeup FD".
msg253657 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2015-10-29 06:17
The OP probably wants something like the Linux-specific signalfd() syscall.
msg253677 - (view) Author: Atsuo Ishimoto (ishimoto) * Date: 2015-10-29 14:44
> Benjamin Peterson added the comment:
> 
> The OP probably wants something like the Linux-specific signalfd() syscall.

Yes.

Long explanation:

I have an console text editor written in Python/curses(http://kaaedit.github.io/).

This editor didn't work with Python 3.5 because select.select() stop raising InterruptedError.

Following is sample code to reproduce. This code displays size of terminal. If you resize your terminal, screen is updated and new size is displayed.

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

import curses, select, sys

def main(stdscr):
    i=0
    while True:
        y, x = stdscr.getmaxyx()
        stdscr.addstr(i % 10, 0, "%d x:%d y:%d" % (i, x, y))
        i+=1
        stdscr.refresh()
        try:
            s=select.select([sys.stdin], [], [])   # wait for user input and some sockets.
        except InterruptedError:
            stdscr.addstr(i % 10, 0, "%d InterruptedError" % i)
            i+=1
            continue
        try:
            c=stdscr.get_wch()
        except curses.error:
            stdscr.addstr(i % 10, 0, "%d curses.error" % i)
            i+=1
            continue
        if c == 'q':
            break

curses.wrapper(main)

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

Resizing terminal generates SIGIWNCH signal. In Python 3.4, select.select() raises InterruptedError when the signal sent. Also, Ncurses library catches the signal and update internal data to reflect new terminal size.

To detect resizing in Python 3.5, I had to set signal handler for SIGWINCH as following sample. 

---------------
import curses, select, sys, signal, os

rfd, wfd = os.pipe()
os.set_blocking(wfd, False)

def f(*args): pass
signal.signal(signal.SIGWINCH, f)
signal.set_wakeup_fd(wfd)

def main(stdscr):
    i=0
    while True:
        y, x = stdscr.getmaxyx()
        stdscr.addstr(i, 0, "%d x:%d y:%d" % (i, x, y))
        i+=1
        stdscr.refresh()
        try:
            s=select.select([sys.stdin, rfd], [], [])
        except InterruptedError:
            stdscr.addstr(i, 0, "%d InterruptedError" % i)
            i+=1
            continue
        try:
            c=stdscr.get_wch()
        except curses.error:
            stdscr.addstr(i, 0, "%d curses.error" % i)
            i+=1
            continue
        if c == 'q':
            break

curses.wrapper(main)
-------------

This code doesn't display updated size of terminal because SIGWINCH signal is not sent to nurses library. I have to call curses.resizeterm() manually to tell new terminal size to nurses.

I don't know this is common situation or not, but In general, it is not safe to set signal handler when the signal is used by third-party library.
msg253711 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-10-30 03:14
It's very easy to get the same behaviour on Python 3.4 (without PEP 475) and Python 3.5 (with PEP 475). Just add the following code at the top of your example:
----
import signal

def sighandler(signum, frame):
    raise InterruptedError

signal.signal(signal.SIGWINCH, sighandler)
---

You can use any Python exception, not only InterruptedError.

PEP 475 only changes the behaviour on signals with an handler which doesn't raise an exception.
msg253712 - (view) Author: Atsuo Ishimoto (ishimoto) * Date: 2015-10-30 03:33
Thank you for good sample. This should be written in PEP 475.

But installing new signal handler can cause different behavior. 

As in my previous example, if other 3rd party libraries(ncurses in my case) need to handle the signal, signal is consumed by Python handler and not delivered to the library.
msg253724 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-10-30 10:02
> As in my previous example, if other 3rd party libraries(ncurses in my case) need to handle the signal, signal is consumed by Python handler and not delivered to the library.

In the Python signal handler, can't you call the ncurses function that
should be called when the terminal is resized?
msg253727 - (view) Author: Atsuo Ishimoto (ishimoto) * Date: 2015-10-30 11:13
Yes, I can. So, for my custom text editor,  problem have solved already.

But I still think it is desirable for Python to have something like signalfd(),  because not all functions used in the signal handler of third party library can be called from user's Python script in general.
msg253728 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-10-30 11:29
Atsuo Ishimoto added the comment:
> But I still think it is desirable for Python to have something like signalfd(),  because not all functions used in the signal handler of third party library can be called from user's Python script in general.

I don't think that you can use signalfd() in your case. When you use
signalfd(), signals must be blocked, so the signal handlers are *not*
called (not the Python signal handler, not the C signal handler, no
signal handler for the masked signals).
msg253729 - (view) Author: Atsuo Ishimoto (ishimoto) * Date: 2015-10-30 11:39
Ah, I didn't know it. Thank you for clarification.
History
Date User Action Args
2022-04-11 14:58:23adminsetgithub: 69668
2015-10-30 11:39:35ishimotosetmessages: + msg253729
2015-10-30 11:29:12vstinnersetmessages: + msg253728
2015-10-30 11:13:06ishimotosetmessages: + msg253727
2015-10-30 10:02:26vstinnersetmessages: + msg253724
2015-10-30 03:33:43ishimotosetmessages: + msg253712
2015-10-30 03:14:07vstinnersetmessages: + msg253711
2015-10-29 14:44:24ishimotosetmessages: + msg253677
2015-10-29 06:17:25benjamin.petersonsetmessages: + msg253657
2015-10-28 14:54:44vstinnersetmessages: + msg253602
2015-10-28 10:58:37berker.peksagsetnosy: + vstinner
2015-10-27 05:27:45ishimotosetstatus: closed -> open

assignee: docs@python
components: + Documentation, - Library (Lib)

nosy: + docs@python
messages: + msg253520
resolution: not a bug ->
2015-10-27 04:49:56benjamin.petersonsetstatus: open -> closed

nosy: + benjamin.peterson
messages: + msg253517

resolution: not a bug
2015-10-26 11:34:56ishimotocreate