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: better handling of foreign signal handlers in signal.signal
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.9, Python 3.8, Python 3.7, Python 3.6, Python 3.5, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Steven G. Johnson
Priority: normal Keywords:

Created on 2020-01-23 20:28 by Steven G. Johnson, last changed 2022-04-11 14:59 by admin.

Messages (1)
msg360578 - (view) Author: Steven G. Johnson (Steven G. Johnson) Date: 2020-01-23 20:28
In embedded Python, if the embedding code sets a signal handler (e.g. for SIGINT), then signal.getsignal(SIGINT) returns None.   However, signal.signal(SIGINT, signal.getsignal(SIGINT)) throws a TypeError, even though it should logically be a no-op.   This behavior is all implemented in Modules/signalmodule.c and seems to have been around since 2.7 at least.

(Background: We observed this in Julia, which embeds Python in order to call Python code, where matplotlib code that temporarily set signal(SIGINT, SIG_DFL) led to an exception when it tried to restore the original signal handler.  See https://github.com/JuliaPy/PyPlot.jl/issues/459)

The C program below exhibits this problem [it sets its own SIGINT handler and then starts up Python to execute signal(SIGINT,getsignal)].   Running it results in "TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object"

Recommended changes:

1) if Handlers[signalnum].func == NULL, then signal(signalnum, None) should be a no-op, returning None.     This will allow signal(signalnum, getsignal(signalnum)) to always succeed (as a no-op).

2) if Handlers[signalnum].func == NULL, then signal(signalnum, SIG_DFL) should be a no-op, returning None.  That is, the default signal handler should be the foreign signal handler if one is installed.

3) The signal-handling documentation should warn against overriding the signal handler for any signalnum where getsignal(signalnum) returns None (i.e. a foreign signal handler), since there is no way to restore the original signal handler afterwards. Anyway, you should be cautious about overriding signal handlers that don't come from Python.   

test code that throws a TypeError (compile and link with libpython):

#include <Python.h>
#include <signal.h>
#include <stdio.h>
void myhandler(int sig) { printf("got signal %d\n", sig); }
int main(void)
{
    signal(SIGINT, myhandler);
    Py_InitializeEx(0);
    PyRun_SimpleString("import signal\n"
                       "old_signal = signal.getsignal(signal.SIGINT)\n"
                       "signal.signal(signal.SIGINT, old_signal)\n"
                       "print(old_signal)\n");
    Py_Finalize();
    return 0;
}
History
Date User Action Args
2022-04-11 14:59:25adminsetgithub: 83619
2020-01-23 20:28:37Steven G. Johnsoncreate