Skip to content

Commit 867c435

Browse files
author
Jean-Paul Calderone
committed
merge forward from the python 2.x branch
1 parent 2aa3af4 commit 867c435

7 files changed

Lines changed: 474 additions & 7 deletions

File tree

‎Doc/library/signal.rst‎

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ rules for working with signals and their handlers:
1313
underlying implementation), with the exception of the handler for
1414
:const:`SIGCHLD`, which follows the underlying implementation.
1515

16-
* There is no way to "block" signals temporarily from critical sections (since
17-
this is not supported by all Unix flavors).
18-
1916
* Although Python signal handlers are called asynchronously as far as the Python
2017
user is concerned, they can only occur between the "atomic" instructions of the
2118
Python interpreter. This means that signals arriving during long calculations
@@ -115,6 +112,46 @@ The variables defined in the :mod:`signal` module are:
115112
in user and kernel space. SIGPROF is delivered upon expiration.
116113

117114

115+
.. data:: SIG_BLOCK
116+
117+
A possible value for the *how* parameter to :func:`sigprocmask`
118+
indicating that signals are to be blocked.
119+
120+
.. versionadded:: 2.7
121+
122+
123+
.. data:: SIG_UNBLOCK
124+
125+
A possible value for the *how* parameter to :func:`sigprocmask`
126+
indicating that signals are to be unblocked.
127+
128+
.. versionadded:: 2.7
129+
130+
131+
.. data:: SIG_SETMASK
132+
133+
A possible value for the *how* parameter to :func:`sigprocmask`
134+
indicating that the signal mask is to be replaced.
135+
136+
.. versionadded:: 2.7
137+
138+
139+
.. data:: SFD_CLOEXEC
140+
141+
A possible flag in the *flags* parameter to :func:`signalfd` which causes
142+
the new file descriptor to be marked as close-on-exec.
143+
144+
.. versionadded:: 2.7
145+
146+
147+
.. data:: SFD_NONBLOCK
148+
149+
A possible flag in the *flags* parameter to :func:`signalfd` which causes
150+
the new file description to be set non-blocking.
151+
152+
.. versionadded:: 2.7
153+
154+
118155
The :mod:`signal` module defines one exception:
119156

120157
.. exception:: ItimerError
@@ -227,6 +264,44 @@ The :mod:`signal` module defines the following functions:
227264
attribute descriptions in the :mod:`inspect` module).
228265

229266

267+
.. function:: signalfd(fd, mask[, flags])
268+
269+
Create a new file descriptor on which to receive signals or modify the
270+
mask of such a file descriptor previously created by this function.
271+
Availability: Linux (See the manpage :manpage:`signalfd(2)` for further
272+
information).
273+
274+
If *fd* is ``-1``, a new file descriptor will be created. Otherwise,
275+
*fd* must be a file descriptor previously returned by this function.
276+
277+
*mask* is a list of signal numbers which will trigger data on this file
278+
descriptor.
279+
280+
*flags* is a bit mask which may include any :const:`signal.SFD_*` flag.
281+
282+
.. versionadded:: 2.7
283+
284+
285+
.. function:: sigprocmask(how, mask)
286+
287+
Set the signal mask for the process. The old signal mask is returned.
288+
Availability: Unix (See the Unix man page :manpage:`sigprocmask(2)` and
289+
:manpage:`pthread_sigmask(2)`.)
290+
291+
If *how* is :const:`signal.SIG_BLOCK`, the signals in the mask are added
292+
to the set of blocked signals.
293+
294+
If *how* is :const:`signal.SIG_UNBLOCK`, the signals in the mask are
295+
removed from the set of blocked signals.
296+
297+
If *how* is :const:`signal.SIG_SETMASK`, the signals in the mask are set
298+
as blocked and the signals not in the mask are set as unblocked.
299+
300+
*mask* is a list of signal numbers (eg :const:`signal.SIGUSR1`).
301+
302+
.. versionadded:: 2.7
303+
304+
230305
.. _signal-example:
231306

232307
Example

‎Lib/test/test_signal.py‎

Lines changed: 203 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,9 +462,210 @@ def test_itimer_prof(self):
462462
# and the handler should have been called
463463
self.assertEqual(self.hndl_called, True)
464464

465+
466+
467+
class SomeException(Exception):
468+
"""
469+
A unique exception class to be raised by a signal handler to verify that the
470+
signal handler was invoked.
471+
"""
472+
473+
474+
475+
def raiser(*args):
476+
"""A signal handler which raises SomeException."""
477+
raise SomeException()
478+
479+
480+
481+
class SigprocmaskTests(unittest.TestCase):
482+
"""Tests for sigprocmask."""
483+
def _handle_sigusr1(self):
484+
old_handler = signal.signal(signal.SIGUSR1, raiser)
485+
self.addCleanup(signal.signal, signal.SIGUSR1, old_handler)
486+
487+
488+
def test_signature(self):
489+
"""When invoked with other than two arguments, sigprocmask raises
490+
TypeError.
491+
"""
492+
self.assertRaises(TypeError, signal.sigprocmask)
493+
self.assertRaises(TypeError, signal.sigprocmask, 1)
494+
self.assertRaises(TypeError, signal.sigprocmask, 1, 2, 3)
495+
496+
497+
def test_invalid_how(self):
498+
"""If a value other than SIG_BLOCK, SIG_UNBLOCK, or SIG_SETMASK is
499+
passed for the how argument to sigprocmask, ValueError is raised.
500+
"""
501+
message = "value specified for how \(1700\) invalid"
502+
with self.assertRaisesRegexp(ValueError, message):
503+
signal.sigprocmask(1700, [])
504+
505+
506+
def test_invalid_signal_iterable(self):
507+
"""If iterating over the value passed for the signals parameter to
508+
sigprocmask raises an exception, sigprocmask raises that exception.
509+
"""
510+
class BrokenIter(object):
511+
def __iter__(self):
512+
raise RuntimeError("my __iter__ is broken")
513+
with self.assertRaisesRegexp(RuntimeError, "my __iter__ is broken"):
514+
signal.sigprocmask(signal.SIG_BLOCK, BrokenIter())
515+
516+
517+
def test_invalid_signal(self):
518+
"""If an object in the iterable passed for the signals parameter to
519+
sigprocmask isn't an integer, TypeError is raised."""
520+
with self.assertRaisesRegexp(TypeError, "an integer is required"):
521+
signal.sigprocmask(signal.SIG_BLOCK, [object()])
522+
523+
524+
def test_return_previous_mask(self):
525+
"""sigprocmask returns a list of the signals previously masked.
526+
"""
527+
previous = signal.sigprocmask(signal.SIG_BLOCK, [1, 3, 5])
528+
result = signal.sigprocmask(signal.SIG_BLOCK, previous)
529+
self.assertEquals(result, [1, 3, 5])
530+
531+
532+
def test_block(self):
533+
"""When invoked with SIG_BLOCK, sigprocmask blocks the signals in the
534+
sigmask list.
535+
"""
536+
self._handle_sigusr1()
537+
previous = signal.sigprocmask(signal.SIG_BLOCK, [signal.SIGUSR1])
538+
os.kill(os.getpid(), signal.SIGUSR1)
539+
with self.assertRaises(SomeException):
540+
# Expect to receive SIGUSR1 after unblocking it.
541+
signal.sigprocmask(signal.SIG_SETMASK, previous)
542+
543+
544+
def test_unblock(self):
545+
"""When invoked with SIG_UNBLOCK, sigprocmask unblocks the signals in
546+
the sigmask list.
547+
"""
548+
self._handle_sigusr1()
549+
previous = signal.sigprocmask(signal.SIG_BLOCK, [signal.SIGUSR1])
550+
self.addCleanup(signal.sigprocmask, signal.SIG_SETMASK, previous)
551+
signal.sigprocmask(signal.SIG_UNBLOCK, [signal.SIGUSR1])
552+
553+
with self.assertRaises(SomeException):
554+
os.kill(os.getpid(), signal.SIGUSR1)
555+
556+
557+
def test_long_signals(self):
558+
"""sigprocmask accepts signal numbers as instances of long."""
559+
previous = signal.sigprocmask(
560+
signal.SIG_SETMASK, [long(signal.SIGUSR1), long(signal.SIGUSR2)])
561+
masked = signal.sigprocmask(signal.SIG_SETMASK, previous)
562+
self.assertEquals(masked, [signal.SIGUSR1, signal.SIGUSR2])
563+
564+
565+
566+
class SignalfdTests(unittest.TestCase):
567+
"""
568+
Tests for signal.signalfd.
569+
"""
570+
def test_signature(self):
571+
"""When invoked with fewer than two arguments or more than three,
572+
signalfd raises TypeError.
573+
"""
574+
self.assertRaises(TypeError, signal.signalfd)
575+
self.assertRaises(TypeError, signal.signalfd, 1)
576+
self.assertRaises(TypeError, signal.signalfd, 1, 2, 3, 4)
577+
578+
579+
def test_create_signalfd(self):
580+
"""When invoked with a file descriptor of -1, signalfd allocates a new
581+
file descriptor for signal information delivery and returns it.
582+
"""
583+
fd = signal.signalfd(-1, [])
584+
self.assertTrue(isinstance(fd, int))
585+
os.close(fd)
586+
587+
588+
def test_non_iterable_signals(self):
589+
"""If an object which is not iterable is passed for the sigmask list
590+
argument to signalfd, the exception raised by trying to iterate over
591+
that object is raised.
592+
"""
593+
self.assertRaises(TypeError, signal.signalfd, -1, object())
594+
595+
596+
def test_non_integer_signals(self):
597+
"""If any non-integer values are included in the sigmask list argument
598+
to signalfd, the exception raised by the attempt to convert them to an
599+
integer is raised.
600+
"""
601+
self.assertRaises(TypeError, signal.signalfd, -1, [object()])
602+
603+
604+
def test_out_of_range_signal(self):
605+
"""If a signal number that is out of the valid range is included in the
606+
sigmask list argument to signalfd, ValueError is raised.
607+
"""
608+
message = "signal number -2 out of range"
609+
with self.assertRaisesRegexp(ValueError, message):
610+
signal.signalfd(-1, [-2])
611+
612+
613+
def test_handle_signals(self):
614+
"""After signalfd is called, if a signal is received which was in the
615+
sigmask list passed to that call, information about the signal can be
616+
read from the fd returned by that call.
617+
"""
618+
fd = signal.signalfd(-1, [signal.SIGUSR2])
619+
self.addCleanup(os.close, fd)
620+
previous = signal.sigprocmask(signal.SIG_BLOCK, [signal.SIGUSR2])
621+
self.addCleanup(signal.sigprocmask, signal.SIG_SETMASK, previous)
622+
os.kill(os.getpid(), signal.SIGUSR2)
623+
bytes = os.read(fd, 128)
624+
self.assertTrue(bytes)
625+
626+
627+
def test_close_on_exec(self):
628+
"""If the bit mask passed as the 3rd argument to signalfd includes
629+
SFD_CLOEXEC, the returned file descriptor has FD_CLOEXEC set on it.
630+
"""
631+
import fcntl
632+
fd = signal.signalfd(-1, [], signal.SFD_CLOEXEC)
633+
self.addCleanup(os.close, fd)
634+
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
635+
self.assertTrue(flags & fcntl.FD_CLOEXEC)
636+
637+
638+
def test_nonblocking(self):
639+
"""If the bit mask passed as the 3rd argument to signalfd includes
640+
SFD_NOBLOCK, the file description referenced by the returned file
641+
descriptor has O_NONBLOCK set on it.
642+
"""
643+
import fcntl
644+
fd = signal.signalfd(-1, [], signal.SFD_NONBLOCK)
645+
self.addCleanup(os.close, fd)
646+
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
647+
self.assertTrue(flags & os.O_NONBLOCK)
648+
649+
650+
def test_default_flags(self):
651+
"""If an empty bit mask is passed as the 3rd argument to signalfd,
652+
neither FD_CLOEXEC nor O_NONBLOCK is set on the resulting file
653+
descriptor/description.
654+
"""
655+
import fcntl
656+
fd = signal.signalfd(-1, [])
657+
self.addCleanup(os.close, fd)
658+
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
659+
self.assertFalse(flags & fcntl.FD_CLOEXEC)
660+
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
661+
self.assertFalse(flags & os.O_NONBLOCK)
662+
663+
465664
def test_main():
466-
support.run_unittest(BasicSignalTests, InterProcessSignalTests,
467-
WakeupSignalTests, SiginterruptTest, ItimerTest)
665+
support.run_unittest(
666+
BasicSignalTests, InterProcessSignalTests,
667+
WakeupSignalTests, SiginterruptTest, ItimerTest, SignalfdTests,
668+
SigprocmaskTests)
468669

469670

470671
if __name__ == "__main__":

‎Misc/NEWS‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,11 @@ Library
10251025
- Issue #5949: added check for correct lineends in input from IMAP server
10261026
in imaplib.
10271027

1028+
- Issue #8407: The signal module gains the ``signalfd()`` and
1029+
``sigprocmask(2)`` functions providing access to the signalfd(2) and
1030+
sigprocmask(2) system calls respectively on Linux systems which implement
1031+
them.
1032+
10281033
- Add count() and reverse() methods to collections.deque().
10291034

10301035
- Fix variations of extending deques: d.extend(d) d.extendleft(d) d+=d

0 commit comments

Comments
 (0)