Python interface to signal handlers

In this module, we distinguish between the “OS-level” signal handler and the “Python-level” signal handler.

The Python function signal.signal() sets both of these: it sets the Python-level signal handler to the function specified by the user. It also sets the OS-level signal handler to a specific C function which calls the Python-level signal handler.

The Python signal module does not allow access to the OS-level signal handler (in particular, it does not allow one to temporarily change a signal handler if the OS-level handler was not the Python one).

class cysignals.pysignals.SigAction

An opaque object representing an OS-level signal handler.

The only legal initializers are signal.SIG_DFL (the default), signal.SIG_IGN and another SigAction object (which is copied).

EXAMPLES:

>>> from cysignals.pysignals import SigAction
>>> SigAction()
<SigAction with sa_handler=SIG_DFL>
>>> import signal
>>> SigAction(signal.SIG_DFL)
<SigAction with sa_handler=SIG_DFL>
>>> SigAction(signal.SIG_IGN)
<SigAction with sa_handler=SIG_IGN>
>>> A = SigAction(signal.SIG_IGN)
>>> SigAction(A)
<SigAction with sa_handler=SIG_IGN>
>>> SigAction(A) == A
True

TESTS:

>>> SigAction(42)
Traceback (most recent call last):
...
TypeError: cannot initialize SigAction from <... 'int'>
class cysignals.pysignals.changesignal(sig, action)

Context to temporarily change a signal handler.

This should be used as follows:

with changesignal(sig, action):
    ...

Inside the context, code behaves as if signal.signal(sig, action) was called. When leaving the context, the signal handler is restored to what it was before. Both the Python-level and OS-level signal handlers are restored.

EXAMPLES:

>>> from cysignals.pysignals import changesignal
>>> import os, signal
>>> def handler(*args): print("got signal")
>>> _ = signal.signal(signal.SIGQUIT, signal.SIG_IGN)
>>> with changesignal(signal.SIGQUIT, handler):
...     os.kill(os.getpid(), signal.SIGQUIT)
got signal
>>> os.kill(os.getpid(), signal.SIGQUIT)
>>> with changesignal(signal.SIGQUIT, handler):
...     setossignal(signal.SIGQUIT, signal.SIG_DFL)
...     raise Exception("just testing")
Traceback (most recent call last):
...
Exception: just testing
>>> os.kill(os.getpid(), signal.SIGQUIT)
cysignals.pysignals.getossignal(sig)

Get the OS-level signal handler.

This returns an opaque object of type SigAction which can only be used in a future call to setossignal().

EXAMPLES:

>>> from cysignals.pysignals import getossignal
>>> import signal
>>> getossignal(signal.SIGINT)
<SigAction with sa_handler=0x...>
>>> getossignal(signal.SIGUSR1)
<SigAction with sa_handler=SIG_DFL>
>>> def handler(*args): pass
>>> _ = signal.signal(signal.SIGUSR1, handler)
>>> getossignal(signal.SIGUSR1)
<SigAction with sa_handler=0x...>

TESTS:

>>> getossignal(None)
Traceback (most recent call last):
...
TypeError: an integer is required
>>> getossignal(-1)
Traceback (most recent call last):
...
OSError: [Errno 22] Invalid argument
cysignals.pysignals.setossignal(sig, action)

Set the OS-level signal handler to action, which should either be signal.SIG_DFL or signal.SIG_IGN or a SigAction object returned by an earlier call to getossignal() or setossignal().

Return the old signal handler.

EXAMPLES:

>>> from cysignals.pysignals import setossignal
>>> import os, signal
>>> def handler(*args): print("got signal")
>>> _ = signal.signal(signal.SIGHUP, handler)
>>> os.kill(os.getpid(), signal.SIGHUP)
got signal
>>> pyhandler = setossignal(signal.SIGHUP, signal.SIG_IGN)
>>> pyhandler
<SigAction with sa_handler=0x...>
>>> os.kill(os.getpid(), signal.SIGHUP)
>>> setossignal(signal.SIGHUP, pyhandler)
<SigAction with sa_handler=SIG_IGN>
>>> os.kill(os.getpid(), signal.SIGHUP)
got signal
>>> setossignal(signal.SIGHUP, signal.SIG_DFL) == pyhandler
True

TESTS:

>>> setossignal(signal.SIGHUP, None)
Traceback (most recent call last):
...
TypeError: cannot initialize SigAction from <... 'NoneType'>
>>> setossignal(-1, signal.SIG_DFL)
Traceback (most recent call last):
...
OSError: [Errno 22] Invalid argument
cysignals.pysignals.setsignal(sig, action, osaction=None)

Set the Python-level signal handler for signal sig to action. If osaction is given, set the OS-level signal handler to osaction. If osaction is None (the default), change only the Python-level handler and keep the OS-level handler.

Return the old Python-level handler.

EXAMPLES:

>>> from cysignals.pysignals import *
>>> def handler(*args): print("got signal")
>>> _ = signal.signal(signal.SIGSEGV, handler)
>>> A = getossignal(signal.SIGILL)
>>> _ = setsignal(signal.SIGILL, getsignal(signal.SIGSEGV))
>>> getossignal(signal.SIGILL) == A
True
>>> _ = setossignal(signal.SIGILL, getossignal(signal.SIGSEGV))
>>> import os
>>> os.kill(os.getpid(), signal.SIGILL)
got signal
>>> setsignal(signal.SIGILL, signal.SIG_DFL)
<function handler at 0x...>
>>> _ = setsignal(signal.SIGALRM, signal.SIG_DFL, signal.SIG_IGN)
>>> os.kill(os.getpid(), signal.SIGALRM)
>>> _ = setsignal(signal.SIGALRM, handler, getossignal(signal.SIGSEGV))
>>> os.kill(os.getpid(), signal.SIGALRM)
got signal

TESTS:

>>> setsignal(-1, signal.SIG_DFL)
Traceback (most recent call last):
...
OSError: [Errno 22] Invalid argument