python-statemachine 3.1.2__py3-none-any.whl → 3.2.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.
Files changed (59) hide show
  1. {python_statemachine-3.1.2.dist-info → python_statemachine-3.2.0.dist-info}/METADATA +16 -3
  2. python_statemachine-3.2.0.dist-info/RECORD +72 -0
  3. {python_statemachine-3.1.2.dist-info → python_statemachine-3.2.0.dist-info}/WHEEL +1 -1
  4. statemachine/__init__.py +1 -1
  5. statemachine/callbacks.py +5 -11
  6. statemachine/configuration.py +5 -6
  7. statemachine/contrib/diagram/extract.py +23 -24
  8. statemachine/contrib/diagram/formatter.py +5 -7
  9. statemachine/contrib/diagram/model.py +9 -11
  10. statemachine/contrib/diagram/renderers/dot.py +20 -26
  11. statemachine/contrib/diagram/renderers/mermaid.py +36 -40
  12. statemachine/contrib/diagram/renderers/table.py +7 -9
  13. statemachine/contrib/weighted.py +7 -11
  14. statemachine/dispatcher.py +13 -12
  15. statemachine/engines/async_.py +5 -6
  16. statemachine/engines/base.py +12 -14
  17. statemachine/event.py +1 -2
  18. statemachine/exceptions.py +1 -1
  19. statemachine/factory.py +11 -15
  20. statemachine/graph.py +2 -2
  21. statemachine/invoke.py +12 -11
  22. statemachine/io/__init__.py +45 -225
  23. statemachine/io/{scxml/actions.py → actions.py} +158 -288
  24. statemachine/io/builder.py +195 -0
  25. statemachine/io/class_factory.py +236 -0
  26. statemachine/io/evaluators.py +275 -0
  27. statemachine/io/interpreter.py +128 -0
  28. statemachine/io/{scxml/invoke.py → invoke.py} +77 -49
  29. statemachine/io/json/__init__.py +1 -0
  30. statemachine/io/json/reader.py +27 -0
  31. statemachine/io/loader.py +161 -0
  32. statemachine/io/model.py +268 -0
  33. statemachine/io/native.py +402 -0
  34. statemachine/io/ports.py +83 -0
  35. statemachine/io/schemas/statechart.schema.json +258 -0
  36. statemachine/io/scxml/__init__.py +12 -0
  37. statemachine/io/scxml/processor.py +23 -253
  38. statemachine/io/scxml/{parser.py → reader.py} +64 -47
  39. statemachine/io/system_variables.py +184 -0
  40. statemachine/io/validation.py +44 -0
  41. statemachine/io/yaml/__init__.py +1 -0
  42. statemachine/io/yaml/reader.py +65 -0
  43. statemachine/locale/en/LC_MESSAGES/statemachine.po +19 -19
  44. statemachine/locale/hi_IN/LC_MESSAGES/statemachine.po +19 -19
  45. statemachine/locale/pt_BR/LC_MESSAGES/statemachine.po +19 -19
  46. statemachine/locale/zh_CN/LC_MESSAGES/statemachine.po +19 -19
  47. statemachine/orderedset.py +3 -3
  48. statemachine/registry.py +1 -4
  49. statemachine/signature.py +2 -5
  50. statemachine/spec_parser.py +171 -42
  51. statemachine/state.py +5 -6
  52. statemachine/statemachine.py +18 -20
  53. statemachine/states.py +3 -5
  54. statemachine/transition.py +3 -4
  55. statemachine/transition_list.py +4 -5
  56. statemachine/transition_mixin.py +1 -1
  57. python_statemachine-3.1.2.dist-info/RECORD +0 -58
  58. statemachine/io/scxml/schema.py +0 -175
  59. {python_statemachine-3.1.2.dist-info → python_statemachine-3.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,31 +1,32 @@
1
1
  import re
2
2
  import xml.etree.ElementTree as ET
3
- from typing import List
3
+ from collections.abc import Callable
4
4
  from typing import Literal
5
- from typing import Set
6
5
  from typing import cast
7
6
  from urllib.parse import urlparse
8
7
 
9
- from .schema import Action
10
- from .schema import AssignAction
11
- from .schema import CancelAction
12
- from .schema import DataItem
13
- from .schema import DataModel
14
- from .schema import DoneData
15
- from .schema import ExecutableContent
16
- from .schema import ForeachAction
17
- from .schema import HistoryState
18
- from .schema import IfAction
19
- from .schema import IfBranch
20
- from .schema import InvokeDefinition
21
- from .schema import LogAction
22
- from .schema import Param
23
- from .schema import RaiseAction
24
- from .schema import ScriptAction
25
- from .schema import SendAction
26
- from .schema import State
27
- from .schema import StateMachineDefinition
28
- from .schema import Transition
8
+ from ..model import Action
9
+ from ..model import AssignAction
10
+ from ..model import CancelAction
11
+ from ..model import DataItem
12
+ from ..model import DataModel
13
+ from ..model import DoneData
14
+ from ..model import ExecutableContent
15
+ from ..model import ForeachAction
16
+ from ..model import HistoryState
17
+ from ..model import IfAction
18
+ from ..model import IfBranch
19
+ from ..model import InvokeDefinition
20
+ from ..model import LogAction
21
+ from ..model import Param
22
+ from ..model import RaiseAction
23
+ from ..model import ScriptAction
24
+ from ..model import SendAction
25
+ from ..model import State
26
+ from ..model import StateMachineDefinition
27
+ from ..model import Transition
28
+ from ..ports import FormatSpec
29
+ from ..ports import register_format
29
30
 
30
31
 
31
32
  def strip_namespaces(tree: ET.Element):
@@ -40,7 +41,7 @@ def strip_namespaces(tree: ET.Element):
40
41
  attrib[new_name] = attrib.pop(name)
41
42
 
42
43
 
43
- def _parse_initial(initial_content: "str | None") -> List[str]:
44
+ def _parse_initial(initial_content: "str | None") -> list[str]:
44
45
  if initial_content is None:
45
46
  return []
46
47
  return initial_content.split()
@@ -87,13 +88,13 @@ def parse_scxml(scxml_content: str) -> StateMachineDefinition: # noqa: C901
87
88
  return definition
88
89
 
89
90
 
90
- def _find_own_datamodel_elements(root: ET.Element) -> List[ET.Element]:
91
+ def _find_own_datamodel_elements(root: ET.Element) -> list[ET.Element]:
91
92
  """Find <datamodel> elements that belong to this SCXML document, not to inline children.
92
93
 
93
94
  Skips any <datamodel> nested inside <content> elements (which contain inline
94
95
  child SCXML documents for <invoke>).
95
96
  """
96
- result: List[ET.Element] = []
97
+ result: list[ET.Element] = []
97
98
 
98
99
  def _walk(elem: ET.Element):
99
100
  for child in elem:
@@ -122,7 +123,7 @@ def parse_datamodel(root: ET.Element) -> "DataModel | None":
122
123
  data_model.data.append(
123
124
  DataItem(
124
125
  id=data_elem.attrib["id"],
125
- src=src_parsed,
126
+ src=src,
126
127
  expr=data_elem.attrib.get("expr"),
127
128
  content=content,
128
129
  )
@@ -157,7 +158,7 @@ def parse_history(state_elem: ET.Element) -> HistoryState:
157
158
 
158
159
  def parse_state( # noqa: C901
159
160
  state_elem: ET.Element,
160
- initial_states: Set[str],
161
+ initial_states: set[str],
161
162
  is_final: bool = False,
162
163
  is_parallel: bool = False,
163
164
  ) -> State:
@@ -279,25 +280,11 @@ def parse_executable_content(element: ET.Element) -> ExecutableContent:
279
280
 
280
281
 
281
282
  def parse_element(element: ET.Element) -> Action:
282
- tag = element.tag
283
- if tag == "raise":
284
- return parse_raise(element)
285
- elif tag == "assign":
286
- return parse_assign(element)
287
- elif tag == "log":
288
- return parse_log(element)
289
- elif tag == "if":
290
- return parse_if(element)
291
- elif tag == "send":
292
- return parse_send(element)
293
- elif tag == "script":
294
- return parse_script(element)
295
- elif tag == "foreach":
296
- return parse_foreach(element)
297
- elif tag == "cancel":
298
- return parse_cancel(element)
299
-
300
- raise ValueError(f"Unknown tag: {tag}")
283
+ try:
284
+ parser = _ELEMENT_PARSERS[element.tag]
285
+ except KeyError:
286
+ raise ValueError(f"Unknown tag: {element.tag}") from None
287
+ return parser(element)
301
288
 
302
289
 
303
290
  def parse_raise(element: ET.Element) -> RaiseAction:
@@ -434,7 +421,7 @@ def parse_invoke(element: ET.Element) -> InvokeDefinition:
434
421
  autoforward = element.attrib.get("autoforward", "false").lower() == "true"
435
422
  namelist = element.attrib.get("namelist")
436
423
 
437
- params: List[Param] = []
424
+ params: list[Param] = []
438
425
  content: "str | None" = None
439
426
  finalize: "ExecutableContent | None" = None
440
427
 
@@ -471,3 +458,33 @@ def parse_invoke(element: ET.Element) -> InvokeDefinition:
471
458
  content=content,
472
459
  finalize=finalize,
473
460
  )
461
+
462
+
463
+ #: Dispatch table for executable-content elements (all parsers share the same
464
+ #: ``(element) -> Action`` signature, so a lookup is cleaner than a branch chain).
465
+ _ELEMENT_PARSERS: "dict[str, Callable[[ET.Element], Action]]" = {
466
+ "raise": parse_raise,
467
+ "assign": parse_assign,
468
+ "log": parse_log,
469
+ "if": parse_if,
470
+ "send": parse_send,
471
+ "script": parse_script,
472
+ "foreach": parse_foreach,
473
+ "cancel": parse_cancel,
474
+ }
475
+
476
+
477
+ class SCXMLReader:
478
+ """Format adapter that parses SCXML (XML) documents into the neutral IR."""
479
+
480
+ def read(self, text: str, *, source_name: "str | None" = None) -> StateMachineDefinition:
481
+ return parse_scxml(text)
482
+
483
+
484
+ register_format(
485
+ FormatSpec(
486
+ name="scxml",
487
+ extensions=(".scxml", ".xml"),
488
+ reader_factory=SCXMLReader,
489
+ )
490
+ )
@@ -0,0 +1,184 @@
1
+ """Runtime system variables of the statechart execution model.
2
+
3
+ These are part of the (SCXML-derived) execution model the library implements, not of
4
+ any particular syntax — so they are format-neutral and available to statecharts loaded
5
+ from SCXML, JSON or YAML alike. The :class:`~statemachine.io.interpreter.Interpreter`
6
+ injects them on every event via :func:`build_system_variables`:
7
+
8
+ - ``_event``: the current event, wrapped as :class:`EventDataWrapper`
9
+ (``name``/``data``/``type``/``origintype``/``sendid``/``invokeid``).
10
+ - ``_sessionid``: a stable id for the running machine session.
11
+ - ``_name``: the machine name.
12
+ - ``_ioprocessors``: the session's :class:`IOProcessor`.
13
+ """
14
+
15
+ from dataclasses import dataclass
16
+ from typing import Any
17
+
18
+
19
+ @dataclass
20
+ class _Data:
21
+ kwargs: dict
22
+
23
+ def __getattr__(self, name):
24
+ return self.kwargs.get(name, None)
25
+
26
+ def get(self, name, default=None):
27
+ return self.kwargs.get(name, default)
28
+
29
+
30
+ class OriginTypeSCXML(str):
31
+ """The origintype of an :ref:`Event` as specified by the SCXML namespace."""
32
+
33
+ def __eq__(self, other):
34
+ return other == "http://www.w3.org/TR/scxml/#SCXMLEventProcessor" or other == "scxml"
35
+
36
+
37
+ class EventDataWrapper:
38
+ """The ``_event`` system variable: a read-only view of the current event.
39
+
40
+ Exposes ``name``/``data``/``type``/``origintype``/``sendid``/``invokeid`` following
41
+ the SCXML event model, which is the library's execution model regardless of the
42
+ source syntax.
43
+ """
44
+
45
+ origin: str = ""
46
+ origintype: str = OriginTypeSCXML("scxml")
47
+ invokeid: str = ""
48
+ """If this event is generated from an invoked child process, the Processor MUST set
49
+ this field to the invoke id of the invocation that triggered the child process.
50
+ Otherwise it MUST leave it blank.
51
+ """
52
+
53
+ def __init__(self, event_data=None, *, trigger_data=None):
54
+ self.event_data = event_data
55
+ if trigger_data is not None:
56
+ self.trigger_data = trigger_data
57
+ elif event_data is not None:
58
+ self.trigger_data = event_data.trigger_data
59
+ else:
60
+ raise ValueError("Either event_data or trigger_data must be provided")
61
+
62
+ td = self.trigger_data
63
+ self.sendid = td.send_id
64
+ self.invokeid = td.kwargs.get("_invokeid", "")
65
+ if td.event is None or td.event.internal:
66
+ if "error.execution" == td.event:
67
+ self.type = "platform"
68
+ else:
69
+ self.type = "internal"
70
+ self.origintype = ""
71
+ else:
72
+ self.type = "external"
73
+
74
+ @classmethod
75
+ def from_trigger_data(cls, trigger_data):
76
+ """Create an EventDataWrapper directly from a TriggerData (no EventData needed)."""
77
+ return cls(trigger_data=trigger_data)
78
+
79
+ def __getattr__(self, name):
80
+ if self.event_data is not None:
81
+ return getattr(self.event_data, name)
82
+ raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
83
+
84
+ def __eq__(self, value):
85
+ "This makes SCXML test 329 pass. It assumes that the event is the same instance"
86
+ return isinstance(value, EventDataWrapper)
87
+
88
+ @property
89
+ def name(self):
90
+ if self.event_data is not None:
91
+ return self.event_data.event
92
+ return str(self.trigger_data.event) if self.trigger_data.event else None
93
+
94
+ @property
95
+ def data(self):
96
+ "Property used to access the event payload (the SCXML ``_event.data``)."
97
+ td = self.trigger_data
98
+ if td.kwargs:
99
+ return _Data(td.kwargs)
100
+ elif td.args and len(td.args) == 1:
101
+ return td.args[0]
102
+ elif td.args:
103
+ return td.args
104
+ else:
105
+ return None
106
+
107
+
108
+ class IOProcessor:
109
+ """The ``_ioprocessors`` system variable for a session.
110
+
111
+ A minimal Event I/O Processor handle: indexing by any processor name returns itself,
112
+ and ``location`` is the machine name.
113
+ """
114
+
115
+ def __init__(self, interpreter, machine):
116
+ self.interpreter = interpreter
117
+ self.machine = machine
118
+
119
+ def __getitem__(self, name: str):
120
+ return self
121
+
122
+ @property
123
+ def location(self):
124
+ return self.machine.name
125
+
126
+ def get(self, name: str):
127
+ return getattr(self, name)
128
+
129
+
130
+ @dataclass
131
+ class SessionData:
132
+ """Per-machine runtime session state held by the interpreter."""
133
+
134
+ machine: Any
135
+ processor: IOProcessor
136
+ first_event_raised: bool = False
137
+
138
+ def __post_init__(self):
139
+ self.session_id = f"{self.machine.name}:{id(self.machine)}"
140
+
141
+
142
+ def build_system_variables(machine, session_data: SessionData, event, event_data) -> dict:
143
+ """Compute the system variables to inject into callbacks for the current event.
144
+
145
+ ``_event`` is exposed only after the first real (non-``__initial__``) event, matching
146
+ the SCXML rule that ``_event`` is unbound during the initial macrostep.
147
+ """
148
+ if not session_data.first_event_raised and event and event != "__initial__":
149
+ session_data.first_event_raised = True
150
+
151
+ _event: "EventDataWrapper | None" = None
152
+ if session_data.first_event_raised:
153
+ _event = EventDataWrapper(event_data)
154
+
155
+ return {
156
+ "_name": machine.name,
157
+ "_sessionid": session_data.session_id,
158
+ "_ioprocessors": session_data.processor,
159
+ "_event": _event,
160
+ }
161
+
162
+
163
+ def create_invoke_init_callable():
164
+ """Create a callback that extracts invoke-specific kwargs and stores them on the machine.
165
+
166
+ Inserted at position 0 in the initial state's onentry list for invoked children, so
167
+ ``_invoke_session`` and ``_invoke_params`` are handled before any other callbacks run,
168
+ even for machines without a datamodel.
169
+ """
170
+ initialized = False
171
+
172
+ def invoke_init(*args, **kwargs):
173
+ nonlocal initialized
174
+ if initialized:
175
+ return
176
+ initialized = True
177
+ machine = kwargs.get("machine")
178
+ if machine is not None:
179
+ # Use get() not pop(): each callback receives a copy of kwargs
180
+ # (via EventData.extended_kwargs), so pop would be misleading.
181
+ machine._invoke_params = kwargs.get("_invoke_params")
182
+ machine._invoke_session = kwargs.get("_invoke_session")
183
+
184
+ return invoke_init
@@ -0,0 +1,44 @@
1
+ """Optional JSON Schema validation for the native (JSON/YAML) statechart format.
2
+
3
+ Requires the optional ``[validation]`` extra (``jsonschema``). The schema itself is
4
+ shipped with the package at ``statemachine/io/schemas/statechart.schema.json`` and is
5
+ also published so editors/tools can reference it via ``$schema``.
6
+ """
7
+
8
+ import json
9
+ from functools import lru_cache
10
+ from importlib.resources import files
11
+
12
+ from ..exceptions import InvalidDefinition
13
+
14
+ SCHEMA_RESOURCE = "statechart.schema.json"
15
+
16
+
17
+ @lru_cache(maxsize=1)
18
+ def load_schema() -> dict:
19
+ """Load and cache the bundled statechart JSON Schema."""
20
+ resource = files("statemachine.io").joinpath("schemas").joinpath(SCHEMA_RESOURCE)
21
+ return json.loads(resource.read_text(encoding="utf-8")) # type: ignore[no-any-return]
22
+
23
+
24
+ def validate_document(doc) -> None:
25
+ """Validate a parsed native document against the statechart JSON Schema.
26
+
27
+ Raises:
28
+ InvalidDefinition: if the document does not conform to the schema, or if the
29
+ optional ``jsonschema`` dependency is not installed.
30
+ """
31
+ try:
32
+ import jsonschema # type: ignore[import-untyped]
33
+ except ModuleNotFoundError as exc: # pragma: no cover - exercised via import mock
34
+ raise InvalidDefinition(
35
+ "validate=True requires the 'jsonschema' package. Install it with: "
36
+ 'pip install "python-statemachine[validation]"'
37
+ ) from exc
38
+
39
+ try:
40
+ jsonschema.validate(instance=doc, schema=load_schema())
41
+ except jsonschema.ValidationError as exc:
42
+ raise InvalidDefinition(
43
+ f"Statechart document failed schema validation: {exc.message}"
44
+ ) from exc
@@ -0,0 +1 @@
1
+ """YAML statechart format adapter (requires the optional ``[yaml]`` extra)."""
@@ -0,0 +1,65 @@
1
+ """YAML format adapter: parse a YAML statechart document into the neutral IR.
2
+
3
+ Requires the optional ``[yaml]`` extra (PyYAML). YAML is always parsed with
4
+ ``yaml.safe_load`` semantics so a document can never instantiate arbitrary Python
5
+ objects.
6
+ """
7
+
8
+ from ..model import StateMachineDefinition
9
+ from ..native import native_dict_to_definition
10
+ from ..ports import FormatSpec
11
+ from ..ports import register_format
12
+
13
+ _LOADER = None
14
+
15
+
16
+ def _make_loader(yaml):
17
+ """Build a SafeLoader that does NOT coerce ``on``/``off``/``yes``/``no`` to bool.
18
+
19
+ YAML 1.1 turns those tokens into booleans, which silently mangles state ids
20
+ like ``off``/``on`` into ``True``/``False``. We keep ``yaml.safe_load``'s safety
21
+ but restrict the implicit bool resolver to ``true``/``false`` only.
22
+ """
23
+ global _LOADER
24
+ if _LOADER is not None:
25
+ return _LOADER
26
+
27
+ class _StatechartSafeLoader(yaml.SafeLoader):
28
+ pass
29
+
30
+ _StatechartSafeLoader.yaml_implicit_resolvers = {
31
+ first_char: [
32
+ (tag, regexp)
33
+ for (tag, regexp) in resolvers
34
+ if not (tag == "tag:yaml.org,2002:bool" and first_char not in "tTfF")
35
+ ]
36
+ for first_char, resolvers in yaml.SafeLoader.yaml_implicit_resolvers.items()
37
+ }
38
+ _LOADER = _StatechartSafeLoader
39
+ return _LOADER
40
+
41
+
42
+ class YAMLReader:
43
+ """Format adapter that parses YAML documents (``yaml.safe_load``) into the IR."""
44
+
45
+ def parse_document(self, text: str) -> dict:
46
+ try:
47
+ import yaml # type: ignore[import-untyped]
48
+ except ModuleNotFoundError as exc: # pragma: no cover - exercised via import mock
49
+ raise ImportError(
50
+ "YAML support requires PyYAML. Install it with: "
51
+ 'pip install "python-statemachine[yaml]"'
52
+ ) from exc
53
+ return yaml.load(text, Loader=_make_loader(yaml)) # type: ignore[no-any-return]
54
+
55
+ def read(self, text: str, *, source_name: "str | None" = None) -> StateMachineDefinition:
56
+ return native_dict_to_definition(self.parse_document(text), source_name=source_name)
57
+
58
+
59
+ register_format(
60
+ FormatSpec(
61
+ name="yaml",
62
+ extensions=(".yaml", ".yml"),
63
+ reader_factory=YAMLReader,
64
+ )
65
+ )
@@ -5,7 +5,7 @@ msgid ""
5
5
  msgstr ""
6
6
  "Project-Id-Version: 3.1.1\n"
7
7
  "Report-Msgid-Bugs-To: fgmacedo@gmail.com\n"
8
- "POT-Creation-Date: 2026-05-18 16:16-0300\n"
8
+ "POT-Creation-Date: 2026-06-16 23:00-0300\n"
9
9
  "PO-Revision-Date: 2026-02-24 14:31-0300\n"
10
10
  "Last-Translator: Fernando Macedo <fgmacedo@gmail.com>\n"
11
11
  "Language: en\n"
@@ -16,12 +16,12 @@ msgstr ""
16
16
  "Content-Transfer-Encoding: 8bit\n"
17
17
  "Generated-By: Babel 2.18.0\n"
18
18
 
19
- #: statemachine/callbacks.py:404 statemachine/callbacks.py:409
19
+ #: statemachine/callbacks.py:398 statemachine/callbacks.py:403
20
20
  #, python-brace-format
21
21
  msgid "Did not found name '{}' from model or statemachine"
22
22
  msgstr "Did not found name '{}' from model or statemachine"
23
23
 
24
- #: statemachine/configuration.py:129
24
+ #: statemachine/configuration.py:128
25
25
  msgid ""
26
26
  "There's no current state set. In async code, did you activate the initial"
27
27
  " state? (e.g., `await sm.activate_initial_state()`)"
@@ -29,12 +29,12 @@ msgstr ""
29
29
  "There's no current state set. In async code, did you activate the initial"
30
30
  " state? (e.g., `await sm.activate_initial_state()`)"
31
31
 
32
- #: statemachine/dispatcher.py:126
32
+ #: statemachine/dispatcher.py:123
33
33
  #, python-brace-format
34
34
  msgid "Failed to parse boolean expression '{}'"
35
35
  msgstr "Failed to parse boolean expression '{}'"
36
36
 
37
- #: statemachine/event.py:95
37
+ #: statemachine/event.py:94
38
38
  #, python-brace-format
39
39
  msgid ""
40
40
  "Event() received a non-string 'id' ({cls_name}). To combine multiple "
@@ -43,12 +43,12 @@ msgstr ""
43
43
  "Event() received a non-string 'id' ({cls_name}). To combine multiple "
44
44
  "transitions under one event, use the | operator: t1 | t2."
45
45
 
46
- #: statemachine/event.py:131
46
+ #: statemachine/event.py:130
47
47
  #, python-brace-format
48
48
  msgid "Cannot add callback '{}' to an event with no transitions."
49
49
  msgstr "Cannot add callback '{}' to an event with no transitions."
50
50
 
51
- #: statemachine/event.py:164
51
+ #: statemachine/event.py:163
52
52
  #, python-brace-format
53
53
  msgid "Event {} cannot be called without a SM instance"
54
54
  msgstr "Event {} cannot be called without a SM instance"
@@ -63,7 +63,7 @@ msgstr "{!r} is not a valid state value."
63
63
  msgid "Can't {} when in {}."
64
64
  msgstr "Can't {} when in {}."
65
65
 
66
- #: statemachine/factory.py:81
66
+ #: statemachine/factory.py:77
67
67
  #, python-brace-format
68
68
  msgid ""
69
69
  "There should be one and only one initial state. Your currently have "
@@ -72,7 +72,7 @@ msgstr ""
72
72
  "There should be one and only one initial state. Your currently have "
73
73
  "these: {0}"
74
74
 
75
- #: statemachine/factory.py:198
75
+ #: statemachine/factory.py:194
76
76
  #, python-brace-format
77
77
  msgid ""
78
78
  "There should be one and only one initial state. You currently have these:"
@@ -81,12 +81,12 @@ msgstr ""
81
81
  "There should be one and only one initial state. You currently have these:"
82
82
  " {!r}"
83
83
 
84
- #: statemachine/factory.py:213
84
+ #: statemachine/factory.py:209
85
85
  #, python-brace-format
86
86
  msgid "Cannot declare transitions from final state. Invalid state(s): {}"
87
87
  msgstr "Cannot declare transitions from final state. Invalid state(s): {}"
88
88
 
89
- #: statemachine/factory.py:225
89
+ #: statemachine/factory.py:221
90
90
  #, python-brace-format
91
91
  msgid ""
92
92
  "All non-final states should have at least one outgoing transition. These "
@@ -95,7 +95,7 @@ msgstr ""
95
95
  "All non-final states should have at least one outgoing transition. These "
96
96
  "states have no outgoing transition: {!r}"
97
97
 
98
- #: statemachine/factory.py:239
98
+ #: statemachine/factory.py:235
99
99
  #, python-brace-format
100
100
  msgid ""
101
101
  "All non-final states should have at least one path to a final state. "
@@ -104,7 +104,7 @@ msgstr ""
104
104
  "All non-final states should have at least one path to a final state. "
105
105
  "These states have no path to a final state: {!r}"
106
106
 
107
- #: statemachine/factory.py:252
107
+ #: statemachine/factory.py:248
108
108
  #, python-brace-format
109
109
  msgid ""
110
110
  "There are unreachable states. The statemachine graph should have a single"
@@ -113,7 +113,7 @@ msgstr ""
113
113
  "There are unreachable states. The statemachine graph should have a single"
114
114
  " component. Disconnected states: {}"
115
115
 
116
- #: statemachine/factory.py:289
116
+ #: statemachine/factory.py:285
117
117
  #, python-brace-format
118
118
  msgid ""
119
119
  "Invalid entry in 'listeners': {!r}. Expected a class, callable, or "
@@ -122,7 +122,7 @@ msgstr ""
122
122
  "Invalid entry in 'listeners': {!r}. Expected a class, callable, or "
123
123
  "listener instance."
124
124
 
125
- #: statemachine/factory.py:388
125
+ #: statemachine/factory.py:384
126
126
  #, python-brace-format
127
127
  msgid "An event in the '{}' has no id."
128
128
  msgstr "An event in the '{}' has no id."
@@ -132,20 +132,20 @@ msgstr "An event in the '{}' has no id."
132
132
  msgid "{!r} is not a valid state machine name."
133
133
  msgstr "{!r} is not a valid state machine name."
134
134
 
135
- #: statemachine/state.py:250
135
+ #: statemachine/state.py:249
136
136
  msgid "'donedata' can only be specified on final states."
137
137
  msgstr "'donedata' can only be specified on final states."
138
138
 
139
- #: statemachine/statemachine.py:169
139
+ #: statemachine/statemachine.py:167
140
140
  msgid "There are no states or transitions."
141
141
  msgstr "There are no states or transitions."
142
142
 
143
- #: statemachine/statemachine.py:239
143
+ #: statemachine/statemachine.py:237
144
144
  #, python-brace-format
145
145
  msgid "State overriding is not allowed. Trying to add '{}' to {}"
146
146
  msgstr "State overriding is not allowed. Trying to add '{}' to {}"
147
147
 
148
- #: statemachine/transition.py:75
148
+ #: statemachine/transition.py:74
149
149
  #, python-brace-format
150
150
  msgid ""
151
151
  "Not a valid internal transition from source {source!r}, target {target!r}"