or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

components.mdcore.mdextensions.mdindex.md

components.mddocs/

0

# State and Transition Components

1

2

Individual components for states, transitions, events, and conditions that can be used to build custom state machine configurations. These components provide fine-grained control over state machine behavior and enable advanced customization.

3

4

## Capabilities

5

6

### State Class

7

8

Represents a persistent state managed by a Machine with entry/exit callbacks and trigger handling.

9

10

```python { .api }

11

class State:

12

def __init__(

13

self,

14

name,

15

on_enter=None,

16

on_exit=None,

17

ignore_invalid_triggers=None,

18

final=False

19

):

20

"""

21

Initialize a state.

22

23

Parameters:

24

- name: The name of the state (str or Enum)

25

- on_enter: Callable(s) triggered when state is entered

26

- on_exit: Callable(s) triggered when state is exited

27

- ignore_invalid_triggers: Override machine's ignore_invalid_triggers setting

28

- final: Whether this is a final state

29

"""

30

31

def enter(self, event_data):

32

"""

33

Triggered when a state is entered.

34

35

Parameters:

36

- event_data: EventData object containing transition context

37

"""

38

39

def exit(self, event_data):

40

"""

41

Triggered when a state is exited.

42

43

Parameters:

44

- event_data: EventData object containing transition context

45

"""

46

47

def add_callback(self, trigger, func):

48

"""

49

Add a new enter or exit callback.

50

51

Parameters:

52

- trigger: 'enter' or 'exit'

53

- func: Callback function to add

54

"""

55

56

@property

57

def name(self):

58

"""The name of the state."""

59

60

@property

61

def value(self):

62

"""The state's value (equivalent to name for string states)."""

63

```

64

65

### Transition Class

66

67

Represents a transition between states with conditions, callbacks, and execution logic.

68

69

```python { .api }

70

class Transition:

71

def __init__(

72

self,

73

source,

74

dest,

75

conditions=None,

76

unless=None,

77

before=None,

78

after=None,

79

prepare=None

80

):

81

"""

82

Initialize a transition.

83

84

Parameters:

85

- source: Source state name

86

- dest: Destination state name

87

- conditions: Callbacks that must return True for transition to execute

88

- unless: Callbacks that must return False for transition to execute

89

- before: Callbacks executed before the transition

90

- after: Callbacks executed after the transition

91

- prepare: Callbacks executed before condition checks

92

"""

93

94

def execute(self, event_data):

95

"""

96

Execute the transition if conditions are met.

97

98

Parameters:

99

- event_data: EventData object containing transition context

100

101

Returns:

102

bool: True if transition was executed, False otherwise

103

"""

104

105

def add_callback(self, trigger, func):

106

"""

107

Add a new before, after, or prepare callback.

108

109

Parameters:

110

- trigger: 'before', 'after', or 'prepare'

111

- func: Callback function to add

112

"""

113

```

114

115

### Event Class

116

117

Manages a collection of transitions assigned to the same trigger and handles event execution.

118

119

```python { .api }

120

class Event:

121

def __init__(self, name, machine):

122

"""

123

Initialize an event.

124

125

Parameters:

126

- name: The name of the event/trigger

127

- machine: The Machine instance this event belongs to

128

"""

129

130

def add_transition(self, transition):

131

"""

132

Add a transition to the list of potential transitions.

133

134

Parameters:

135

- transition: Transition object to add

136

"""

137

138

def trigger(self, model, *args, **kwargs):

139

"""

140

Execute all transitions that match the current state.

141

142

Parameters:

143

- model: The model object to trigger on

144

- args: Positional arguments passed to callbacks

145

- kwargs: Keyword arguments passed to callbacks

146

147

Returns:

148

bool: True if at least one transition was successful

149

"""

150

151

def add_callback(self, trigger, func):

152

"""

153

Add a new before or after callback to all available transitions.

154

155

Parameters:

156

- trigger: 'before' or 'after'

157

- func: Callback function to add

158

"""

159

```

160

161

### EventData Class

162

163

Container for relevant data related to an ongoing transition attempt, passed to all callbacks.

164

165

```python { .api }

166

class EventData:

167

def __init__(self, state, event, machine, model, args, kwargs):

168

"""

169

Initialize event data.

170

171

Parameters:

172

- state: The State from which the Event was triggered

173

- event: The triggering Event

174

- machine: The current Machine instance

175

- model: The model/object the machine is bound to

176

- args: Optional positional arguments from trigger method

177

- kwargs: Optional keyword arguments from trigger method

178

"""

179

180

def update(self, state):

181

"""

182

Update the EventData object with a new state.

183

184

Parameters:

185

- state: New State object

186

"""

187

188

# Attributes available on EventData instances:

189

state: State # The State from which the Event was triggered

190

event: Event # The triggering Event

191

machine: Machine # The current Machine instance

192

model: object # The model/object the machine is bound to

193

args: list # Optional positional arguments from trigger method

194

kwargs: dict # Optional keyword arguments from trigger method

195

transition: Transition # Currently active transition

196

error: Exception # In case a triggered event causes an Error

197

result: bool # True if transition successful, False otherwise

198

```

199

200

### Condition Class

201

202

Helper class for condition checks with target validation support.

203

204

```python { .api }

205

class Condition:

206

def __init__(self, func, target=True):

207

"""

208

Initialize a condition.

209

210

Parameters:

211

- func: The function to call for the condition check (str or callable)

212

- target: Indicates the target state for condition validation

213

"""

214

215

def check(self, event_data):

216

"""

217

Check whether the condition passes.

218

219

Parameters:

220

- event_data: EventData object containing transition context

221

222

Returns:

223

bool: True if condition passes, False otherwise

224

"""

225

```

226

227

## Usage Examples

228

229

### Creating Custom States

230

231

```python

232

from transitions import State, Machine

233

234

# Create states with callbacks

235

def on_enter_working():

236

print("Started working!")

237

238

def on_exit_working():

239

print("Stopped working!")

240

241

working_state = State(

242

name='working',

243

on_enter=on_enter_working,

244

on_exit=on_exit_working

245

)

246

247

# Use in machine

248

states = ['idle', working_state, 'done']

249

machine = Machine(model=MyModel(), states=states, initial='idle')

250

```

251

252

### Creating Custom Transitions with Conditions

253

254

```python

255

from transitions import Machine, Transition

256

257

class Robot:

258

def __init__(self):

259

self.battery = 100

260

self.task_queue = []

261

262

def has_battery(self):

263

return self.battery > 20

264

265

def has_tasks(self):

266

return len(self.task_queue) > 0

267

268

def start_task(self):

269

if self.task_queue:

270

task = self.task_queue.pop(0)

271

print(f"Starting task: {task}")

272

self.battery -= 10

273

274

# Create custom transitions with multiple conditions

275

states = ['idle', 'working', 'charging']

276

277

transitions = [

278

{

279

'trigger': 'start_work',

280

'source': 'idle',

281

'dest': 'working',

282

'conditions': ['has_battery', 'has_tasks'],

283

'before': 'start_task'

284

},

285

{

286

'trigger': 'finish_work',

287

'source': 'working',

288

'dest': 'idle'

289

},

290

{

291

'trigger': 'charge',

292

'source': ['idle', 'working'],

293

'dest': 'charging',

294

'unless': lambda: self.battery > 90 # Don't charge if battery is high

295

}

296

]

297

298

robot = Robot()

299

robot.task_queue = ['task1', 'task2', 'task3']

300

301

machine = Machine(model=robot, states=states, transitions=transitions, initial='idle')

302

303

# Test the conditions

304

robot.start_work() # Will work if battery > 20 and tasks available

305

```

306

307

### Working with EventData

308

309

```python

310

from transitions import Machine

311

312

class LoggingRobot:

313

def __init__(self):

314

self.activity_log = []

315

316

def log_transition(self, event_data):

317

"""Log all transition details"""

318

log_entry = {

319

'from_state': event_data.state.name,

320

'to_state': event_data.transition.dest,

321

'trigger': event_data.event.name,

322

'timestamp': time.time(),

323

'args': event_data.args,

324

'kwargs': event_data.kwargs

325

}

326

self.activity_log.append(log_entry)

327

print(f"Transition: {log_entry['from_state']} -> {log_entry['to_state']} via {log_entry['trigger']}")

328

329

states = ['idle', 'working', 'done']

330

transitions = [

331

{

332

'trigger': 'start',

333

'source': 'idle',

334

'dest': 'working',

335

'after': 'log_transition'

336

},

337

{

338

'trigger': 'finish',

339

'source': 'working',

340

'dest': 'done',

341

'after': 'log_transition'

342

}

343

]

344

345

robot = LoggingRobot()

346

machine = Machine(

347

model=robot,

348

states=states,

349

transitions=transitions,

350

initial='idle',

351

send_event=True # This ensures EventData is passed to callbacks

352

)

353

354

robot.start(task_id=123, priority='high') # Arguments will be in event_data

355

robot.finish(result='success')

356

```

357

358

### Custom Event Handling

359

360

```python

361

from transitions import Machine, Event

362

363

class CustomMachine(Machine):

364

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

365

super().__init__(*args, **kwargs)

366

# Override event creation to add custom behavior

367

368

def _create_event(self, trigger, machine):

369

"""Create custom event with additional logging"""

370

event = Event(trigger, machine)

371

372

# Add custom behavior to all events

373

original_trigger = event.trigger

374

375

def logged_trigger(model, *args, **kwargs):

376

print(f"Event '{trigger}' triggered on {model}")

377

result = original_trigger(model, *args, **kwargs)

378

print(f"Event result: {result}")

379

return result

380

381

event.trigger = logged_trigger

382

return event

383

384

# This machine will log all event triggers

385

machine = CustomMachine(model=MyModel(), states=['A', 'B'], transitions=[

386

{'trigger': 'go', 'source': 'A', 'dest': 'B'}

387

], initial='A')

388

```

389

390

### Conditional Transitions with Complex Logic

391

392

```python

393

from transitions import Machine, Condition

394

395

class SmartThermostat:

396

def __init__(self):

397

self.temperature = 20

398

self.target_temp = 22

399

self.hvac_enabled = True

400

401

def too_cold(self):

402

return self.temperature < self.target_temp - 1

403

404

def too_hot(self):

405

return self.temperature > self.target_temp + 1

406

407

def hvac_available(self):

408

return self.hvac_enabled

409

410

def start_heating(self):

411

print(f"Starting heating. Current: {self.temperature}°C, Target: {self.target_temp}°C")

412

413

def start_cooling(self):

414

print(f"Starting cooling. Current: {self.temperature}°C, Target: {self.target_temp}°C")

415

416

states = ['idle', 'heating', 'cooling', 'error']

417

418

transitions = [

419

{

420

'trigger': 'check_temperature',

421

'source': 'idle',

422

'dest': 'heating',

423

'conditions': ['too_cold', 'hvac_available'],

424

'before': 'start_heating'

425

},

426

{

427

'trigger': 'check_temperature',

428

'source': 'idle',

429

'dest': 'cooling',

430

'conditions': ['too_hot', 'hvac_available'],

431

'before': 'start_cooling'

432

},

433

{

434

'trigger': 'check_temperature',

435

'source': ['heating', 'cooling'],

436

'dest': 'idle',

437

'unless': ['too_cold', 'too_hot'] # Return to idle if temperature is right

438

},

439

{

440

'trigger': 'hvac_failure',

441

'source': ['heating', 'cooling'],

442

'dest': 'error'

443

}

444

]

445

446

thermostat = SmartThermostat()

447

machine = Machine(model=thermostat, states=states, transitions=transitions, initial='idle')

448

449

# Simulate temperature changes

450

thermostat.temperature = 18 # Too cold

451

thermostat.check_temperature() # Should start heating

452

453

thermostat.temperature = 22 # Right temperature

454

thermostat.check_temperature() # Should return to idle

455

```