Skip to content

bpo-23325: Turn signal.SIG_DFL and signal.SIG_IGN into functions. #8920

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 0 additions & 26 deletions Lib/signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@
and (name.startswith('SIG') and not name.startswith('SIG_'))
or name.startswith('CTRL_'))

_IntEnum._convert_(
'Handlers', __name__,
lambda name: name in ('SIG_DFL', 'SIG_IGN'))

if 'pthread_sigmask' in _globals:
_IntEnum._convert_(
'Sigmasks', __name__,
Expand All @@ -32,28 +28,6 @@ def _int_to_enum(value, enum_klass):
return value


def _enum_to_int(value):
"""Convert an IntEnum member to a numeric value.
If it's not an IntEnum member return the value itself.
"""
try:
return int(value)
except (ValueError, TypeError):
return value


@_wraps(_signal.signal)
def signal(signalnum, handler):
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
return _int_to_enum(handler, Handlers)


@_wraps(_signal.getsignal)
def getsignal(signalnum):
handler = _signal.getsignal(signalnum)
return _int_to_enum(handler, Handlers)


if 'pthread_sigmask' in _globals:
@_wraps(_signal.pthread_sigmask)
def pthread_sigmask(how, mask):
Expand Down
26 changes: 22 additions & 4 deletions Lib/test/test_signal.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import copy
import errno
import os
import pickle
import random
import signal
import socket
Expand All @@ -21,16 +23,33 @@ class GenericTests(unittest.TestCase):
def test_enums(self):
for name in dir(signal):
sig = getattr(signal, name)
if name in {'SIG_DFL', 'SIG_IGN'}:
self.assertIsInstance(sig, signal.Handlers)
elif name in {'SIG_BLOCK', 'SIG_UNBLOCK', 'SIG_SETMASK'}:
if name in {'SIG_BLOCK', 'SIG_UNBLOCK', 'SIG_SETMASK'}:
self.assertIsInstance(sig, signal.Sigmasks)
elif name.startswith('SIG') and not name.startswith('SIG_'):
self.assertIsInstance(sig, signal.Signals)
elif name.startswith('CTRL_'):
self.assertIsInstance(sig, signal.Signals)
self.assertEqual(sys.platform, "win32")

def test_standard_handlers(self):
for name in 'SIG_DFL', 'SIG_IGN':
with self.subTest(name):
handler = getattr(signal, name)
self.assertIn(name, repr(handler))
self.assertNotEqual(handler.__doc__, type(handler).__doc__)

self.assertIs(copy.copy(handler), handler)
self.assertIs(copy.deepcopy(handler), handler)
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
p = pickle.dumps(handler, proto)
self.assertIs(pickle.loads(p), handler)

old_handler = signal.signal(signal.SIGINT, handler)
try:
self.assertIs(signal.getsignal(signal.SIGINT), handler)
finally:
signal.signal(signal.SIGINT, old_handler)


@unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
class PosixTests(unittest.TestCase):
Expand All @@ -51,7 +70,6 @@ def test_setting_signal_handler_to_none_raises_error(self):

def test_getsignal(self):
hup = signal.signal(signal.SIGHUP, self.trivial_signal_handler)
self.assertIsInstance(hup, signal.Handlers)
self.assertEqual(signal.getsignal(signal.SIGHUP),
self.trivial_signal_handler)
signal.signal(signal.SIGHUP, hup)
Expand Down
16 changes: 3 additions & 13 deletions Lib/unittest/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,9 @@ class _InterruptHandler(object):
def __init__(self, default_handler):
self.called = False
self.original_handler = default_handler
if isinstance(default_handler, int):
if default_handler == signal.SIG_DFL:
# Pretend it's signal.default_int_handler instead.
default_handler = signal.default_int_handler
elif default_handler == signal.SIG_IGN:
# Not quite the same thing as SIG_IGN, but the closest we
# can make it: do nothing.
def default_handler(unused_signum, unused_frame):
pass
else:
raise TypeError("expected SIGINT signal handler to be "
"signal.SIG_IGN, signal.SIG_DFL, or a "
"callable object")
if default_handler == signal.SIG_DFL:
# Pretend it's signal.default_int_handler instead.
default_handler = signal.default_int_handler
self.default_handler = default_handler

def __call__(self, signum, frame):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Singletons :data:`~signal.SIG_DFL` and :data:`~signal.SIG_IGN` are no longer
integers. They are now copyable and pickleable and have docstrings.
68 changes: 67 additions & 1 deletion Modules/clinic/signalmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 44 additions & 10 deletions Modules/signalmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1261,6 +1261,42 @@ signal_pthread_kill_impl(PyObject *module, unsigned long thread_id,
#endif /* #if defined(HAVE_PTHREAD_KILL) */


/*[clinic input]
signal.SIG_DFL
signalnum: int
frame: object
/

Standard signal handler used to refer to the system default handler.
[clinic start generated code]*/

static PyObject *
signal_SIG_DFL_impl(PyObject *module, int signalnum, PyObject *frame)
/*[clinic end generated code: output=7293f25d5f7a3415 input=9df97af30cd58214]*/
{
PyErr_SetString(PyExc_TypeError,
"system default handler can't be called directly");
return NULL;
}


/*[clinic input]
signal.SIG_IGN
signalnum: int
frame: object
/

Standard signal handler used to ignore the given signal.
[clinic start generated code]*/

static PyObject *
signal_SIG_IGN_impl(PyObject *module, int signalnum, PyObject *frame)
/*[clinic end generated code: output=8b52aad79325460a input=1b49e704cb42377a]*/
{
Py_RETURN_NONE;
}


#if defined(__linux__) && defined(__NR_pidfd_send_signal)
/*[clinic input]
signal.pidfd_send_signal
Expand Down Expand Up @@ -1318,6 +1354,8 @@ static PyMethodDef signal_methods[] = {
#if defined(HAVE_SIGFILLSET) || defined(MS_WINDOWS)
SIGNAL_VALID_SIGNALS_METHODDEF
#endif
SIGNAL_SIG_DFL_METHODDEF
SIGNAL_SIG_IGN_METHODDEF
{NULL, NULL} /* sentinel */
};

Expand All @@ -1337,8 +1375,6 @@ pause() -- wait until a signal arrives [Unix only]\n\
default_int_handler() -- default SIGINT handler\n\
\n\
signal constants:\n\
SIG_DFL -- used to refer to the system default handler\n\
SIG_IGN -- used to ignore the signal\n\
NSIG -- number of defined signals\n\
SIGINT, SIGTERM, etc. -- signal numbers\n\
\n\
Expand Down Expand Up @@ -1394,17 +1430,15 @@ PyInit__signal(void)
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);

DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL);
if (!DefaultHandler ||
PyDict_SetItemString(d, "SIG_DFL", DefaultHandler) < 0) {
DefaultHandler = PyDict_GetItemString(d, "SIG_DFL");
if (!DefaultHandler)
goto finally;
}
Py_INCREF(DefaultHandler);

IgnoreHandler = PyLong_FromVoidPtr((void *)SIG_IGN);
if (!IgnoreHandler ||
PyDict_SetItemString(d, "SIG_IGN", IgnoreHandler) < 0) {
IgnoreHandler = PyDict_GetItemString(d, "SIG_IGN");
if (!IgnoreHandler)
goto finally;
}
Py_INCREF(IgnoreHandler);

if (PyModule_AddIntMacro(m, NSIG))
goto finally;
Expand Down