or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions-callbacks.mdcore-statemachine.mddiagrams.mdevents-transitions.mdexceptions.mdindex.mdmixins-integration.mdutilities.md

events-transitions.mddocs/

0

# Events and Transitions

1

2

Event handling and state transition management including event triggers, transition definitions with conditions, guards and validators, and transition lifecycle callbacks.

3

4

## Capabilities

5

6

### Event Class

7

8

Events trigger state transitions and carry data to callbacks and actions.

9

10

```python { .api }

11

class Event(str):

12

"""

13

An event triggers transitions between states.

14

15

Events inherit from str and can be used as string identifiers.

16

They carry information about triggers and associated transitions.

17

18

Parameters:

19

- transitions: Event transitions (string, TransitionList, or None)

20

- id: Event identifier (optional, generated if not provided)

21

- name: Event name identifier (optional, derived from id)

22

"""

23

def __new__(cls, transitions=None, id=None, name=None, _sm=None): ...

24

25

@property

26

def id(self) -> str:

27

"""Get the event identifier."""

28

29

@property

30

def name(self) -> str:

31

"""Get the human-readable event name."""

32

33

@property

34

def transitions(self) -> TransitionList:

35

"""Get transitions associated with this event."""

36

37

def __call__(self, *args, **kwargs):

38

"""Trigger the event (for bound events)."""

39

40

def split(self, sep=None, maxsplit=-1):

41

"""Split event into multiple events (string method)."""

42

43

def is_same_event(self, event=None):

44

"""Check if this event matches the given event."""

45

46

def add_callback(self, callback, group=None, priority=None):

47

"""Add callback to event execution."""

48

```

49

50

### BoundEvent Class

51

52

Runtime event instances bound to specific state machine instances.

53

54

```python { .api }

55

class BoundEvent(Event):

56

"""

57

Event bound to a specific state machine instance.

58

59

Provides callable interface for triggering events directly.

60

"""

61

def __call__(self, *args, **kwargs):

62

"""Call the event to trigger associated transitions."""

63

64

async def __call__(self, *args, **kwargs):

65

"""Async version for async state machines."""

66

```

67

68

### Transition Class

69

70

Defines connections between states with optional conditions, guards, and actions.

71

72

```python { .api }

73

class Transition:

74

"""

75

Represents a state transition with source, target, and conditions.

76

77

Parameters:

78

- source: Source State object

79

- target: Target State object

80

- event: Event trigger(s) - string, list of strings, or space-separated string

81

- internal: Internal transition flag (doesn't trigger entry/exit actions)

82

- validators: Validation callbacks invoked before transition

83

- cond: Condition callbacks that must evaluate to True

84

- unless: Condition callbacks that must evaluate to False

85

- on: Action callbacks executed during transition

86

- before: Callbacks executed before transition

87

- after: Callbacks executed after transition

88

"""

89

def __init__(self, source: State, target: State, event=None, internal: bool = False, validators=None, cond=None, unless=None, on=None, before=None, after=None): ...

90

91

@property

92

def source(self) -> State:

93

"""Get the source state."""

94

95

@property

96

def target(self) -> State:

97

"""Get the target state."""

98

99

@property

100

def event(self) -> str:

101

"""Get the event identifier."""

102

103

@property

104

def internal(self) -> bool:

105

"""Check if transition is internal."""

106

107

def match(self, event: str) -> bool:

108

"""Check if transition matches given event."""

109

110

def add_event(self, event):

111

"""Add event to this transition."""

112

113

def execute(self, machine, event_data):

114

"""Execute the transition."""

115

```

116

117

### TransitionList Class

118

119

Collection of transitions with callback support and fluent API.

120

121

```python { .api }

122

class TransitionList:

123

"""

124

Collection of transitions supporting callbacks and fluent operations.

125

126

Allows chaining transitions and adding shared callbacks.

127

"""

128

def __init__(self, transitions=None): ...

129

130

def add_transitions(self, transitions):

131

"""Add transitions to the collection."""

132

133

def add_event(self, event):

134

"""Add event to all transitions in the collection."""

135

136

def unique_events(self):

137

"""Get unique events across all transitions."""

138

139

def __or__(self, other) -> TransitionList:

140

"""Chain transitions using | operator."""

141

142

def __getitem__(self, index):

143

"""Get transition by index."""

144

145

def __len__(self) -> int:

146

"""Get number of transitions in the list."""

147

148

def cond(self, *callbacks) -> TransitionList:

149

"""Add condition callbacks to all transitions."""

150

151

def unless(self, *callbacks) -> TransitionList:

152

"""Add unless callbacks to all transitions."""

153

154

def on(self, *callbacks) -> TransitionList:

155

"""Add action callbacks to all transitions."""

156

157

def before(self, *callbacks) -> TransitionList:

158

"""Add before callbacks to all transitions."""

159

160

def after(self, *callbacks) -> TransitionList:

161

"""Add after callbacks to all transitions."""

162

```

163

164

## Usage Examples

165

166

### Basic Event and Transition Definition

167

168

```python

169

from statemachine import StateMachine, State, Event

170

171

class OrderMachine(StateMachine):

172

# Define states

173

pending = State(initial=True)

174

paid = State()

175

shipped = State()

176

delivered = State(final=True)

177

cancelled = State(final=True)

178

179

# Define events and transitions

180

pay = pending.to(paid)

181

ship = paid.to(shipped)

182

deliver = shipped.to(delivered)

183

cancel = (

184

pending.to(cancelled)

185

| paid.to(cancelled)

186

| shipped.to(cancelled)

187

)

188

189

# Usage

190

order = OrderMachine()

191

order.send("pay") # Trigger pay event

192

order.pay() # Alternative direct call

193

print(order.current_state.id) # "paid"

194

```

195

196

### Conditional Transitions with Guards

197

198

```python

199

class ATMMachine(StateMachine):

200

idle = State(initial=True)

201

card_inserted = State()

202

pin_entered = State()

203

authenticated = State()

204

205

insert_card = idle.to(card_inserted)

206

enter_pin = card_inserted.to(pin_entered)

207

208

# Conditional transition with guard

209

authenticate = pin_entered.to(authenticated).cond("valid_pin")

210

reject = pin_entered.to(idle).unless("valid_pin")

211

212

def valid_pin(self, pin: str = None):

213

"""Guard function - return True to allow transition."""

214

return pin == "1234"

215

216

# Usage with event data

217

atm = ATMMachine()

218

atm.send("insert_card")

219

atm.send("enter_pin")

220

atm.send("authenticate", pin="1234") # Will succeed

221

```

222

223

### Complex Transition Chains

224

225

```python

226

class WorkflowMachine(StateMachine):

227

start = State(initial=True)

228

step1 = State()

229

step2 = State()

230

step3 = State()

231

completed = State(final=True)

232

error = State(final=True)

233

234

# Chain multiple transitions with shared conditions

235

process = (

236

start.to(step1)

237

| step1.to(step2).cond("step1_valid")

238

| step2.to(step3).cond("step2_valid")

239

| step3.to(completed).cond("step3_valid")

240

).on("log_progress").before("validate_step")

241

242

# Error transitions

243

handle_error = (

244

step1.to(error)

245

| step2.to(error)

246

| step3.to(error)

247

).unless("step_valid")

248

249

def validate_step(self, **kwargs):

250

print("Validating step...")

251

252

def log_progress(self, source: State, target: State, **kwargs):

253

print(f"Moving from {source.id} to {target.id}")

254

255

def step1_valid(self, data=None):

256

return data and data.get("step1_complete", False)

257

258

def step2_valid(self, data=None):

259

return data and data.get("step2_complete", False)

260

261

def step3_valid(self, data=None):

262

return data and data.get("step3_complete", False)

263

264

def step_valid(self, **kwargs):

265

return True # Simplified validation

266

```

267

268

### Internal Transitions

269

270

```python

271

class MediaPlayerMachine(StateMachine):

272

stopped = State(initial=True)

273

playing = State()

274

paused = State()

275

276

play = stopped.to(playing) | paused.to(playing)

277

pause = playing.to(paused)

278

stop = playing.to(stopped) | paused.to(stopped)

279

280

# Internal transition - doesn't trigger entry/exit actions

281

seek = playing.to(playing, internal=True).on("update_position")

282

283

def on_enter_playing(self):

284

print("Starting playback")

285

286

def on_exit_playing(self):

287

print("Stopping playback")

288

289

def update_position(self, position: int):

290

print(f"Seeking to position: {position}")

291

292

# Usage

293

player = MediaPlayerMachine()

294

player.send("play") # Prints: "Starting playback"

295

player.send("seek", position=30) # Prints: "Seeking to position: 30"

296

# No entry/exit messages due to internal=True

297

```

298

299

### Event Data and Parameter Injection

300

301

```python

302

class NotificationMachine(StateMachine):

303

draft = State(initial=True)

304

scheduled = State()

305

sent = State(final=True)

306

307

schedule = draft.to(scheduled)

308

send = scheduled.to(sent)

309

310

def before_schedule(self, event: str, source: State, target: State,

311

when: str = None, recipients: list = None):

312

"""Parameters are automatically injected from event data."""

313

print(f"Scheduling {event} from {source.id} to {target.id}")

314

if when:

315

print(f"Scheduled for: {when}")

316

if recipients:

317

print(f"Recipients: {', '.join(recipients)}")

318

319

def on_send(self, message: str = "Default message"):

320

print(f"Sending: {message}")

321

322

# Usage with event parameters

323

notification = NotificationMachine()

324

notification.send(

325

"schedule",

326

when="2023-12-25 09:00",

327

recipients=["user1@example.com", "user2@example.com"]

328

)

329

notification.send("send", message="Happy Holidays!")

330

```

331

332

### Async Event Handling

333

334

```python

335

import asyncio

336

from statemachine import StateMachine, State

337

338

class AsyncProcessMachine(StateMachine):

339

idle = State(initial=True)

340

processing = State()

341

completed = State(final=True)

342

343

start = idle.to(processing)

344

finish = processing.to(completed)

345

346

async def on_enter_processing(self, task_data=None):

347

"""Async action handler."""

348

print("Starting async processing...")

349

if task_data:

350

await self.process_data(task_data)

351

print("Processing completed")

352

353

async def process_data(self, data):

354

"""Simulate async work."""

355

await asyncio.sleep(2)

356

print(f"Processed: {data}")

357

358

# Usage

359

async def main():

360

machine = AsyncProcessMachine()

361

await machine.send_async("start", task_data="important_data")

362

await machine.send_async("finish")

363

364

asyncio.run(main())

365

```