[Python-Dev] [Python-checkins] cpython: fix #21076: turn signal module constants into enums
Brett Cannon
brett at python.org
Fri Apr 4 16:21:39 CEST 2014
On Fri, Apr 4, 2014 at 10:12 AM, Brett Cannon <brett at python.org> wrote:
> This broke compilation on at least OS X, but I'm willing to bet for all
> UNIX-based systems. I have a fix in the works.
>
Fix is in rev c6e63bb132fb <http://hg.python.org/cpython/rev/c6e63bb132fb>.
>
>
> On Fri, Apr 4, 2014 at 9:34 AM, giampaolo.rodola <
> python-checkins at python.org> wrote:
>
>> http://hg.python.org/cpython/rev/c9239171e429
>> changeset: 90128:c9239171e429
>> user: Giampaolo Rodola' <g.rodola at gmail.com>
>> date: Fri Apr 04 15:34:17 2014 +0200
>> summary:
>> fix #21076: turn signal module constants into enums
>>
>> files:
>> Doc/library/signal.rst | 10 +++
>> Doc/whatsnew/3.5.rst | 5 +
>> Lib/signal.py | 84 ++++++++++++++++++++++++++++
>> Lib/test/test_doctest.py | 2 +-
>> Lib/test/test_signal.py | 39 +++++++++++-
>> Modules/signalmodule.c | 4 +-
>> PC/config.c | 2 +-
>> 7 files changed, 138 insertions(+), 8 deletions(-)
>>
>>
>> diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst
>> --- a/Doc/library/signal.rst
>> +++ b/Doc/library/signal.rst
>> @@ -65,6 +65,16 @@
>> 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
>> --- a/Doc/whatsnew/3.5.rst
>> +++ b/Doc/whatsnew/3.5.rst
>> @@ -134,6 +134,11 @@
>> 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
>> --- /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
>> +
>> +
>> + at _wraps(_signal.signal)
>> +def signal(signalnum, handler):
>> + handler = _signal.signal(_enum_to_int(signalnum),
>> _enum_to_int(handler))
>> + return _int_to_enum(handler, Handlers)
>> +
>> +
>> + at _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__
>> +
>> +
>> + at _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
>> --- a/Lib/test/test_doctest.py
>> +++ b/Lib/test/test_doctest.py
>> @@ -2897,7 +2897,7 @@
>>
>> 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
>> --- 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 @@
>> 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 @@
>>
>> 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 @@
>>
>> 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 @@
>> 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 @@
>> code = '''if 1:
>> import signal
>> import sys
>> + from signal import Signals
>>
>> def handler(signum, frame):
>> 1/0
>> @@ -702,6 +723,7 @@
>> 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 @@
>> 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 @@
>>
>> # 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 @@
>>
>> # 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 @@
>>
>> 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
>> --- a/Modules/signalmodule.c
>> +++ b/Modules/signalmodule.c
>> @@ -967,7 +967,7 @@
>> };
>>
>> PyMODINIT_FUNC
>> -PyInit_signal(void)
>> +PyInit__signal(void)
>> {
>> PyObject *m, *d, *x;
>> int i;
>> @@ -1380,7 +1380,7 @@
>> 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
>> --- a/PC/config.c
>> +++ b/PC/config.c
>> @@ -19,7 +19,7 @@
>> 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);
>>
>> --
>> Repository URL: http://hg.python.org/cpython
>>
>> _______________________________________________
>> Python-checkins mailing list
>> Python-checkins at python.org
>> https://mail.python.org/mailman/listinfo/python-checkins
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20140404/ff31ca9e/attachment-0001.html>
More information about the Python-Dev
mailing list