or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# aiosignal

1

2

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.

3

4

## Package Information

5

6

- **Package Name**: aiosignal

7

- **Package Type**: pypi

8

- **Language**: Python

9

- **Installation**: `pip install aiosignal`

10

- **Minimum Python Version**: 3.9

11

12

## Core Imports

13

14

```python

15

from aiosignal import Signal

16

```

17

18

Version information:

19

20

```python

21

from aiosignal import __version__

22

print(__version__) # "1.4.0"

23

```

24

25

## Basic Usage

26

27

```python

28

import asyncio

29

from aiosignal import Signal

30

31

# Create a signal with an owner object

32

class MyApp:

33

def __init__(self):

34

self.on_startup = Signal(self)

35

36

app = MyApp()

37

38

# Register callbacks using list operations

39

async def init_database():

40

print("Database initialized")

41

42

async def setup_logging():

43

print("Logging configured")

44

45

app.on_startup.append(init_database)

46

app.on_startup.append(setup_logging)

47

48

# Or register using decorator syntax

49

@app.on_startup

50

async def load_config():

51

print("Configuration loaded")

52

53

# Freeze the signal to enable execution

54

app.on_startup.freeze()

55

56

# Send the signal to execute all callbacks

57

async def main():

58

await app.on_startup.send()

59

60

# Run the example

61

asyncio.run(main())

62

```

63

64

## Architecture

65

66

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

67

68

1. **Registration Stage**: Signal acts as a mutable list where callbacks can be added, removed, or modified using standard list operations

69

2. **Execution Stage**: After calling `freeze()`, the signal becomes immutable and can execute callbacks via `send()`

70

71

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

72

73

## Capabilities

74

75

### Signal Creation and Management

76

77

Create and configure signal instances for managing asynchronous callbacks.

78

79

```python { .api }

80

class Signal(FrozenList[Callable[[Unpack[_Ts]], Awaitable[object]]]):

81

def __init__(self, owner: object):

82

"""

83

Initialize a signal with an owner object.

84

85

Args:

86

owner: The object that owns this signal (for identification)

87

"""

88

89

def __repr__(self) -> str:

90

"""

91

Return string representation showing owner, frozen state, and callbacks.

92

93

Returns:

94

str: Formatted representation

95

"""

96

```

97

98

### Callback Registration

99

100

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

101

102

```python { .api }

103

def __call__(

104

self, func: Callable[[Unpack[_Ts]], Awaitable[_T]]

105

) -> Callable[[Unpack[_Ts]], Awaitable[_T]]:

106

"""

107

Decorator to add a function to this signal.

108

109

Args:

110

func: Asynchronous function to register as callback

111

112

Returns:

113

The same function (for use as decorator)

114

"""

115

```

116

117

All standard list operations are available for callback management:

118

119

- `append(callback)` - Add callback to signal

120

- `insert(index, callback)` - Insert callback at specific position

121

- `remove(callback)` - Remove specific callback

122

- `pop(index)` - Remove and return callback at index

123

- `clear()` - Remove all callbacks

124

- `__getitem__(index)` - Get callback at index

125

- `__setitem__(index, callback)` - Replace callback at index

126

- `__delitem__(index)` - Delete callback at index

127

- `__len__()` - Get number of callbacks

128

- `__iter__()` - Iterate over callbacks

129

130

### Signal Execution

131

132

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

133

134

```python { .api }

135

async def send(self, *args: Unpack[_Ts], **kwargs: Any) -> None:

136

"""

137

Send data to all registered receivers.

138

139

The signal must be frozen before sending. All registered callbacks

140

will be executed in registration order.

141

142

Args:

143

*args: Positional arguments to pass to each callback

144

**kwargs: Keyword arguments to pass to each callback

145

146

Raises:

147

RuntimeError: If signal is not frozen

148

TypeError: If any callback is not a coroutine function

149

"""

150

```

151

152

### Signal State Management

153

154

Control the signal's lifecycle and state transitions.

155

156

```python { .api }

157

def freeze(self) -> None:

158

"""

159

Freeze the signal to prevent further modifications.

160

161

After freezing, callbacks cannot be added, removed, or modified.

162

The signal can then be used to send data to callbacks.

163

164

Raises:

165

RuntimeError: If attempting to modify after freezing

166

"""

167

168

@property

169

def frozen(self) -> bool:

170

"""

171

Check if signal is frozen.

172

173

Returns:

174

bool: True if signal is frozen, False otherwise

175

"""

176

```

177

178

## Types

179

180

```python { .api }

181

from typing import Any, Awaitable, Callable, TypeVar

182

from typing_extensions import Unpack, TypeVarTuple

183

184

_T = TypeVar("_T")

185

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

186

187

__version__: str = "1.4.0"

188

189

class Signal(FrozenList[Callable[[Unpack[_Ts]], Awaitable[object]]]):

190

"""

191

Generic signal class that can be parameterized with callback argument types.

192

193

Examples:

194

Signal[str, int] - callbacks take (str, int) arguments

195

Signal[dict] - callbacks take dict argument

196

Signal - callbacks take any arguments

197

"""

198

199

_owner: object

200

```

201

202

## Error Handling

203

204

aiosignal raises the following exceptions:

205

206

- **RuntimeError**: When attempting to send a non-frozen signal or modify a frozen signal

207

- **TypeError**: When registered callbacks are not coroutine functions

208

209

```python

210

# Example error handling

211

import asyncio

212

from aiosignal import Signal

213

214

signal = Signal(owner=None)

215

216

# This raises RuntimeError - signal not frozen

217

try:

218

await signal.send()

219

except RuntimeError as e:

220

print(f"Cannot send non-frozen signal: {e}")

221

222

# Register non-coroutine callback

223

def sync_callback():

224

pass

225

226

signal.append(sync_callback)

227

signal.freeze()

228

229

# This raises TypeError - callback not a coroutine

230

try:

231

await signal.send()

232

except TypeError as e:

233

print(f"Callback must be coroutine: {e}")

234

```

235

236

## Dependencies

237

238

aiosignal requires the following dependencies:

239

240

- **frozenlist >= 1.1.0**: Provides the immutable list base class

241

- **typing-extensions >= 4.2**: Advanced typing features for Python < 3.13

242

243

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