You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/actions.md
+11-12Lines changed: 11 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,7 +5,7 @@ outside world, and indeed they are the main reason why they exist at all.
5
5
6
6
The main point of introducing a state machine is for the
7
7
actions to be invoked at the right times, depending on the sequence of events
8
-
and the state of the guards.
8
+
and the state of the {ref}`conditions`.
9
9
10
10
Actions are most commonly performed on entry or exit of a state, although
11
11
it is possible to add them before/after a transition.
@@ -79,7 +79,7 @@ After 'go', on the 'final' state.
79
79
80
80
81
81
```{seealso}
82
-
All actions and {ref}`guards` support multiple method signatures. They follow the
82
+
All actions and {ref}`conditions` support multiple method signatures. They follow the
83
83
{ref}`dynamic-dispatch` method calling implemented on this library.
84
84
```
85
85
@@ -298,7 +298,7 @@ On loop
298
298
In addition to {ref}`actions`, you can specify {ref}`validators and guards` that are checked before a transition is started. They are meant to stop a transition to occur.
299
299
300
300
```{seealso}
301
-
See {ref}`guards` and {ref}`validators`.
301
+
See {ref}`conditions` and {ref}`validators`.
302
302
```
303
303
304
304
@@ -377,21 +377,20 @@ For {ref}`RTC model`, only the main event will get its value list, while the cha
377
377
378
378
379
379
(dynamic-dispatch)=
380
-
## Dynamic dispatch
380
+
(dynamic dispatch)=
381
+
## Dependency injection
381
382
382
-
{ref}`statemachine` implements a custom dispatch mechanism on all those available Actions and
383
-
Guards. This means that you can declare an arbitrary number of `*args` and `**kwargs`, and the
384
-
library will match your method signature of what's expected to receive with the provided arguments.
383
+
{ref}`statemachine` implements a dependency injection mechanism on all available {ref}`Actions` and
384
+
{ref}`Conditions` that automatically inspects and matches the expected callback params with those available by the library in conjunction with any values informed when calling an event using `*args` and `**kwargs`.
385
385
386
-
This means that if on your `on_enter_<state.id>()` or `on_<event>()` method, you need to know
387
-
the `source` ({ref}`state`), or the `event` ({ref}`event`), or access a keyword
388
-
argument passed with the trigger, just add this parameter to the method and It will be passed
389
-
by the dispatch mechanics.
386
+
The library ensures that your method signatures match the expected arguments.
387
+
388
+
For example, if you need to access the source (state), the event (event), or any keyword arguments passed with the trigger in any method, simply include these parameters in the method. They will be automatically passed by the dependency injection dispatch mechanics.
390
389
391
390
In other words, if you implement a method to handle an event and don't declare any parameter,
392
391
you'll be fine, if you declare an expected parameter, you'll also be covered.
393
392
394
-
For your convenience, all these parameters are available for you on any Action or Guard:
393
+
For your convenience, all these parameters are available for you on any callback:
The {ref}`StateMachine` has full async suport. You can write async {ref}`actions`, {ref}`guards` and {ref}`event` triggers.
8
+
9
+
Keeping the same external API do interact both on sync or async codebases.
10
+
11
+
```{note}
12
+
All the handlers will run on the same thread they're called. So it's not recommended to mix sync with async code unless
13
+
you know what you're doing.
14
+
```
15
+
16
+
## Asynchronous Support
17
+
18
+
We support native coroutine using asyncio, enabling seamless integration with asynchronous code.
19
+
There's no change on the public API of the library to work on async codebases.
20
+
21
+
One requirement is that when running on an async code, you must manually await for the {ref}`initial state activation` to be able to check the current state.
22
+
23
+
24
+
```{seealso}
25
+
See {ref}`sphx_glr_auto_examples_air_conditioner_machine.py` for an example of
26
+
async code with a state machine.
27
+
```
28
+
29
+
30
+
```py
31
+
>>>classAsyncStateMachine(StateMachine):
32
+
... initial = State('Initial', initial=True)
33
+
... final = State('Final', final=True)
34
+
...
35
+
... advance = initial.to(final)
36
+
...
37
+
...asyncdefon_advance(self):
38
+
...return42
39
+
40
+
>>>asyncdefrun_sm():
41
+
... sm = AsyncStateMachine()
42
+
... result =await sm.advance()
43
+
...print(f"Result is {result}")
44
+
...print(sm.current_state)
45
+
46
+
>>> asyncio.run(run_sm())
47
+
Result is42
48
+
Final
49
+
50
+
```
51
+
52
+
## Sync codebase with async handlers
53
+
54
+
The same state machine can be executed on a sync codebase, even if it contains async handlers. The handlers will be
55
+
awaited on an `asyncio.get_event_loop()` if needed.
56
+
57
+
```py
58
+
>>> sm = AsyncStateMachine()
59
+
>>> result = sm.advance()
60
+
>>>print(f"Result is {result}")
61
+
Result is42
62
+
>>>print(sm.current_state)
63
+
Final
64
+
65
+
```
66
+
67
+
68
+
(initial state activation)=
69
+
## Initial State Activation for Async Code
70
+
71
+
When working with asynchronous state machines from async code, users must manually [activate initial state](statemachine.StateMachine.activate_initial_state) to be able to check the current state. This change ensures proper state initialization and
72
+
execution flow given that Python don't allow awaiting at class initalization time and the initial state activation
Copy file name to clipboardExpand all lines: docs/guards.md
+19-16Lines changed: 19 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,34 +1,32 @@
1
1
(validators-and-guards)=
2
-
# Validators and guards
2
+
(validators and guards)=
3
+
# Conditions and Validators
3
4
4
-
Validations and Guards are checked before a transition is started. They are meant to stop a
5
+
Conditions and Validations are checked before a transition is started. They are meant to prevent or stop a
5
6
transition to occur.
6
7
7
-
The main difference is that {ref}`validators` raise exceptions to stop the flow, and {ref}`guards`
8
+
The main difference is that {ref}`validators` raise exceptions to stop the flow, and {ref}`conditions`
8
9
act like predicates that shall resolve to a ``boolean`` value.
9
10
10
11
```{seealso}
11
12
Please see {ref}`dynamic-dispatch` to know more about how this lib supports multiple signatures
12
13
for all the available callbacks, being validators and guards or {ref}`actions`.
13
14
```
14
15
15
-
## Guards
16
+
(guards)=
17
+
## Conditions
16
18
17
-
Also known as **Conditional transition**.
19
+
This feature is also known as a **Conditional Transition**.
18
20
19
-
A guard is a condition that may be checked when a {ref}`statemachine` wants to handle
20
-
an {ref}`event`. A guard is declared on the {ref}`transition`, and when that {ref}`transition`
21
-
would trigger, then the guard (if any) is checked. If the guard is `True`
22
-
then the transition does happen. If the guard is `False`, the transition
23
-
is ignored.
21
+
A conditional transition occurs only if specific conditions or criteria are met. In addition to checking if there is a transition handling the event in the current state, you can register callbacks that are evaluated based on other factors or inputs at runtime.
24
22
25
-
When {ref}`transitions` have guards, then it's possible to define two or more
26
-
transitions for the same {ref}`event` from the same {ref}`state`. When the {ref}`event` happens, then
27
-
the guarded transitions are checked, one by one, and the first transition
28
-
whose guard is true will be used, and the others will be ignored.
23
+
When a transition is conditional, it includes a condition (also known as a _guard_) that must be satisfied for the transition to take place. If the condition is not met, the transition does not occur, and the state machine remains in its current state or follows an alternative path.
29
24
30
-
A guard is generally a boolean function or boolean variable and must not have any side effects.
31
-
Side effects are reserved for {ref}`actions`.
25
+
This feature allows for multiple transitions on the same {ref}`event`, with each {ref}`transition` checked in the order they are declared. A condition acts like a predicate (a function that evaluates to true/false) and is checked when a {ref}`statemachine` handles an {ref}`event` with a transition from the current state bound to this event. The first transition that meets the conditions (if any) is executed. If none of the transitions meet the conditions, the state machine either raises an exception or does nothing (see the `allow_event_without_transition` parameter of {ref}`StateMachine`).
26
+
27
+
When {ref}`transitions` have guards, it is possible to define two or more transitions for the same {ref}`event` from the same {ref}`state`. When the {ref}`event` occurs, the guarded transitions are checked one by one, and the first transition whose guard is true will be executed, while the others will be ignored.
28
+
29
+
A condition is generally a boolean function, property, or attribute, and must not have any side effects. Side effects are reserved for {ref}`actions`.
32
30
33
31
There are two variations of Guard clauses available:
In this release, we conducted a significant update focusing on adding asynchronous support, and enhancing overall functionality. In fact, the approach we took was to go all the way down changing the internals of the library to be fully async, keeping only the current external API as a thin sync wrapper.
8
+
9
+
Here are the major changes and new features:
10
+
11
+
### Asynchronous Support in 2.3.0
12
+
13
+
This release introduces native coroutine support using asyncio, enabling seamless integration with asynchronous code.
14
+
15
+
Now you can send and await for events, and also write async {ref}`Actions`, {ref}`Conditions` and {ref}`Validators`.
16
+
17
+
18
+
```{seealso}
19
+
See {ref}`sphx_glr_auto_examples_air_conditioner_machine.py` for an example of
20
+
async code with a state machine.
21
+
```
22
+
23
+
24
+
```py
25
+
>>>classAsyncStateMachine(StateMachine):
26
+
... initial = State('Initial', initial=True)
27
+
... final = State('Final', final=True)
28
+
...
29
+
... advance = initial.to(final)
30
+
31
+
>>>asyncdefrun_sm():
32
+
... sm = AsyncStateMachine()
33
+
...await sm.advance()
34
+
...print(sm.current_state)
35
+
36
+
>>> asyncio.run(run_sm())
37
+
Final
38
+
39
+
```
40
+
41
+
42
+
### Manual Initial State Activation for Async Code
43
+
44
+
When working with asynchronous state machines from async code, users must manually activate
45
+
the initial state . This change ensures proper state initialization and execution flow given that
46
+
Python don't allow awaiting at class initalization time and the initial state activation may contain
0 commit comments