Metadata-Version: 2.4
Name: simsig
Version: 0.1.2
Summary: simsig: a simple but powerful thread-safe, and async-aware signal handling framework for Python
Author-email: Alex Semenyaka <alex.semenyaka@gmail.com>
License: MIT
Project-URL: Homepage, https://pypi.org/project/simsig/
Project-URL: Repository, https://github.com/alexsemenyaka/simsig
Keywords: signals
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# simsig: A Simple and Powerful Signal Handling Framework

[![Build Status](https://img.shields.io/travis/com/alexsemenyaka/simsig.svg?style=for-the-badge)](https://travis-ci.com/alexsemenyaka/simsig)
[![PyPI version](https://img.shields.io/pypi/v/simsig.svg?style=for-the-badge)](https://pypi.org/project/simsig/)
[![Coverage Status](https://img.shields.io/coveralls/github/alexsemenyaka/simsig.svg?style=for-the-badge)](https://coveralls.io/github/alexsemenyaka/simsig)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/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:
```bash
pip install simsig
```
For developers, you can install it in editable mode from a local clone:
```bash
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:
* [**POSIX.1-2017 `<signal.h>` Specification**]([https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html))

---
## 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.

```python
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.

```python
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.

```python
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`:
```bash
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.
