Python - Signal Handling



Signal handling in Python allows you to define custom handlers for managing asynchronous events such as interrupts or termination requests from keyboard, alarms, and even system signals. You can control how your program responds to various signals by defining custom handlers. The signal module in Python provides mechanisms to set and manage signal handlers.

A signal handler is a function that gets executed when a specific signal is received. The signal.signal() function allows defining custom handlers for signals. The signal module offers a way to define custom handlers that will be executed when a specific signal is received. Some default handlers are already installed in Python, which are −

  • SIGPIPE is ignored.
  • SIGINT is translated into a KeyboardInterrupt exception.

Commonly Used Signals

Python signal handlers are executed in the main Python thread of the main interpreter, even if the signal is received in another thread. Signals can't be used for inter-thread communication.

Following are the list of some common signals and their default actions −

  • SIGINT − Interrupt from keyboard (Ctrl+C), which raises a KeyboardInterrupt.
  • SIGTERM − Termination signal.
  • SIGALRM− Timer signal from alarm().
  • SIGCHLD − Child process stopped or terminated.
  • SIGUSR1 and SIGUSR2 − User-defined signals.

Setting a Signal Handler

To set a signal handler, we can use the signal.signal() function. It allows you to define custom handlers for signals. A handler remains installed until explicitly reset, except for SIGCHLD.

Example

Here is an example of setting a signal handler using the signal.signal() function with the SIGINT handler.

import signal
import time

def handle_signal(signum, frame):
   print(f"Signal {signum} received")

# Setting the handler for SIGINT
signal.signal(signal.SIGINT, handle_signal)

print("Press Ctrl+C to trigger SIGINT")
while True:
   time.sleep(1)

Output

On executing the above program, you will get the following results −

Press Ctrl+C to trigger SIGINT
Signal 2 received
Signal 2 received
Signal 2 received
Signal 2 received

Signal Handling on Windows

On Windows, the signal.signal() function can only handle a limited set of signals. If you try to use a signal not supported on Windows, a ValueError will be raised. And, an AttributeError will be raised if a signal name is not defined as a SIG* module level constant.

The supported signals on Windows are follows −

  • SIGABRT
  • SIGFPE
  • SIGILL
  • SIGINT
  • SIGSEGV
  • SIGTERM
  • SIGBREAK

Handling Timers and Alarms

Timers and alarms can be used to schedule signal delivery after a certain amount of time. Let's observe following example of handling alarms.

import signal
import time

def handler(signum, stack):
   print('Alarm: ', time.ctime())

signal.signal(signal.SIGALRM, handler)
signal.alarm(2)
time.sleep(5)
for i in range(5):
   signal.alarm(2)
   time.sleep(5)
   print("interrupted #%d" % i)

Output

On executing the above program, you will get the following results −

Alarm:  Wed Jul 17 17:30:11 2024
Alarm:  Wed Jul 17 17:30:16 2024
interrupted #0
Alarm:  Wed Jul 17 17:30:21 2024
interrupted #1
Alarm:  Wed Jul 17 17:30:26 2024
interrupted #2
Alarm:  Wed Jul 17 17:30:31 2024
interrupted #3
Alarm:  Wed Jul 17 17:30:36 2024
interrupted #4

Getting Signal Names from Numbers

There is no straightforward way of getting signal names from numbers in Python. You can use the signal module to get all its attributes, filter out those that start with SIG, and store them in a dictionary.

Example

This example creates a dictionary where the keys are signal numbers and the values are the corresponding signal names. This is useful for dynamically resolving signal names from their numeric values.

import signal

sig_items = reversed(sorted(signal.__dict__.items()))
final = dict((k, v) for v, k in sig_items if v.startswith('SIG') and not v.startswith('SIG_'))
print(final)

Output

On executing the above program, you will get the following results −

{<Signals.SIGXFSZ: 25>: 'SIGXFSZ', <Signals.SIGXCPU: 24>: 'SIGXCPU', <Signals.SIGWINCH: 28>: 'SIGWINCH', <Signals.SIGVTALRM: 26>: 'SIGVTALRM', <Signals.SIGUSR2: 12>: 'SIGUSR2', <Signals.SIGUSR1: 10>: 'SIGUSR1', <Signals.SIGURG: 23>: 'SIGURG', <Signals.SIGTTOU: 22>: 'SIGTTOU', <Signals.SIGTTIN: 21>: 'SIGTTIN', <Signals.SIGTSTP: 20>: 'SIGTSTP', <Signals.SIGTRAP: 5>: 'SIGTRAP', <Signals.SIGTERM: 15>: 'SIGTERM', <Signals.SIGSYS: 31>: 'SIGSYS', <Signals.SIGSTOP: 19>: 'SIGSTOP', <Signals.SIGSEGV: 11>: 'SIGSEGV', <Signals.SIGRTMIN: 34>: 'SIGRTMIN', <Signals.SIGRTMAX: 64>: 'SIGRTMAX', <Signals.SIGQUIT: 3>: 'SIGQUIT', <Signals.SIGPWR: 30>: 'SIGPWR', <Signals.SIGPROF: 27>: 'SIGPROF', <Signals.SIGIO: 29>: 'SIGIO', <Signals.SIGPIPE: 13>: 'SIGPIPE', <Signals.SIGKILL: 9>: 'SIGKILL', <Signals.SIGABRT: 6>: 'SIGABRT', <Signals.SIGINT: 2>: 'SIGINT', <Signals.SIGILL: 4>: 'SIGILL', <Signals.SIGHUP: 1>: 'SIGHUP', <Signals.SIGFPE: 8>: 'SIGFPE', <Signals.SIGCONT: 18>: 'SIGCONT', <Signals.SIGCHLD: 17>: 'SIGCHLD', <Signals.SIGBUS: 7>: 'SIGBUS', <Signals.SIGALRM: 14>: 'SIGALRM'}
Advertisements