Skip to main content

Lib/signal.py

cpython 3.14 @ ab2d84fe1023/Lib/signal.py

Lib/signal.py is a thin pure-Python layer over Modules/signalmodule.c. The C module exposes signal, getsignal, raise_signal, strsignal, valid_signals, SIG_DFL, SIG_IGN, and the POSIX extensions (alarm, setitimer, getitimer, pause, pthread_kill, pthread_sigmask, sigwait, sigwaitinfo, sigtimedwait). The Python module wraps these with two IntEnum subclasses, Signals and Handlers, that give all signal numbers and the two special handler values symbolic names.

The central function is signal(signum, handler), which installs handler as the callback to invoke when the process receives signum. CPython's signal delivery model calls the handler from the main thread only: signals are received by the C-level signal(2) handler, which sets a flag in _PyRuntime.ceval.signals_pending, and the eval loop checks that flag at safe points (between bytecode instructions) and calls the registered Python handler from the main thread's frame.

Map

LinesSymbolRolegopy
1-40Module header, re-exports from _signalImport the C extension names into the module namespace; define __all__.(stdlib pending)
41-100Signals enum, signal constantsSignals(IntEnum) enumerates SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM on all platforms, plus POSIX-only names; re-registers each constant at module level.(stdlib pending)
101-150Handlers enum, SIG_DFL, SIG_IGNHandlers(IntEnum) with members SIG_DFL (0) and SIG_IGN (1); getsignal normalises its return value to a Handlers member when possible.(stdlib pending)
151-200signal, getsignal, raise_signal, strsignal, valid_signalsWrappers that accept both integer signal numbers and Signals enum members; signal also accepts Handlers members for SIG_DFL/SIG_IGN.(stdlib pending)
201-250alarm, pause, setitimer, getitimerPOSIX-only; setitimer takes ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF and delivers SIGALRM, SIGVTALRM, or SIGPROF respectively; only available if hasattr(signal, 'setitimer').(stdlib pending)
251-300pthread_kill, pthread_sigmask, sigwait, sigwaitinfo, sigtimedwaitPOSIX thread signal functions; pthread_sigmask accepts and returns frozenset of Signals members; only defined when the C extension supplies them.(stdlib pending)
301-350set_wakeup_fd, NSIG, SIGRTMIN, SIGRTMAXset_wakeup_fd writes a byte to a file descriptor or socket when a signal arrives, enabling integration with event loops; real-time signal range constants on Linux.(stdlib pending)

Reading

signal() handler registration (lines 151 to 200)

cpython 3.14 @ ab2d84fe1023/Lib/signal.py#L151-200

def signal(signalnum, handler):
handler = _enum_to_int(handler)
signalnum = Signals(signalnum)
return _signal.signal(signalnum, handler)

def getsignal(signalnum):
signalnum = Signals(signalnum)
handler = _signal.getsignal(signalnum)
try:
return Handlers(handler)
except ValueError:
# handler is a Python callable, not SIG_DFL/SIG_IGN
return handler

signal() accepts any integer-compatible signal specifier and normalises it to a Signals member before delegating to _signal.signal. The C function stores the callable in a per-signal slot in _PyRuntime and installs a C-level sigaction handler that sets the pending-signal flag.

getsignal() returns the currently registered handler. When the return value is SIG_DFL (0) or SIG_IGN (1), it is converted to the corresponding Handlers enum member. Otherwise it is returned as-is (the Python callable, or SIG_DFL/SIG_IGN as raw ints if the signal was set by a non-Python sigaction call).

A ValueError is raised if signalnum is not a valid signal on the current platform. A TypeError is raised if handler is not callable, SIG_DFL, or SIG_IGN. Only the main thread may call signal(); calling from a non-main thread raises ValueError.

setitimer and SIGALRM (lines 201 to 250)

cpython 3.14 @ ab2d84fe1023/Lib/signal.py#L201-250

# These are only available on POSIX.
if hasattr(_signal, 'setitimer'):
setitimer = _signal.setitimer
getitimer = _signal.getitimer
ITIMER_REAL = _signal.ITIMER_REAL
ITIMER_VIRTUAL = _signal.ITIMER_VIRTUAL
ITIMER_PROF = _signal.ITIMER_PROF

class ItimerError(OSError):
pass

setitimer(which, seconds, interval=0.0) arms an interval timer. which selects the timer type:

  • ITIMER_REAL: decrements in real time; delivers SIGALRM on expiry.
  • ITIMER_VIRTUAL: decrements only while the process is executing in user mode; delivers SIGVTALRM.
  • ITIMER_PROF: decrements while executing in user or kernel mode; used with profilers; delivers SIGPROF.

seconds is the initial delay; interval is the reload value for repeating timers (0 for one-shot). getitimer(which) returns a (value, interval) tuple for the current timer state.

The ItimerError exception is raised when the underlying setitimer(2) call fails, for example if seconds overflows the platform's timer resolution.

pthread_sigmask (lines 251 to 300)

cpython 3.14 @ ab2d84fe1023/Lib/signal.py#L251-300

if hasattr(_signal, 'pthread_sigmask'):
def pthread_sigmask(how, mask):
sigs = set(mask)
return _signal.pthread_sigmask(how, sigs)

pthread_sigmask(how, mask) manipulates the calling thread's signal mask. how is one of SIG_BLOCK, SIG_UNBLOCK, or SIG_SETMASK (constants from _signal). mask is an iterable of signal numbers or Signals members. The return value is a frozenset of the previous mask as Signals members.

sigwait(sigset) suspends the calling thread until one of the signals in sigset is pending, then removes it from the pending set and returns its number as a Signals member. Unlike pause(), it does not invoke the registered Python handler; the signal is consumed silently.

pthread_kill(thread_id, signum) sends a signal to a specific POSIX thread. thread_id is obtained from threading.get_native_id() or threading.Thread.native_id. The signal is delivered to the target thread rather than to the process, which is useful for interrupting a blocked read() or select() in another thread.

Signals and Handlers enums (lines 41 to 150)

cpython 3.14 @ ab2d84fe1023/Lib/signal.py#L41-150

class Signals(IntEnum):
SIGABRT = _signal.SIGABRT
SIGFPE = _signal.SIGFPE
SIGILL = _signal.SIGILL
SIGINT = _signal.SIGINT
SIGSEGV = _signal.SIGSEGV
SIGTERM = _signal.SIGTERM
# Platform-conditional members added below via globals()
...

class Handlers(IntEnum):
SIG_DFL = _signal.SIG_DFL
SIG_IGN = _signal.SIG_IGN

SIG_DFL = Handlers.SIG_DFL
SIG_IGN = Handlers.SIG_IGN

Both enums subclass IntEnum so that their members are usable anywhere a plain integer is expected. The Signals class is populated with only the constants that exist in _signal; optional members such as SIGHUP, SIGCHLD, SIGUSR1, SIGUSR2, and real-time signals (SIGRTMIN through SIGRTMAX) are added conditionally by iterating dir(_signal) and inserting entries whose names match SIG[A-Z].

After the class is built, each member is also assigned at module level (SIGINT = Signals.SIGINT), preserving backward compatibility with code that imports signal names directly from the module.

gopy mirror

Lib/signal.py is pure Python and can be bundled verbatim once the _signal C extension is available. The critical requirement is the eval-loop integration: the pending-signal check (_PyRuntime.ceval.signals_pending) must fire between instructions and dispatch to the registered Python handlers from the main goroutine. set_wakeup_fd requires a writable file descriptor or socket, which maps naturally to a Go channel or pipe in the event-loop integration layer.