merge along w/ fix for issue #2107 (commit c9239171e429)
This commit is contained in:
commit
815a6f38a6
@ -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:
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
84
Lib/signal.py
Normal 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
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user