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 anotherSigAction
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¶
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)
- class cysignals.pysignals.containsignals¶
Context to revert any changes to given signal handlers and block those signals.
This should be used as follows:
with containsignals(signals): ...
where
signals
is a list of signals (by default, all signals numbered from 1 to 31 except forSIGKILL
andSIGSTOP
, which cannot be handled).When entering the context, the current handlers of those signals are saved. They are restored when exiting the context. This is mainly meant to prevent unwanted changes to signal handlers that other code may make. Both the Python-level and OS-level signal handlers are saved and restored.
Also, the signals from the list
signals
are blocked. So any newly-installed signal handlers are prevented from being triggered.EXAMPLES:
>>> from cysignals.pysignals import containsignals >>> import os, signal >>> def handler(*args): ... print("got signal") >>> _ = signal.signal(signal.SIGBUS, handler) >>> with containsignals([signal.SIGBUS]): ... _ = signal.signal(signal.SIGBUS, signal.SIG_DFL) ... # This signal is delivered when exiting the context ... os.kill(os.getpid(), signal.SIGBUS) ... print("no signal yet") no signal yet got signal
The same example but now containing all signals:
>>> with containsignals() as C: ... print("blocked {0} signals".format(len(C.oldhandlers))) ... _ = signal.signal(signal.SIGBUS, signal.SIG_DFL) ... # This signal is delivered when exiting the context ... os.kill(os.getpid(), signal.SIGBUS) ... print("no signal yet") blocked 29 signals no signal yet got signal
This time, we send a signal which is not contained. We set a new handler, which is not blocked or changed by the context:
>>> def fancyhandler(*args): ... print("fancy!") >>> with containsignals([signal.SIGINT]): ... _ = signal.signal(signal.SIGBUS, fancyhandler) ... os.kill(os.getpid(), signal.SIGBUS) fancy! >>> os.kill(os.getpid(), signal.SIGBUS) fancy!
- 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 tosetossignal()
.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...>
Check whether a signal is handled by the Python signal handler:
>>> from cysignals.pysignals import python_os_handler >>> getossignal(signal.SIGUSR1) == python_os_handler True >>> _ = signal.signal(signal.SIGUSR1, signal.SIG_IGN) >>> getossignal(signal.SIGUSR1) == python_os_handler False >>> getossignal(signal.SIGABRT) == python_os_handler False
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 besignal.SIG_DFL
orsignal.SIG_IGN
or aSigAction
object returned by an earlier call togetossignal()
orsetossignal()
.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
toaction
. Ifosaction
is given, set the OS-level signal handler toosaction
. Ifosaction
isNone
(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