merge along w/ fix for issue #2107 (commit c9239171e429)

This commit is contained in:
Brett Cannon 2014-04-04 10:20:28 -04:00
commit 815a6f38a6
7 changed files with 139 additions and 9 deletions

View File

@ -65,6 +65,16 @@ Besides, only the main thread is allowed to set a new signal handler.
Module contents 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: The variables defined in the :mod:`signal` module are:

View File

@ -134,6 +134,11 @@ New Modules
Improved 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` * :class:`xmlrpc.client.ServerProxy` is now a :term:`context manager`
(contributed by Claudiu Popa in :issue:`20627`). (contributed by Claudiu Popa in :issue:`20627`).

84
Lib/signal.py Normal file
View File

@ -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

View File

@ -2897,7 +2897,7 @@ Invalid doctest option:
def test_main(): def test_main():
# Check the doctest cases in doctest itself: # 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: # Check the doctest cases defined here:
from test import test_doctest from test import test_doctest
support.run_doctest(test_doctest, verbosity=True) support.run_doctest(test_doctest, verbosity=True)

View File

@ -1,6 +1,7 @@
import unittest import unittest
from test import support from test import support
from contextlib import closing from contextlib import closing
import enum
import gc import gc
import pickle import pickle
import select import select
@ -39,6 +40,22 @@ def ignoring_eintr(__func, *args, **kwargs):
return None 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") @unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
class InterProcessSignalTests(unittest.TestCase): class InterProcessSignalTests(unittest.TestCase):
MAX_DURATION = 20 # Entire test should last at most 20 sec. MAX_DURATION = 20 # Entire test should last at most 20 sec.
@ -195,6 +212,7 @@ class PosixTests(unittest.TestCase):
def test_getsignal(self): def test_getsignal(self):
hup = signal.signal(signal.SIGHUP, self.trivial_signal_handler) hup = signal.signal(signal.SIGHUP, self.trivial_signal_handler)
self.assertIsInstance(hup, signal.Handlers)
self.assertEqual(signal.getsignal(signal.SIGHUP), self.assertEqual(signal.getsignal(signal.SIGHUP),
self.trivial_signal_handler) self.trivial_signal_handler)
signal.signal(signal.SIGHUP, hup) signal.signal(signal.SIGHUP, hup)
@ -271,7 +289,7 @@ class WakeupSignalTests(unittest.TestCase):
os.close(read) os.close(read)
os.close(write) os.close(write)
""".format(signals, ordered, test_body) """.format(tuple(map(int, signals)), ordered, test_body)
assert_python_ok('-c', code) assert_python_ok('-c', code)
@ -604,6 +622,8 @@ class PendingSignalsTests(unittest.TestCase):
signal.pthread_sigmask(signal.SIG_BLOCK, [signum]) signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
os.kill(os.getpid(), signum) os.kill(os.getpid(), signum)
pending = signal.sigpending() pending = signal.sigpending()
for sig in pending:
assert isinstance(sig, signal.Signals), repr(pending)
if pending != {signum}: if pending != {signum}:
raise Exception('%s != {%s}' % (pending, signum)) raise Exception('%s != {%s}' % (pending, signum))
try: try:
@ -660,6 +680,7 @@ class PendingSignalsTests(unittest.TestCase):
code = '''if 1: code = '''if 1:
import signal import signal
import sys import sys
from signal import Signals
def handler(signum, frame): def handler(signum, frame):
1/0 1/0
@ -702,6 +723,7 @@ class PendingSignalsTests(unittest.TestCase):
def test(signum): def test(signum):
signal.alarm(1) signal.alarm(1)
received = signal.sigwait([signum]) received = signal.sigwait([signum])
assert isinstance(received, signal.Signals), received
if received != signum: if received != signum:
raise Exception('received %s, not %s' % (received, signum)) raise Exception('received %s, not %s' % (received, signum))
''') ''')
@ -842,8 +864,14 @@ class PendingSignalsTests(unittest.TestCase):
def kill(signum): def kill(signum):
os.kill(os.getpid(), signum) os.kill(os.getpid(), signum)
def check_mask(mask):
for sig in mask:
assert isinstance(sig, signal.Signals), repr(sig)
def read_sigmask(): 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 signum = signal.SIGUSR1
@ -852,6 +880,7 @@ class PendingSignalsTests(unittest.TestCase):
# Unblock SIGUSR1 (and copy the old mask) to test our signal handler # Unblock SIGUSR1 (and copy the old mask) to test our signal handler
old_mask = signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum]) old_mask = signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])
check_mask(old_mask)
try: try:
kill(signum) kill(signum)
except ZeroDivisionError: except ZeroDivisionError:
@ -861,11 +890,13 @@ class PendingSignalsTests(unittest.TestCase):
# Block and then raise SIGUSR1. The signal is blocked: the signal # Block and then raise SIGUSR1. The signal is blocked: the signal
# handler is not called, and the signal is now pending # 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) kill(signum)
# Check the new mask # Check the new mask
blocked = read_sigmask() blocked = read_sigmask()
check_mask(blocked)
if signum not in blocked: if signum not in blocked:
raise Exception("%s not in %s" % (signum, blocked)) raise Exception("%s not in %s" % (signum, blocked))
if old_mask ^ blocked != {signum}: if old_mask ^ blocked != {signum}:
@ -928,7 +959,7 @@ class PendingSignalsTests(unittest.TestCase):
def test_main(): def test_main():
try: try:
support.run_unittest(PosixTests, InterProcessSignalTests, support.run_unittest(GenericTests, PosixTests, InterProcessSignalTests,
WakeupFDTests, WakeupSignalTests, WakeupFDTests, WakeupSignalTests,
SiginterruptTest, ItimerTest, WindowsSignalTests, SiginterruptTest, ItimerTest, WindowsSignalTests,
PendingSignalsTests) PendingSignalsTests)

View File

@ -956,7 +956,7 @@ the first is the signal number, the second is the interrupted stack frame.");
static struct PyModuleDef signalmodule = { static struct PyModuleDef signalmodule = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
"signal", "_signal",
module_doc, module_doc,
-1, -1,
signal_methods, signal_methods,
@ -967,7 +967,7 @@ static struct PyModuleDef signalmodule = {
}; };
PyMODINIT_FUNC PyMODINIT_FUNC
PyInit_signal(void) PyInit__signal(void)
{ {
PyObject *m, *d, *x; PyObject *m, *d, *x;
int i; int i;
@ -1380,7 +1380,7 @@ PyErr_SetInterrupt(void)
void void
PyOS_InitInterrupts(void) PyOS_InitInterrupts(void)
{ {
PyObject *m = PyImport_ImportModule("signal"); PyObject *m = PyImport_ImportModule("_signal");
if (m) { if (m) {
Py_DECREF(m); Py_DECREF(m);
} }

View File

@ -19,7 +19,7 @@ extern PyObject* PyInit_math(void);
extern PyObject* PyInit__md5(void); extern PyObject* PyInit__md5(void);
extern PyObject* PyInit_nt(void); extern PyObject* PyInit_nt(void);
extern PyObject* PyInit__operator(void); extern PyObject* PyInit__operator(void);
extern PyObject* PyInit_signal(void); extern PyObject* PyInit__signal(void);
extern PyObject* PyInit__sha1(void); extern PyObject* PyInit__sha1(void);
extern PyObject* PyInit__sha256(void); extern PyObject* PyInit__sha256(void);
extern PyObject* PyInit__sha512(void); extern PyObject* PyInit__sha512(void);