[Python-checkins] cpython: fix #21076: turn signal module constants into enums

Brett Cannon brett at python.org
Fri Apr 4 16:12:23 CEST 2014


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.


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-checkins/attachments/20140404/ec3c9b19/attachment-0001.html>


More information about the Python-checkins mailing list