or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/pypi-aiosignal

A coroutine-based signal implementation for asyncio projects providing asynchronous callback management

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/aiosignal@1.4.x

To install, run

npx @tessl/cli install tessl/pypi-aiosignal@1.4.0

index.mddocs/

aiosignal

A coroutine-based signal implementation for asyncio projects. aiosignal provides a Signal class that acts as a list of registered asynchronous callbacks with a two-stage lifecycle: first, callbacks can be registered using standard list operations, then the signal is frozen to prevent further modifications and enable callback execution.

Package Information

  • Package Name: aiosignal
  • Package Type: pypi
  • Language: Python
  • Installation: pip install aiosignal
  • Minimum Python Version: 3.9

Core Imports

from aiosignal import Signal

Version information:

from aiosignal import __version__
print(__version__)  # "1.4.0"

Basic Usage

import asyncio
from aiosignal import Signal

# Create a signal with an owner object
class MyApp:
    def __init__(self):
        self.on_startup = Signal(self)

app = MyApp()

# Register callbacks using list operations
async def init_database():
    print("Database initialized")

async def setup_logging():
    print("Logging configured")

app.on_startup.append(init_database)
app.on_startup.append(setup_logging)

# Or register using decorator syntax
@app.on_startup
async def load_config():
    print("Configuration loaded")

# Freeze the signal to enable execution
app.on_startup.freeze()

# Send the signal to execute all callbacks
async def main():
    await app.on_startup.send()

# Run the example
asyncio.run(main())

Architecture

The Signal class inherits from FrozenList (from the frozenlist package), providing a two-stage lifecycle:

  1. Registration Stage: Signal acts as a mutable list where callbacks can be added, removed, or modified using standard list operations
  2. Execution Stage: After calling freeze(), the signal becomes immutable and can execute callbacks via send()

This design ensures thread-safe callback execution while preventing accidental modifications during execution.

Capabilities

Signal Creation and Management

Create and configure signal instances for managing asynchronous callbacks.

class Signal(FrozenList[Callable[[Unpack[_Ts]], Awaitable[object]]]):
    def __init__(self, owner: object):
        """
        Initialize a signal with an owner object.
        
        Args:
            owner: The object that owns this signal (for identification)
        """
    
    def __repr__(self) -> str:
        """
        Return string representation showing owner, frozen state, and callbacks.
        
        Returns:
            str: Formatted representation
        """

Callback Registration

Register asynchronous callbacks that will be executed when the signal is sent.

def __call__(
    self, func: Callable[[Unpack[_Ts]], Awaitable[_T]]
) -> Callable[[Unpack[_Ts]], Awaitable[_T]]:
    """
    Decorator to add a function to this signal.
    
    Args:
        func: Asynchronous function to register as callback
        
    Returns:
        The same function (for use as decorator)
    """

All standard list operations are available for callback management:

  • append(callback) - Add callback to signal
  • insert(index, callback) - Insert callback at specific position
  • remove(callback) - Remove specific callback
  • pop(index) - Remove and return callback at index
  • clear() - Remove all callbacks
  • __getitem__(index) - Get callback at index
  • __setitem__(index, callback) - Replace callback at index
  • __delitem__(index) - Delete callback at index
  • __len__() - Get number of callbacks
  • __iter__() - Iterate over callbacks

Signal Execution

Execute all registered callbacks by sending the signal with optional arguments.

async def send(self, *args: Unpack[_Ts], **kwargs: Any) -> None:
    """
    Send data to all registered receivers.
    
    The signal must be frozen before sending. All registered callbacks
    will be executed in registration order.
    
    Args:
        *args: Positional arguments to pass to each callback
        **kwargs: Keyword arguments to pass to each callback
        
    Raises:
        RuntimeError: If signal is not frozen
        TypeError: If any callback is not a coroutine function
    """

Signal State Management

Control the signal's lifecycle and state transitions.

def freeze(self) -> None:
    """
    Freeze the signal to prevent further modifications.
    
    After freezing, callbacks cannot be added, removed, or modified.
    The signal can then be used to send data to callbacks.
    
    Raises:
        RuntimeError: If attempting to modify after freezing
    """

@property
def frozen(self) -> bool:
    """
    Check if signal is frozen.
    
    Returns:
        bool: True if signal is frozen, False otherwise
    """

Types

from typing import Any, Awaitable, Callable, TypeVar
from typing_extensions import Unpack, TypeVarTuple

_T = TypeVar("_T")
_Ts = TypeVarTuple("_Ts", default=Unpack[tuple[()]])

__version__: str = "1.4.0"

class Signal(FrozenList[Callable[[Unpack[_Ts]], Awaitable[object]]]):
    """
    Generic signal class that can be parameterized with callback argument types.
    
    Examples:
        Signal[str, int] - callbacks take (str, int) arguments
        Signal[dict] - callbacks take dict argument
        Signal - callbacks take any arguments
    """
    
    _owner: object

Error Handling

aiosignal raises the following exceptions:

  • RuntimeError: When attempting to send a non-frozen signal or modify a frozen signal
  • TypeError: When registered callbacks are not coroutine functions
# Example error handling
import asyncio
from aiosignal import Signal

signal = Signal(owner=None)

# This raises RuntimeError - signal not frozen
try:
    await signal.send()
except RuntimeError as e:
    print(f"Cannot send non-frozen signal: {e}")

# Register non-coroutine callback
def sync_callback():
    pass

signal.append(sync_callback)
signal.freeze()

# This raises TypeError - callback not a coroutine
try:
    await signal.send()
except TypeError as e:
    print(f"Callback must be coroutine: {e}")

Dependencies

aiosignal requires the following dependencies:

  • frozenlist >= 1.1.0: Provides the immutable list base class
  • typing-extensions >= 4.2: Advanced typing features for Python < 3.13

The package is designed for Python 3.9+ with full asyncio compatibility.