Skip to main content

simsig: a simple but powerful thread-safe, and async-aware signal handling framework for Python

Project description

simsig: A Simple and Powerful Signal Handling Framework

Build Status PyPI version Coverage Status License: MIT

simsig is a Python library that provides a high-level, intuitive, and powerful interface for handling OS signals. It's built on top of Python's standard signal module but abstracts away its complexities and limitations, making it easy to write robust, signal-aware applications.

Key Features

  • Graceful Shutdown: Easily register cleanup functions that run on terminating signals like SIGINT (Ctrl+C) or SIGTERM.
  • Functional & OOP API: Use simple module-level functions for quick tasks, or instantiate the SimSig class for more complex state management.
  • Powerful Context Managers:
    • Temporarily change signal handlers for critical sections of code.
    • Run blocks of code with a timeout.
    • Block signal delivery entirely for performance-critical operations.
  • Handler Chaining: Add new behavior to existing signal handlers without overwriting them.
  • Asyncio Integration: A dedicated, safe method for handling signals within an asyncio event loop.
  • Cross-Platform: Provides a consistent interface and gracefully handles differences between operating systems (e.g., UNIX vs. Windows).
    • Windows support is fairly limited though at the moment

Installation

Install the library directly from PyPI:

pip install simsig

For developers, you can install it in editable mode from a local clone:

git clone https://github.com/alexsemenyaka/simsig.git
cd simsig
pip install -e .

A Primer on UNIX Signals

Before diving into the library, it's helpful to understand what signals are.

What Are Signals?

A signal is a form of Inter-Process Communication (IPC) in UNIX-like systems. It's a notification sent to a process to inform it of an event. Think of it as a software interrupt or a doorbell for a process. When a signal is sent, the operating system interrupts the process's normal execution flow to deliver it.

Signal Dispositions

A process can handle a signal in one of three ways (its "disposition"):

  1. Catch the signal: The process can register a custom function (a signal handler) that will be executed when the signal is received.
  2. Ignore the signal: The process can tell the OS to simply discard the signal. The special constant for this is SIG_IGN.
  3. Use the default action: Every signal has a default action, which is executed if the process doesn't specify otherwise. The constant for this is SIG_DFL. Common default actions include terminating the process, creating a core dump, or doing nothing.

Synchronous vs. Asynchronous Signals

This is a key distinction:

  • Asynchronous Signals: These are generated by events external to the process and can arrive at any time. The classic example is pressing Ctrl+C in your terminal, which causes the OS to send a SIGINT to the foreground process. Other examples include SIGTERM from the kill command or SIGHUP when a terminal closes.
  • Synchronous Signals: These are caused directly by the process's own execution. For example, if a process attempts an illegal memory access, the CPU generates a fault that the OS translates into a SIGSEGV (Segmentation Fault) sent back to the process. Other examples include SIGFPE (Floating-Point Exception) for invalid math operations or SIGILL for an illegal instruction. They are "synchronous" because they are tied to a specific point in the code.

For more detailed information, the official POSIX standard for <signal.h> is the ultimate reference:


Core Usage

simsig provides a simple functional API for most common use cases.

Basic Usage: Graceful Shutdown & Custom Handlers

This example sets up a handler to perform a clean exit on Ctrl+C and a custom handler for a user signal.

import simsig
import time
import os
import sys

# This minimal example is designed for UNIX-like systems.
if sys.platform == "win32":
    sys.exit(0)

# 1. Define a minimal exit function.
def on_exit():
    # Exit the process immediately, without cleanup.
    os._exit(0)

# 2. All terminating signals (including Ctrl+C) will now exit silently.
simsig.graceful_shutdown(on_exit)

# 3. Define an empty handler for status checks.
def show_status(signal_number, frame):
    # Do nothing, just catch the signal.
    pass

# 4. Set the handler for the user signal SIGUSR1.
if simsig.has_sig('SIGUSR1'):
    simsig.set_handler(simsig.Signals.SIGUSR1, show_status)

# 5. An infinite loop to keep the process alive.
while True:
    time.sleep(1)

Advanced Usage: Context Managers

simsig provides powerful context managers for temporarily changing signal behavior.

import simsig
import time
import sys

if sys.platform == "win32":
    sys.exit(0)

# 1. Temporarily ignore Ctrl+C for 10 seconds.
print("Ignoring Ctrl+C for 10 seconds...")
with simsig.temp_handler(simsig.Signals.SIGINT, simsig.SigReaction.ign):
    time.sleep(10)
print("Ctrl+C is now restored.")

# 2. Run a block that will be terminated by a timeout after 2 seconds.
print("\nRunning a 5-second task with a 2-second timeout...")
try:
    with simsig.with_timeout(2):
        time.sleep(5)
except simsig.SimSigTimeoutError:
    # Catch the timeout error and do nothing.
    print("Caught expected timeout.")

Asynchronous Programming (asyncio)

Handling signals in asyncio requires special care. simsig provides a safe and easy way to integrate with the event loop.

import simsig
import asyncio
import sys

if sys.platform == "win32":
    sys.exit(0)

shutdown_event = asyncio.Event()

# The handler must be a regular function that sets the asyncio event.
def shutdown_handler():
    print("\nSignal received, notifying tasks...")
    shutdown_event.set()

async def main():
    # Register the handler correctly within the running loop.
    simsig.async_handler([simsig.Signals.SIGINT, simsig.Signals.SIGTERM], shutdown_handler)
    
    print("Application running. Press Ctrl+C to shut down.")
    await shutdown_event.wait()
    print("Shutdown complete.")

if __name__ == "__main__":
    asyncio.run(main())

API Reference

The library exposes both a class-based and a functional API.

  • Classes:
    • SimSig: The main class for handling signals in an object-oriented way.
    • Signals: An IntEnum containing all signals available on the current OS.
    • SigReaction: An IntEnum for high-level actions (DFLT, IGN, fin).
    • SimSigTimeoutError: Custom exception for timeouts.
  • Functions:
    • set_handler, graceful_shutdown, chain_handler, ignore_terminal_signals, reset_to_defaults, async_handler, get_signal_setting, has_sig.
  • Context Managers:
    • temp_handler, with_timeout, block_signals.

For detailed information on each function's parameters, please refer to the docstrings within the source code.

Testing

To run the test suite, install the development dependencies and run pytest:

pip install -e ".[dev]"  # Assuming dev dependencies are in pyproject.toml
pytest

License

This project is licensed under the MIT License. See the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

simsig-0.1.2.tar.gz (17.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

simsig-0.1.2-py3-none-any.whl (16.8 kB view details)

Uploaded Python 3

File details

Details for the file simsig-0.1.2.tar.gz.

File metadata

  • Download URL: simsig-0.1.2.tar.gz
  • Upload date:
  • Size: 17.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.10

File hashes

Hashes for simsig-0.1.2.tar.gz
Algorithm Hash digest
SHA256 6bce6064bc1dec1958b23e9e4754f43e25903c474d679e31b51e68c9c9c3a029
MD5 2358a1502fd6fbc76bc79ada04a50e3b
BLAKE2b-256 8c6dfe07a00504121d605d4bed4498cafd9612f3d646650ec404581ccc009073

See more details on using hashes here.

File details

Details for the file simsig-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: simsig-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 16.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.10

File hashes

Hashes for simsig-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 2971756cd9fbf55855739a0d1e4c8831a359e6532a3fbd6ba2e94375679462f0
MD5 1c349af249b58dd6a70dd7a71c16484d
BLAKE2b-256 c75b92dcf8421484a883ae7f4e297e69af8161a5bf4588071fa30335b02fd851

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page