[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