Signals
This section contains documentation for the signals module.
Signal system for Paperap.
This module provides a flexible signal/event system that allows components to communicate without direct dependencies. It supports prioritized handlers, handler chains, and temporary handler disabling.
The signal system is designed around a singleton registry that manages all signals and their handlers. Signals can be created, connected to, and emitted through this registry.
- class paperap.signals.QueueType[source]
Bases:
TypedDict
A type used by SignalRegistry for storing queued signal actions.
This dictionary stores handlers that are registered before their signals are created, allowing for flexible registration order.
- connect
Maps signal names to sets of (handler, priority) tuples.
- disconnect
Maps signal names to sets of handlers to disconnect.
- disable
Maps signal names to sets of handlers to disable.
- enable
Maps signal names to sets of handlers to enable.
- final class paperap.signals.SignalPriority[source]
Bases:
object
Priority levels for signal handlers.
These constants define standard priority levels for signal handlers. Lower numbers execute first, allowing precise control over handler execution order.
- FIRST
Execute before all other handlers.
- HIGH
Execute with high priority.
- NORMAL
Default priority level.
- LOW
Execute with low priority.
- LAST
Execute after all other handlers.
- FIRST = 0
- HIGH = 25
- NORMAL = 50
- LOW = 75
- LAST = 100
- class paperap.signals.SignalParams[source]
Bases:
TypedDict
A type used by SignalRegistry for storing signal parameters.
- name
The name of the signal.
- description
A description of the signal’s purpose.
-
name:
str
-
description:
str
- class paperap.signals.Signal(name, description='')[source]
Bases:
Generic
A signal that can be connected to and emitted.
Handlers can be registered with a priority to control execution order. Each handler receives the output of the previous handler as its first argument, enabling a filter/transformation chain. This allows handlers to modify data as it passes through the chain.
- name
The unique name of this signal.
- description
A human-readable description of the signal’s purpose.
- _handlers
Dictionary mapping priority levels to lists of handler functions.
- _disabled_handlers
Set of temporarily disabled handler functions.
Example
>>> signal = Signal("document.save") >>> def log_save(doc, **kwargs): ... print(f"Saving document: {doc.title}") ... return doc >>> signal.connect(log_save) >>> signal.emit(document)
- __init__(name, description='')[source]
Initialize a new signal.
-
name:
str
-
description:
str
- connect(handler, priority=50)[source]
Connect a handler to this signal.
- Parameters:
handler (Callable[…, _ReturnType]) – The handler function to be called when the signal is emitted. The function should accept the transformed value as its first argument, and return the further transformed value.
priority (int) – The priority level for this handler (lower numbers execute first). Use SignalPriority constants for standard levels.
- Return type:
None
Example
>>> def transform_document(doc, **kwargs): ... doc.title = doc.title.upper() ... return doc >>> signal.connect(transform_document, SignalPriority.HIGH)
- disconnect(handler)[source]
Disconnect a handler from this signal.
- Parameters:
handler (Callable[…, _ReturnType]) – The handler to disconnect. The handler will no longer be called when the signal is emitted.
- Return type:
None
Example
>>> signal.disconnect(transform_document)
- emit(*args, **kwargs)[source]
Emit the signal, calling all connected handlers in priority order.
Each handler receives the output of the previous handler as its first argument. Other arguments are passed unchanged. This creates a transformation chain where each handler can modify the data before passing it to the next handler.
- Parameters:
*args (Any) – Positional arguments to pass to handlers. The first argument is the value that will be transformed through the handler chain.
**kwargs (Any) – Keyword arguments to pass to all handlers.
- Return type:
_ReturnType | None
- Returns:
The final result after all handlers have processed the data.
Example
>>> # Transform a document through multiple handlers >>> transformed_doc = signal.emit(document, user="admin")
- disable(handler)[source]
Temporarily disable a handler without disconnecting it.
Disabled handlers remain connected but are skipped during signal emission. This is useful for temporarily suspending a handler’s execution without losing its registration.
- Parameters:
handler (Callable[…, _ReturnType]) – The handler to disable.
- Return type:
None
Example
>>> signal.disable(log_save) # Temporarily stop logging
- enable(handler)[source]
Re-enable a temporarily disabled handler.
- Parameters:
handler (Callable[…, _ReturnType]) – The handler to enable. If the handler wasn’t disabled, this method has no effect.
- Return type:
None
Example
>>> signal.enable(log_save) # Resume logging
- class paperap.signals.SignalRegistry[source]
Bases:
object
Registry of all signals in the application.
This singleton class manages all signals in the application, providing a central point for creating, connecting to, and emitting signals.
The registry also handles queuing of signal actions when signals are connected to before they are created, ensuring that handlers are properly registered regardless of initialization order.
- _instance
The singleton instance of this class.
- _signals
Dictionary mapping signal names to Signal instances.
- _queue
Dictionary of queued actions for signals not yet created.
Examples
>>> # Emit a signal with keyword arguments >>> SignalRegistry.emit( ... "document.save:success", ... "Fired when a document has been saved successfully", ... kwargs = {"document": document} ... )
>>> # Emit a signal that transforms data >>> filtered_data = SignalRegistry.emit( ... "document.save:before", ... "Fired before a document is saved. Optionally filters the data that will be saved.", ... args = (data,), ... kwargs = {"document": document} ... )
>>> # Connect a handler to a signal >>> def log_document_save(document, **kwargs): ... print(f"Document saved: {document.title}") ... return document >>> SignalRegistry.connect("document.save:success", log_document_save)
- __init__()[source]
Initialize the signal registry.
- static __new__(cls)[source]
Ensure that only one instance of the class is created (singleton pattern).
- Return type:
Self
- Returns:
The singleton instance of this class.
- classmethod get_instance()[source]
Get the singleton instance of this class.
This method ensures that only one instance of SignalRegistry exists throughout the application.
- Return type:
Self
- Returns:
The singleton instance of this class.
- register(signal)[source]
Register a signal and process any queued actions for it.
This method registers a signal with the registry and processes any actions (connect, disconnect, etc.) that were queued for this signal before it was created.
- queue_action(action, name, handler, priority=None)[source]
Queue a signal-related action to be processed when the signal is registered.
This method allows actions to be queued for signals that haven’t been created yet, ensuring that handlers can be registered in any order.
- Parameters:
action (
Literal
['connect'
,'disconnect'
,'disable'
,'enable'
]) – The action to queue (connect, disconnect, disable, enable).name (
str
) – The signal name.handler (
Callable
[...
,Any
]) – The handler function to queue.priority (
int
|None
) – The priority level for this handler (only for connect action).
- Raises:
ValueError – If the action is invalid.
- Return type:
Example
>>> registry.queue_action("connect", "document.save", log_handler, SignalPriority.HIGH)
- get(name)[source]
Get a signal by name.
- Parameters:
name (
str
) – The signal name.- Return type:
- Returns:
The signal instance, or None if not found.
Example
>>> signal = registry.get("document.save:success") >>> if signal: ... signal.emit(document)
- list_signals()[source]
List all registered signal names.
Example
>>> signals = registry.list_signals() >>> print(f"Available signals: {', '.join(signals)}")
- create(name, description='', return_type=None)[source]
Create and register a new signal.
This method creates a new signal with the given name and description, registers it with the registry, and processes any queued actions for it.
- Parameters:
- Return type:
- Returns:
The new signal instance.
Example
>>> save_signal = registry.create( ... "document.save:success", ... "Fired when a document has been saved successfully" ... )
- emit(name, description='', *, return_type=None, args=None, kwargs=None)[source]
Emit a signal, calling handlers in priority order.
This method emits a signal with the given name, creating it if it doesn’t exist. Each handler in the signal’s chain receives the output of the previous handler as its first argument, allowing for data transformation.
- Parameters:
name (
str
) – Signal name. If the signal doesn’t exist, it will be created.description (
str
) – Optional description for new signals.return_type (
type
[TypeVar
(_ReturnType
)] |None
) – Optional return type for new signals.args (
Optional
[TypeVar
(_ReturnType
)]) – The value to be transformed through the handler chain.kwargs (
dict
[str
,Any
] |None
) – Keyword arguments passed to all handlers.
- Return type:
- Returns:
The transformed value after all handlers have processed it.
Example
>>> # Transform document data through a handler chain >>> processed_data = registry.emit( ... "document.process", ... "Process document data before saving", ... args=document_data, ... kwargs={"user": current_user} ... )
- connect(name, handler, priority=50)[source]
Connect a handler to a signal, or queue it if the signal is not yet registered.
This method connects a handler function to a signal with the given name. If the signal doesn’t exist yet, the connection is queued and will be established when the signal is created.
- Parameters:
- Return type:
Example
>>> def log_document_save(document, **kwargs): ... print(f"Document saved: {document.title}") ... return document >>> registry.connect("document.save:success", log_document_save)
- disconnect(name, handler)[source]
Disconnect a handler from a signal, or queue it if the signal is not yet registered.
This method disconnects a handler function from a signal with the given name. If the signal doesn’t exist yet, the disconnection is queued and will be processed when the signal is created.
- Parameters:
- Return type:
Example
>>> registry.disconnect("document.save:success", log_document_save)
- disable(name, handler)[source]
Temporarily disable a handler for a signal, or queue it if the signal is not yet registered.
This method temporarily disables a handler function for a signal with the given name. The handler remains connected but will be skipped during signal emission. If the signal doesn’t exist yet, the disable action is queued.
- Parameters:
- Return type:
Example
>>> # Temporarily disable logging during bulk operations >>> registry.disable("document.save:success", log_document_save)
- enable(name, handler)[source]
Enable a previously disabled handler, or queue it if the signal is not yet registered.
This method re-enables a previously disabled handler function for a signal. If the signal doesn’t exist yet, the enable action is queued.
- Parameters:
- Return type:
Example
>>> # Re-enable logging after bulk operations >>> registry.enable("document.save:success", log_document_save)
- is_queued(action, name, handler)[source]
Check if a handler is queued for a signal action.
This method checks if a specific handler is queued for a specific action on a signal that hasn’t been created yet.
- Parameters:
- Return type:
- Returns:
True if the handler is queued for the specified action, False otherwise.
Example
>>> is_queued = registry.is_queued("disable", "document.save", log_handler) >>> print(f"Handler is {'queued for disabling' if is_queued else 'not queued'}")