python-statemachine 2.3.6__py3-none-any.whl → 2.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_statemachine-2.3.6.dist-info → python_statemachine-2.5.0.dist-info}/METADATA +22 -23
- python_statemachine-2.5.0.dist-info/RECORD +37 -0
- {python_statemachine-2.3.6.dist-info → python_statemachine-2.5.0.dist-info}/WHEEL +1 -1
- statemachine/__init__.py +3 -2
- statemachine/callbacks.py +82 -67
- statemachine/contrib/diagram.py +4 -8
- statemachine/dispatcher.py +122 -49
- statemachine/engines/async_.py +27 -34
- statemachine/engines/base.py +40 -0
- statemachine/engines/sync.py +29 -37
- statemachine/event.py +124 -29
- statemachine/event_data.py +2 -1
- statemachine/events.py +14 -6
- statemachine/exceptions.py +3 -2
- statemachine/factory.py +53 -22
- statemachine/locale/en/LC_MESSAGES/statemachine.po +27 -15
- statemachine/locale/hi_IN/LC_MESSAGES/statemachine.po +93 -0
- statemachine/locale/pt_BR/LC_MESSAGES/statemachine.po +36 -34
- statemachine/locale/zh_CN/LC_MESSAGES/statemachine.po +93 -0
- statemachine/signature.py +7 -26
- statemachine/spec_parser.py +150 -0
- statemachine/state.py +66 -27
- statemachine/statemachine.py +52 -73
- statemachine/states.py +2 -2
- statemachine/transition.py +23 -4
- statemachine/transition_list.py +17 -79
- statemachine/transition_mixin.py +82 -0
- python_statemachine-2.3.6.dist-info/RECORD +0 -32
- {python_statemachine-2.3.6.dist-info → python_statemachine-2.5.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: python-statemachine
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: Python Finite State Machines made easy.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
Maintainer-email: fgmacedo@gmail.com
|
|
11
|
-
Requires-Python: >=3.7
|
|
5
|
+
Project-URL: homepage, https://github.com/fgmacedo/python-statemachine
|
|
6
|
+
Author-email: Fernando Macedo <fgmacedo@gmail.com>
|
|
7
|
+
Maintainer-email: Fernando Macedo <fgmacedo@gmail.com>
|
|
8
|
+
License: MIT License
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
10
|
Classifier: Framework :: AsyncIO
|
|
11
|
+
Classifier: Framework :: Django
|
|
13
12
|
Classifier: Intended Audience :: Developers
|
|
14
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
14
|
Classifier: Natural Language :: English
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.7
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.8
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -21,9 +19,11 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Home Automation
|
|
24
23
|
Classifier: Topic :: Software Development :: Libraries
|
|
24
|
+
Requires-Python: >=3.7
|
|
25
25
|
Provides-Extra: diagrams
|
|
26
|
-
Requires-Dist: pydot
|
|
26
|
+
Requires-Dist: pydot>=2.0.0; extra == 'diagrams'
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
29
29
|
# Python StateMachine
|
|
@@ -196,19 +196,19 @@ Easily iterate over all states:
|
|
|
196
196
|
|
|
197
197
|
```py
|
|
198
198
|
>>> [s.id for s in sm.states]
|
|
199
|
-
['green', '
|
|
199
|
+
['green', 'yellow', 'red']
|
|
200
200
|
|
|
201
201
|
```
|
|
202
202
|
|
|
203
203
|
Or over events:
|
|
204
204
|
|
|
205
205
|
```py
|
|
206
|
-
>>> [t.
|
|
206
|
+
>>> [t.id for t in sm.events]
|
|
207
207
|
['cycle']
|
|
208
208
|
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
-
Call an event by its
|
|
211
|
+
Call an event by its id:
|
|
212
212
|
|
|
213
213
|
```py
|
|
214
214
|
>>> sm.cycle()
|
|
@@ -216,7 +216,7 @@ Don't move.
|
|
|
216
216
|
'Running cycle from yellow to red'
|
|
217
217
|
|
|
218
218
|
```
|
|
219
|
-
Or send an event with the event
|
|
219
|
+
Or send an event with the event id:
|
|
220
220
|
|
|
221
221
|
```py
|
|
222
222
|
>>> sm.send('cycle')
|
|
@@ -405,7 +405,7 @@ There's a lot more to cover, please take a look at our docs:
|
|
|
405
405
|
https://python-statemachine.readthedocs.io.
|
|
406
406
|
|
|
407
407
|
|
|
408
|
-
## Contributing
|
|
408
|
+
## Contributing
|
|
409
409
|
|
|
410
410
|
* <a class="github-button" href="https://github.com/fgmacedo/python-statemachine" data-icon="octicon-star" aria-label="Star fgmacedo/python-statemachine on GitHub">Star this project</a>
|
|
411
411
|
* <a class="github-button" href="https://github.com/fgmacedo/python-statemachine/issues" data-icon="octicon-issue-opened" aria-label="Issue fgmacedo/python-statemachine on GitHub">Open an Issue</a>
|
|
@@ -413,18 +413,17 @@ https://python-statemachine.readthedocs.io.
|
|
|
413
413
|
|
|
414
414
|
- If you found this project helpful, please consider giving it a star on GitHub.
|
|
415
415
|
|
|
416
|
-
- **Contribute code**: If you would like to contribute code
|
|
416
|
+
- **Contribute code**: If you would like to contribute code, please submit a pull
|
|
417
417
|
request. For more information on how to contribute, please see our [contributing.md](contributing.md) file.
|
|
418
418
|
|
|
419
|
-
- **Report bugs**: If you find any bugs
|
|
419
|
+
- **Report bugs**: If you find any bugs, please report them by opening an issue
|
|
420
420
|
on our GitHub issue tracker.
|
|
421
421
|
|
|
422
|
-
- **Suggest features**: If you have
|
|
423
|
-
an issue on our GitHub issue tracker.
|
|
422
|
+
- **Suggest features**: If you have an idea for a new feature, of feels something being harder than it should be,
|
|
423
|
+
please let us know by opening an issue on our GitHub issue tracker.
|
|
424
424
|
|
|
425
|
-
- **Documentation**: Help improve
|
|
425
|
+
- **Documentation**: Help improve documentation by submitting pull requests.
|
|
426
426
|
|
|
427
|
-
- **Promote the project**: Help spread the word
|
|
427
|
+
- **Promote the project**: Help spread the word by sharing on social media,
|
|
428
428
|
writing a blog post, or giving a talk about it. Tag me on Twitter
|
|
429
429
|
[@fgmacedo](https://twitter.com/fgmacedo) so I can share it too!
|
|
430
|
-
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
statemachine/__init__.py,sha256=JsTxT_XFohxEg-P6qmBYcxNTwzW-A2a9zLIk7wpRzaM,226
|
|
2
|
+
statemachine/callbacks.py,sha256=UUOuMotQDV3ZkpqjWp3CIL3UrA70Bh_xYDf4Ugjlou8,11475
|
|
3
|
+
statemachine/dispatcher.py,sha256=Ai8i79Lo5HdUJ-toOVg4NDUzAy88sryBHDbYdl_7sWE,7785
|
|
4
|
+
statemachine/event.py,sha256=DhAVtoPGCHv95r3QOuL-Tn93UMWXPhiuODFcc9FPUPA,4586
|
|
5
|
+
statemachine/event_data.py,sha256=H9lp_XnvHSK9YErUOCvMK3ZBjWhC-xDSOJ2gZxtmrq8,2261
|
|
6
|
+
statemachine/events.py,sha256=UTYJu8te_bxiORTQpoXY5tB_x-ymVPWDOREcxCyhExA,1018
|
|
7
|
+
statemachine/exceptions.py,sha256=vHVQPTTMMkVvySNbN6XZPciBryvpY608LDe3MCnmxFU,1124
|
|
8
|
+
statemachine/factory.py,sha256=crL2FPfzdku3STlbxaF9N3oV9L1BFWoCAT9K3zEnBYo,9219
|
|
9
|
+
statemachine/graph.py,sha256=KtwB1CYckaLjTgQD9tEeuaEzJje9q3fPVpBViW5TgSk,487
|
|
10
|
+
statemachine/i18n.py,sha256=NLvGseaORmQ0G-V_J8tkjoxh_piWMOm2CI6mBQpLamc,362
|
|
11
|
+
statemachine/mixins.py,sha256=Y1fa52Cj20JaGkyNk3P7Gpqkt4cGTjJ0YyV_VQyCl0M,1231
|
|
12
|
+
statemachine/model.py,sha256=OylI3FjMiHpYyDl9mtK1zEJMeSvemaN4giQDonpc8kI,211
|
|
13
|
+
statemachine/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
statemachine/registry.py,sha256=HmV9sUGkYVrNUZxJYoZo-trSUis7dIun_WcGktblgc8,922
|
|
15
|
+
statemachine/signature.py,sha256=mZjYXjMAF2XNLxI_MfTYoReJxTuPBHExEm7H76yueWY,7117
|
|
16
|
+
statemachine/spec_parser.py,sha256=DRG-_6bWRTCTgU6qZHqRp5aOxFRZP0O_4VvVxyleItA,5686
|
|
17
|
+
statemachine/state.py,sha256=a2uJZyn8sM8HdnVN_FesCgcjoeDE8gfXCAChbLm4p9g,9504
|
|
18
|
+
statemachine/statemachine.py,sha256=mznQ4NpiysrHm1Ef3JPGRQ07gSXzAD9bER3r_ou9d40,10881
|
|
19
|
+
statemachine/states.py,sha256=pPROZwIyE3_tlBGL3DJXnM3gr1pWsWICEMMo2c742RY,4889
|
|
20
|
+
statemachine/transition.py,sha256=YDpI6NuCipW4cF4GVIt3o8ACE6VhWAQWHaZKjmjSQRA,5335
|
|
21
|
+
statemachine/transition_list.py,sha256=I9viQ0zr6E8oL3WEESkst9DwQeTNJ6nwDAQXsWeo3_c,4179
|
|
22
|
+
statemachine/transition_mixin.py,sha256=OGKF-hMyTEZDtlK_XxGaa5OqxYjmUtThMac9tDQUKUY,2615
|
|
23
|
+
statemachine/utils.py,sha256=DpcrGqlbrnT-ogh-BogG0L07EG3KirHOsKORHlspDlI,1041
|
|
24
|
+
statemachine/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
+
statemachine/contrib/diagram.py,sha256=CVNIhBTedik_02b-4KUua_3_HtiO1TYQNWOg3ulkqiE,7159
|
|
26
|
+
statemachine/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
+
statemachine/engines/async_.py,sha256=HA9EtS7vYS5tulvdwH2NnTMkpUddcDZPXVNckSQFAak,5212
|
|
28
|
+
statemachine/engines/base.py,sha256=d65JvngOrp8sE4RR8i2wrd72wzIci9-3InMLyGdxsZQ,1201
|
|
29
|
+
statemachine/engines/sync.py,sha256=Cys05fn7zQf3WowdIaVrdD8mAVQ-HEjbTMRngv_Z4cU,5035
|
|
30
|
+
statemachine/locale/en/LC_MESSAGES/statemachine.po,sha256=4Pk-h5nk7twOTHcRQ_Chanfdi5EtFi9aTAzGkVJuCo8,2424
|
|
31
|
+
statemachine/locale/hi_IN/LC_MESSAGES/statemachine.po,sha256=Bs5bbIxDrYtODSNJKNl4FH8ZtjEJwPIidRwbAxMwu5E,4941
|
|
32
|
+
statemachine/locale/pt_BR/LC_MESSAGES/statemachine.po,sha256=gmnhc15-6YVDCLWYT0ZQL5jfXHgIOYq5p5rJLqNPaxE,3593
|
|
33
|
+
statemachine/locale/zh_CN/LC_MESSAGES/statemachine.po,sha256=cDbRHDYpi3pwJkFmaSn79q5KmX9cGmHFHw1ndj37vRw,3325
|
|
34
|
+
python_statemachine-2.5.0.dist-info/METADATA,sha256=LzU6fCMlw6gANXQeCCgRpyVRorsAzvF8UMWERtnowng,14068
|
|
35
|
+
python_statemachine-2.5.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
36
|
+
python_statemachine-2.5.0.dist-info/licenses/LICENSE,sha256=zcP7TsJMqaFxuTvLRZPT7nJl3_ppjxR9Z76BE9pL5zc,1074
|
|
37
|
+
python_statemachine-2.5.0.dist-info/RECORD,,
|
statemachine/__init__.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
from .event import Event
|
|
1
2
|
from .state import State
|
|
2
3
|
from .statemachine import StateMachine
|
|
3
4
|
|
|
4
5
|
__author__ = """Fernando Macedo"""
|
|
5
6
|
__email__ = "fgmacedo@gmail.com"
|
|
6
|
-
__version__ = "2.
|
|
7
|
+
__version__ = "2.5.0"
|
|
7
8
|
|
|
8
|
-
__all__ = ["StateMachine", "State"]
|
|
9
|
+
__all__ = ["StateMachine", "State", "Event"]
|
statemachine/callbacks.py
CHANGED
|
@@ -6,18 +6,22 @@ from enum import IntEnum
|
|
|
6
6
|
from enum import IntFlag
|
|
7
7
|
from enum import auto
|
|
8
8
|
from inspect import isawaitable
|
|
9
|
-
from
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
10
|
from typing import Callable
|
|
11
11
|
from typing import Dict
|
|
12
|
-
from typing import Generator
|
|
13
|
-
from typing import Iterable
|
|
14
12
|
from typing import List
|
|
15
|
-
from typing import Type
|
|
16
13
|
|
|
17
14
|
from .exceptions import AttrNotFound
|
|
18
15
|
from .i18n import _
|
|
19
16
|
from .utils import ensure_iterable
|
|
20
17
|
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from typing import Set
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def allways_true(*args, **kwargs):
|
|
23
|
+
return True
|
|
24
|
+
|
|
21
25
|
|
|
22
26
|
class CallbackPriority(IntEnum):
|
|
23
27
|
GENERIC = 0
|
|
@@ -50,10 +54,6 @@ class CallbackGroup(IntEnum):
|
|
|
50
54
|
return f"{self.name}@{id(specs)}"
|
|
51
55
|
|
|
52
56
|
|
|
53
|
-
def allways_true(*args, **kwargs):
|
|
54
|
-
return True
|
|
55
|
-
|
|
56
|
-
|
|
57
57
|
class CallbackSpec:
|
|
58
58
|
"""Specs about callbacks.
|
|
59
59
|
|
|
@@ -63,11 +63,15 @@ class CallbackSpec:
|
|
|
63
63
|
before any real call is performed.
|
|
64
64
|
"""
|
|
65
65
|
|
|
66
|
+
names_not_found: "Set[str] | None" = None
|
|
67
|
+
"""List of names that were not found on the model or statemachine"""
|
|
68
|
+
|
|
66
69
|
def __init__(
|
|
67
70
|
self,
|
|
68
71
|
func,
|
|
69
72
|
group: CallbackGroup,
|
|
70
73
|
is_convention=False,
|
|
74
|
+
is_event: bool = False,
|
|
71
75
|
cond=None,
|
|
72
76
|
priority: CallbackPriority = CallbackPriority.NAMING,
|
|
73
77
|
expected_value=None,
|
|
@@ -75,6 +79,7 @@ class CallbackSpec:
|
|
|
75
79
|
self.func = func
|
|
76
80
|
self.group = group
|
|
77
81
|
self.is_convention = is_convention
|
|
82
|
+
self.is_event = is_event
|
|
78
83
|
self.cond = cond
|
|
79
84
|
self.expected_value = expected_value
|
|
80
85
|
self.priority = priority
|
|
@@ -85,11 +90,22 @@ class CallbackSpec:
|
|
|
85
90
|
elif callable(func):
|
|
86
91
|
self.reference = SpecReference.CALLABLE
|
|
87
92
|
self.is_bounded = hasattr(func, "__self__")
|
|
88
|
-
self.attr_name =
|
|
93
|
+
self.attr_name = (
|
|
94
|
+
func.__name__ if not self.is_event or self.is_bounded else f"_{func.__name__}_"
|
|
95
|
+
)
|
|
96
|
+
if not self.is_bounded:
|
|
97
|
+
func.attr_name = self.attr_name
|
|
98
|
+
func.is_event = is_event
|
|
89
99
|
else:
|
|
90
100
|
self.reference = SpecReference.NAME
|
|
91
101
|
self.attr_name = func
|
|
92
102
|
|
|
103
|
+
self.may_contain_boolean_expression = (
|
|
104
|
+
not self.is_convention
|
|
105
|
+
and self.group == CallbackGroup.COND
|
|
106
|
+
and self.reference == SpecReference.NAME
|
|
107
|
+
)
|
|
108
|
+
|
|
93
109
|
def __repr__(self):
|
|
94
110
|
return f"{type(self).__name__}({self.func!r}, is_convention={self.is_convention!r})"
|
|
95
111
|
|
|
@@ -105,44 +121,19 @@ class CallbackSpec:
|
|
|
105
121
|
def __hash__(self):
|
|
106
122
|
return id(self)
|
|
107
123
|
|
|
108
|
-
def _update_func(self, func: Callable, attr_name: str):
|
|
109
|
-
self.func = func
|
|
110
|
-
self.reference = SpecReference.CALLABLE
|
|
111
|
-
self.attr_name = attr_name
|
|
112
|
-
|
|
113
|
-
def build(self, resolver) -> Generator["CallbackWrapper", None, None]:
|
|
114
|
-
"""
|
|
115
|
-
Resolves the `func` into a usable callable.
|
|
116
|
-
|
|
117
|
-
Args:
|
|
118
|
-
resolver (callable): A method responsible to build and return a valid callable that
|
|
119
|
-
can receive arbitrary parameters like `*args, **kwargs`.
|
|
120
|
-
"""
|
|
121
|
-
for callback in resolver.search(self):
|
|
122
|
-
condition = self.cond if self.cond is not None else allways_true
|
|
123
|
-
yield CallbackWrapper(
|
|
124
|
-
callback=callback,
|
|
125
|
-
condition=condition,
|
|
126
|
-
meta=self,
|
|
127
|
-
unique_key=callback.unique_key,
|
|
128
|
-
)
|
|
129
|
-
|
|
130
124
|
|
|
131
125
|
class SpecListGrouper:
|
|
132
|
-
def __init__(
|
|
133
|
-
self, list: "CallbackSpecList", group: CallbackGroup, factory=CallbackSpec
|
|
134
|
-
) -> None:
|
|
126
|
+
def __init__(self, list: "CallbackSpecList", group: CallbackGroup) -> None:
|
|
135
127
|
self.list = list
|
|
136
128
|
self.group = group
|
|
137
|
-
self.factory = factory
|
|
138
129
|
self.key = group.build_key(list)
|
|
139
130
|
|
|
140
131
|
def add(self, callbacks, **kwargs):
|
|
141
|
-
self.list.add(callbacks, group=self.group,
|
|
132
|
+
self.list.add(callbacks, group=self.group, **kwargs)
|
|
142
133
|
return self
|
|
143
134
|
|
|
144
135
|
def __call__(self, callback):
|
|
145
|
-
return self.list._add_unbounded_callback(callback, group=self.group
|
|
136
|
+
return self.list._add_unbounded_callback(callback, group=self.group)
|
|
146
137
|
|
|
147
138
|
def _add_unbounded_callback(self, func, is_event=False, transitions=None, **kwargs):
|
|
148
139
|
self.list._add_unbounded_callback(
|
|
@@ -150,7 +141,6 @@ class SpecListGrouper:
|
|
|
150
141
|
is_event=is_event,
|
|
151
142
|
transitions=transitions,
|
|
152
143
|
group=self.group,
|
|
153
|
-
factory=self.factory,
|
|
154
144
|
**kwargs,
|
|
155
145
|
)
|
|
156
146
|
|
|
@@ -164,12 +154,13 @@ class CallbackSpecList:
|
|
|
164
154
|
def __init__(self, factory=CallbackSpec):
|
|
165
155
|
self.items: List[CallbackSpec] = []
|
|
166
156
|
self.conventional_specs = set()
|
|
157
|
+
self._groupers: Dict[CallbackGroup, SpecListGrouper] = {}
|
|
167
158
|
self.factory = factory
|
|
168
159
|
|
|
169
160
|
def __repr__(self):
|
|
170
161
|
return f"{type(self).__name__}({self.items!r}, factory={self.factory!r})"
|
|
171
162
|
|
|
172
|
-
def _add_unbounded_callback(self, func,
|
|
163
|
+
def _add_unbounded_callback(self, func, transitions=None, **kwargs):
|
|
173
164
|
"""This list was a target for adding a func using decorator
|
|
174
165
|
`@<state|event>[.on|before|after|enter|exit]` syntax.
|
|
175
166
|
|
|
@@ -192,11 +183,7 @@ class CallbackSpecList:
|
|
|
192
183
|
event.
|
|
193
184
|
|
|
194
185
|
"""
|
|
195
|
-
|
|
196
|
-
if not getattr(func, "_specs_to_update", None):
|
|
197
|
-
func._specs_to_update = set()
|
|
198
|
-
if is_event:
|
|
199
|
-
func._specs_to_update.add(spec._update_func)
|
|
186
|
+
self._add(func, **kwargs)
|
|
200
187
|
func._transitions = transitions
|
|
201
188
|
|
|
202
189
|
return func
|
|
@@ -207,15 +194,16 @@ class CallbackSpecList:
|
|
|
207
194
|
def clear(self):
|
|
208
195
|
self.items = []
|
|
209
196
|
|
|
210
|
-
def grouper(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
return
|
|
197
|
+
def grouper(self, group: CallbackGroup) -> SpecListGrouper:
|
|
198
|
+
if group not in self._groupers:
|
|
199
|
+
self._groupers[group] = SpecListGrouper(self, group)
|
|
200
|
+
return self._groupers[group]
|
|
214
201
|
|
|
215
|
-
def _add(self, func, group: CallbackGroup,
|
|
216
|
-
if
|
|
217
|
-
|
|
218
|
-
|
|
202
|
+
def _add(self, func, group: CallbackGroup, **kwargs):
|
|
203
|
+
if isinstance(func, CallbackSpec):
|
|
204
|
+
spec = func
|
|
205
|
+
else:
|
|
206
|
+
spec = self.factory(func, group, **kwargs)
|
|
219
207
|
|
|
220
208
|
if spec in self.items:
|
|
221
209
|
return
|
|
@@ -245,7 +233,7 @@ class CallbackWrapper:
|
|
|
245
233
|
unique_key: str,
|
|
246
234
|
) -> None:
|
|
247
235
|
self._callback = callback
|
|
248
|
-
self._iscoro =
|
|
236
|
+
self._iscoro = getattr(callback, "is_coroutine", False)
|
|
249
237
|
self.condition = condition
|
|
250
238
|
self.meta = meta
|
|
251
239
|
self.unique_key = unique_key
|
|
@@ -292,19 +280,21 @@ class CallbacksExecutor:
|
|
|
292
280
|
def __str__(self):
|
|
293
281
|
return ", ".join(str(c) for c in self)
|
|
294
282
|
|
|
295
|
-
def
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
continue
|
|
283
|
+
def add(self, key: str, spec: CallbackSpec, builder: Callable[[], Callable]):
|
|
284
|
+
if key in self.items_already_seen:
|
|
285
|
+
return
|
|
299
286
|
|
|
300
|
-
|
|
301
|
-
insort(self.items, callback)
|
|
287
|
+
self.items_already_seen.add(key)
|
|
302
288
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
289
|
+
condition = spec.cond if spec.cond is not None else allways_true
|
|
290
|
+
wrapper = CallbackWrapper(
|
|
291
|
+
callback=builder(),
|
|
292
|
+
condition=condition,
|
|
293
|
+
meta=spec,
|
|
294
|
+
unique_key=key,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
insort(self.items, wrapper)
|
|
308
298
|
|
|
309
299
|
async def async_call(self, *args, **kwargs):
|
|
310
300
|
return await asyncio.gather(
|
|
@@ -341,9 +331,6 @@ class CallbacksRegistry:
|
|
|
341
331
|
self._registry: Dict[str, CallbacksExecutor] = defaultdict(CallbacksExecutor)
|
|
342
332
|
self.has_async_callbacks: bool = False
|
|
343
333
|
|
|
344
|
-
def clear(self):
|
|
345
|
-
self._registry.clear()
|
|
346
|
-
|
|
347
334
|
def __getitem__(self, key: str) -> CallbacksExecutor:
|
|
348
335
|
return self._registry[key]
|
|
349
336
|
|
|
@@ -356,6 +343,13 @@ class CallbacksRegistry:
|
|
|
356
343
|
callback for callback in self[meta.group.build_key(specs)] if callback.meta == meta
|
|
357
344
|
):
|
|
358
345
|
continue
|
|
346
|
+
|
|
347
|
+
if meta.names_not_found:
|
|
348
|
+
raise AttrNotFound(
|
|
349
|
+
_("Did not found name '{}' from model or statemachine").format(
|
|
350
|
+
", ".join(meta.names_not_found)
|
|
351
|
+
),
|
|
352
|
+
)
|
|
359
353
|
raise AttrNotFound(
|
|
360
354
|
_("Did not found name '{}' from model or statemachine").format(meta.func)
|
|
361
355
|
)
|
|
@@ -364,3 +358,24 @@ class CallbacksRegistry:
|
|
|
364
358
|
self.has_async_callbacks = any(
|
|
365
359
|
callback._iscoro for executor in self._registry.values() for callback in executor
|
|
366
360
|
)
|
|
361
|
+
|
|
362
|
+
def call(self, key: str, *args, **kwargs):
|
|
363
|
+
if key not in self._registry:
|
|
364
|
+
return []
|
|
365
|
+
return self._registry[key].call(*args, **kwargs)
|
|
366
|
+
|
|
367
|
+
def async_call(self, key: str, *args, **kwargs):
|
|
368
|
+
return self._registry[key].async_call(*args, **kwargs)
|
|
369
|
+
|
|
370
|
+
def all(self, key: str, *args, **kwargs):
|
|
371
|
+
if key not in self._registry:
|
|
372
|
+
return True
|
|
373
|
+
return self._registry[key].all(*args, **kwargs)
|
|
374
|
+
|
|
375
|
+
def async_all(self, key: str, *args, **kwargs):
|
|
376
|
+
return self._registry[key].async_all(*args, **kwargs)
|
|
377
|
+
|
|
378
|
+
def str(self, key: str) -> str:
|
|
379
|
+
if key not in self._registry:
|
|
380
|
+
return ""
|
|
381
|
+
return str(self._registry[key])
|
statemachine/contrib/diagram.py
CHANGED
|
@@ -29,7 +29,7 @@ class DotGraphMachine:
|
|
|
29
29
|
transition_font_size = "9"
|
|
30
30
|
"""Transition font size in points"""
|
|
31
31
|
|
|
32
|
-
def __init__(self, machine):
|
|
32
|
+
def __init__(self, machine: StateMachine):
|
|
33
33
|
self.machine = machine
|
|
34
34
|
|
|
35
35
|
def _get_graph(self):
|
|
@@ -69,11 +69,11 @@ class DotGraphMachine:
|
|
|
69
69
|
def _actions_getter(self):
|
|
70
70
|
if isinstance(self.machine, StateMachine):
|
|
71
71
|
|
|
72
|
-
def getter(grouper):
|
|
73
|
-
return self.machine.
|
|
72
|
+
def getter(grouper) -> str:
|
|
73
|
+
return self.machine._callbacks.str(grouper.key)
|
|
74
74
|
else:
|
|
75
75
|
|
|
76
|
-
def getter(grouper):
|
|
76
|
+
def getter(grouper) -> str:
|
|
77
77
|
all_names = set(dir(self.machine))
|
|
78
78
|
return ", ".join(
|
|
79
79
|
str(c) for c in grouper if not c.is_convention or c.func in all_names
|
|
@@ -166,10 +166,6 @@ def quickchart_write_svg(sm: StateMachine, path: str):
|
|
|
166
166
|
>>> sm = OrderControl()
|
|
167
167
|
>>> print(sm._graph().to_string())
|
|
168
168
|
digraph list {
|
|
169
|
-
fontname=Arial;
|
|
170
|
-
fontsize=10;
|
|
171
|
-
label=OrderControl;
|
|
172
|
-
rankdir=LR;
|
|
173
169
|
...
|
|
174
170
|
|
|
175
171
|
To give you an example, we included this method that will serialize the dot, request the graph
|