summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGiampaolo Rodola' <g.rodola@gmail.com>2014-04-04 13:34:17 (GMT)
committerGiampaolo Rodola' <g.rodola@gmail.com>2014-04-04 13:34:17 (GMT)
commite09fb7198ae2e4f737db7369006ad19e51699fbf (patch)
tree52a94575cd518a23b79430a0d89c66cbc839a2d5
parentbcc174615ca2bd5fe60442407ca1411759d823cf (diff)
downloadcpython-e09fb7198ae2e4f737db7369006ad19e51699fbf.zip
cpython-e09fb7198ae2e4f737db7369006ad19e51699fbf.tar.gz
cpython-e09fb7198ae2e4f737db7369006ad19e51699fbf.tar.bz2
fix #21076: turn signal module constants into enums
-rw-r--r--Doc/library/signal.rst10
-rw-r--r--Doc/whatsnew/3.5.rst5
-rw-r--r--Lib/signal.py84
-rw-r--r--Lib/test/test_doctest.py2
-rw-r--r--Lib/test/test_signal.py39
-rw-r--r--Modules/signalmodule.c4
-rw-r--r--PC/config.c2
7 files changed, 138 insertions, 8 deletions
diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst
index 84e2836..a97ce66 100644
--- a/Doc/library/signal.rst
+++ b/Doc/library/signal.rst
@@ -65,6 +65,16 @@ Besides, only the main thread is allowed to set a new signal handler.
Module contents
---------------
+.. versionchanged:: 3.5
+ signal (SIG*), handler (:const:`SIG_DFL`, :const:`SIG_IGN`) and sigmask
+ (:const:`SIG_BLOCK`, :const:`SIG_UNBLOCK`, :const:`SIG_SETMASK`)
+ related constants listed below were turned into
+ :class:`enums <enum.IntEnum>`.
+ :func:`getsignal`, :func:`pthread_sigmask`, :func:`sigpending` and
+ :func:`sigwait` functions return human-readable
+ :class:`enums <enum.IntEnum>`.
+
+
The variables defined in the :mod:`signal` module are:
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
index 78bddcb..4218134 100644
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -134,6 +134,11 @@ New Modules
Improved Modules
================
+* Different constants of :mod:`signal` module are now enumeration values using
+ the :mod:`enum` module. This allows meaningful names to be printed during
+ debugging, instead of integer “magic numbers”. (contribute by Giampaolo
+ Rodola' in :issue:`21076`)
+
* :class:`xmlrpc.client.ServerProxy` is now a :term:`context manager`
(contributed by Claudiu Popa in :issue:`20627`).
diff --git a/Lib/signal.py b/Lib/signal.py
new file mode 100644
index 0000000..90e5313
--- /dev/null
+++ b/Lib/signal.py
@@ -0,0 +1,84 @@
+import _signal
+from _signal import *
+from functools import wraps as _wraps
+from enum import IntEnum as _IntEnum
+
+_globals = globals()
+
+Signals = _IntEnum(
+ 'Signals',
+ {name: value for name, value in _globals.items()
+ if name.isupper()
+ and (name.startswith('SIG') and not name.startswith('SIG_'))
+ or name.startswith('CTRL_')})
+
+class Handlers(_IntEnum):
+ SIG_DFL = _signal.SIG_DFL
+ SIG_IGN = _signal.SIG_IGN
+
+_globals.update(Signals.__members__)
+_globals.update(Handlers.__members__)
+
+if 'pthread_sigmask' in _globals:
+ class Sigmasks(_IntEnum):
+ SIG_BLOCK = _signal.SIG_BLOCK
+ SIG_UNBLOCK = _signal.SIG_UNBLOCK
+ SIG_SETMASK = _signal.SIG_SETMASK
+
+ _globals.update(Sigmasks.__members__)
+
+
+def _int_to_enum(value, enum_klass):
+ """Convert a numeric value to an IntEnum member.
+ If it's not a known member, return the numeric value itself.
+ """
+ try:
+ return enum_klass(value)
+ except ValueError:
+ return value
+
+
+def _enum_to_int(value):
+ """Convert an IntEnum member to a numeric value.
+ If it's not a 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):
+ sigs_set = _signal.pthread_sigmask(how, mask)
+ return set(_int_to_enum(x, Signals) for x in sigs_set)
+ pthread_sigmask.__doc__ = _signal.pthread_sigmask.__doc__
+
+
+@_wraps(_signal.sigpending)
+def sigpending():
+ sigs = _signal.sigpending()
+ return set(_int_to_enum(x, Signals) for x in sigs)
+
+
+if 'sigwait' in _globals:
+ @_wraps(_signal.sigwait)
+ def sigwait(sigset):
+ retsig = _signal.sigwait(sigset)
+ return _int_to_enum(retsig, Signals)
+ sigwait.__doc__ = _signal.sigwait
+
+del _globals, _wraps
diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py
index 56193e8..a1029ed 100644
--- a/Lib/test/test_doctest.py
+++ b/Lib/test/test_doctest.py
@@ -2897,7 +2897,7 @@ Invalid doctest option:
def test_main():
# Check the doctest cases in doctest itself:
- support.run_doctest(doctest, verbosity=True)
+ ret = support.run_doctest(doctest, verbosity=True)
# Check the doctest cases defined here:
from test import test_doctest
support.run_doctest(test_doctest, verbosity=True)
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py
index a6f2c64..31e6d37 100644
--- a/Lib/test/test_signal.py
+++ b/Lib/test/test_signal.py
@@ -1,6 +1,7 @@
import unittest
from test import support
from contextlib import closing
+import enum
import gc
import pickle
import select
@@ -39,6 +40,22 @@ def ignoring_eintr(__func, *args, **kwargs):
return None
+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'}:
+ 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")
+
+
@unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
class InterProcessSignalTests(unittest.TestCase):
MAX_DURATION = 20 # Entire test should last at most 20 sec.
@@ -195,6 +212,7 @@ class PosixTests(unittest.TestCase):
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)
@@ -271,7 +289,7 @@ class WakeupSignalTests(unittest.TestCase):
os.close(read)
os.close(write)
- """.format(signals, ordered, test_body)
+ """.format(tuple(map(int, signals)), ordered, test_body)
assert_python_ok('-c', code)
@@ -604,6 +622,8 @@ class PendingSignalsTests(unittest.TestCase):
signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
os.kill(os.getpid(), signum)
pending = signal.sigpending()
+ for sig in pending:
+ assert isinstance(sig, signal.Signals), repr(pending)
if pending != {signum}:
raise Exception('%s != {%s}' % (pending, signum))
try:
@@ -660,6 +680,7 @@ class PendingSignalsTests(unittest.TestCase):
code = '''if 1:
import signal
import sys
+ from signal import Signals
def handler(signum, frame):
1/0
@@ -702,6 +723,7 @@ class PendingSignalsTests(unittest.TestCase):
def test(signum):
signal.alarm(1)
received = signal.sigwait([signum])
+ assert isinstance(received, signal.Signals), received
if received != signum:
raise Exception('received %s, not %s' % (received, signum))
''')
@@ -842,8 +864,14 @@ class PendingSignalsTests(unittest.TestCase):
def kill(signum):
os.kill(os.getpid(), signum)
+ def check_mask(mask):
+ for sig in mask:
+ assert isinstance(sig, signal.Signals), repr(sig)
+
def read_sigmask():
- return signal.pthread_sigmask(signal.SIG_BLOCK, [])
+ sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, [])
+ check_mask(sigmask)
+ return sigmask
signum = signal.SIGUSR1
@@ -852,6 +880,7 @@ class PendingSignalsTests(unittest.TestCase):
# Unblock SIGUSR1 (and copy the old mask) to test our signal handler
old_mask = signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])
+ check_mask(old_mask)
try:
kill(signum)
except ZeroDivisionError:
@@ -861,11 +890,13 @@ class PendingSignalsTests(unittest.TestCase):
# Block and then raise SIGUSR1. The signal is blocked: the signal
# handler is not called, and the signal is now pending
- signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
+ mask = signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
+ check_mask(mask)
kill(signum)
# Check the new mask
blocked = read_sigmask()
+ check_mask(blocked)
if signum not in blocked:
raise Exception("%s not in %s" % (signum, blocked))
if old_mask ^ blocked != {signum}:
@@ -928,7 +959,7 @@ class PendingSignalsTests(unittest.TestCase):
def test_main():
try:
- support.run_unittest(PosixTests, InterProcessSignalTests,
+ support.run_unittest(GenericTests, PosixTests, InterProcessSignalTests,
WakeupFDTests, WakeupSignalTests,
SiginterruptTest, ItimerTest, WindowsSignalTests,
PendingSignalsTests)
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
index fedaddf..c648bde 100644
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -967,7 +967,7 @@ static struct PyModuleDef signalmodule = {
};
PyMODINIT_FUNC
-PyInit_signal(void)
+PyInit__signal(void)
{
PyObject *m, *d, *x;
int i;
@@ -1380,7 +1380,7 @@ PyErr_SetInterrupt(void)
void
PyOS_InitInterrupts(void)
{
- PyObject *m = PyImport_ImportModule("signal");
+ PyObject *m = PyImport_ImportModule("_signal");
if (m) {
Py_DECREF(m);
}
diff --git a/PC/config.c b/PC/config.c
index 72c9381..4a1e719 100644
--- a/PC/config.c
+++ b/PC/config.c
@@ -19,7 +19,7 @@ extern PyObject* PyInit_math(void);
extern PyObject* PyInit__md5(void);
extern PyObject* PyInit_nt(void);
extern PyObject* PyInit__operator(void);
-extern PyObject* PyInit_signal(void);
+extern PyObject* PyInit__signal(void);
extern PyObject* PyInit__sha1(void);
extern PyObject* PyInit__sha256(void);
extern PyObject* PyInit__sha512(void);