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
[](https://github.com/fgmacedo/python-statemachine/compare/main...develop)
9
9
10
10
11
11
Python [finite-state machines](https://en.wikipedia.org/wiki/Finite-state_machine) made easy.
Welcome to python-statemachine, an intuitive and powerful state machine framework designed for a
19
-
great developer experience.
19
+
Welcome to python-statemachine, an intuitive and powerful state machine library designed for a
20
+
great developer experience. We provide an _pythonic_ and expressive API for implementing state
21
+
machines in sync or asynchonous Python codebases.
20
22
21
-
🚀 With StateMachine, you can easily create complex, dynamic systems with clean, readable code.
23
+
## Features
22
24
23
-
💡 Our framework makes it easy to understand and reason about the different states, events and
24
-
transitions in your system, so you can focus on building great products.
25
+
- ✨ **Basic components**: Easily define **States**, **Events**, and **Transitions** to model your logic.
26
+
- ⚙️ **Actions and handlers**: Attach actions and handlers to states, events, and transitions to control behavior dynamically.
27
+
- 🛡️ **Conditional transitions**: Implement **Guards** and **Validators** to conditionally control transitions, ensuring they only occur when specific conditions are met.
28
+
- 🚀 **Full async support**: Enjoy full asynchronous support. Await events, and dispatch callbacks asynchronously for seamless integration with async codebases.
29
+
- 🔄 **Full sync support**: Use the same state machine from synchronous codebases without any modifications.
30
+
- 🎨 **Declarative and simple API**: Utilize a clean, elegant, and readable API to define your state machine, making it easy to maintain and understand.
31
+
- 👀 **Observer pattern support**: Register external and generic objects to watch events and register callbacks.
32
+
- 🔍 **Decoupled design**: Separate concerns with a decoupled "state machine" and "model" design, promoting cleaner architecture and easier maintenance.
33
+
- ✅ **Correctness guarantees**: Ensured correctness with validations at class definition time:
34
+
- Ensures exactly one `initial` state.
35
+
- Disallows transitions from `final` states.
36
+
- Requires ongoing transitions for all non-final states.
37
+
- Guarantees all non-final states have at least one path to a final state if final states are declared.
38
+
- Validates the state machine graph representation has a single component.
39
+
- 📦 **Flexible event dispatching**: Dispatch events with any extra data, making it available to all callbacks, including actions and guards.
40
+
- 🔧 **Dependency injection**: Needed parameters are injected into callbacks.
41
+
- 📊 **Graphical representation**: Generate and output graphical representations of state machines. Create diagrams from the command line, at runtime, or even in Jupyter notebooks.
42
+
- 🌍 **Internationalization support**: Provides error messages in different languages, making the library accessible to a global audience.
43
+
- 🛡️ **Robust testing**: Ensured reliability with a codebase that is 100% covered by automated tests, including all docs examples. Releases follow semantic versioning for predictable releases.
44
+
- 🏛️ **Domain model integration**: Seamlessly integrate with domain models using Mixins.
45
+
- 🔧 **Django integration**: Automatically discover state machines in Django applications.
25
46
26
-
🔒 python-statemachine also provides robust error handling and ensures that your system stays
27
-
in a valid state at all times.
28
47
29
48
30
-
A few reasons why you may consider using it:
31
-
32
-
* 📈 python-statemachine is designed to help you build scalable,
33
-
maintainable systems that can handle any complexity.
34
-
* 💪 You can easily create and manage multiple state machines within a single application.
35
-
* 🚫 Prevents common mistakes and ensures that your system stays in a valid state at all times.
36
-
37
-
38
-
## Getting started
39
-
49
+
## Installing
40
50
41
51
To install Python State Machine, run this command in your terminal:
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
0 commit comments