python-statemachine 3.2.0__py3-none-any.whl → 3.2.2__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 (40) hide show
  1. {python_statemachine-3.2.0.dist-info → python_statemachine-3.2.2.dist-info}/METADATA +7 -6
  2. python_statemachine-3.2.2.dist-info/RECORD +73 -0
  3. {python_statemachine-3.2.0.dist-info → python_statemachine-3.2.2.dist-info}/WHEEL +1 -1
  4. statemachine/__init__.py +1 -1
  5. statemachine/callbacks.py +11 -0
  6. statemachine/contrib/diagram/__init__.py +15 -6
  7. statemachine/contrib/diagram/extract.py +3 -3
  8. statemachine/contrib/diagram/formatter.py +4 -2
  9. statemachine/contrib/diagram/graphviz.py +66 -0
  10. statemachine/contrib/diagram/sphinx_ext.py +28 -7
  11. statemachine/dispatcher.py +8 -3
  12. statemachine/engines/async_.py +224 -180
  13. statemachine/engines/base.py +257 -245
  14. statemachine/engines/sync.py +224 -108
  15. statemachine/event.py +29 -10
  16. statemachine/event_data.py +19 -3
  17. statemachine/events.py +5 -2
  18. statemachine/exceptions.py +1 -1
  19. statemachine/factory.py +126 -29
  20. statemachine/io/__init__.py +18 -11
  21. statemachine/io/actions.py +124 -15
  22. statemachine/io/evaluators.py +37 -9
  23. statemachine/io/invoke.py +12 -0
  24. statemachine/io/loader.py +17 -3
  25. statemachine/io/schemas/statechart.schema.json +8 -1
  26. statemachine/io/scxml/__init__.py +6 -3
  27. statemachine/io/scxml/processor.py +1 -1
  28. statemachine/io/scxml/reader.py +24 -9
  29. statemachine/io/system_variables.py +34 -19
  30. statemachine/locale/en/LC_MESSAGES/statemachine.po +58 -17
  31. statemachine/locale/hi_IN/LC_MESSAGES/statemachine.po +66 -17
  32. statemachine/locale/pt_BR/LC_MESSAGES/statemachine.po +66 -17
  33. statemachine/locale/zh_CN/LC_MESSAGES/statemachine.po +60 -17
  34. statemachine/orderedset.py +12 -3
  35. statemachine/registry.py +6 -1
  36. statemachine/spec_parser.py +151 -21
  37. statemachine/state.py +47 -8
  38. statemachine/statemachine.py +42 -19
  39. python_statemachine-3.2.0.dist-info/RECORD +0 -72
  40. {python_statemachine-3.2.0.dist-info → python_statemachine-3.2.2.dist-info}/licenses/LICENSE +0 -0
statemachine/io/loader.py CHANGED
@@ -3,7 +3,7 @@
3
3
  :func:`load` is the simple entry point: give it a file path (format detected by
4
4
  extension) or inline content (with an explicit ``format=``) and it returns the
5
5
  ready-to-instantiate :class:`~statemachine.statemachine.StateChart` class. It is
6
- **secure by default** — expressions are evaluated by a restricted AST-whitelist
6
+ **secure by default** — expressions are evaluated by a restricted AST-allowlist
7
7
  evaluator and ``<script>`` / arbitrary Python is rejected unless ``trusted=True``.
8
8
 
9
9
  For advanced scenarios (a document declaring several statecharts, or SCXML files
@@ -39,6 +39,20 @@ def _chdir(new_dir: Path):
39
39
  os.chdir(original)
40
40
 
41
41
 
42
+ def _is_existing_file(source: str) -> bool:
43
+ """Whether ``source`` names an existing file, treating "too long"/invalid as not a file.
44
+
45
+ A long single-line inline document (e.g. minified JSON) is not a path, but reaches
46
+ :meth:`Path.is_file`, which on some platforms raises ``OSError`` (``ENAMETOOLONG``) or
47
+ ``ValueError`` (embedded NUL) instead of returning ``False``. Swallow those so inline
48
+ content is never mistaken for a crash; a real, resolvable path still returns ``True``.
49
+ """
50
+ try:
51
+ return Path(source).is_file()
52
+ except (OSError, ValueError):
53
+ return False
54
+
55
+
42
56
  def _resolve_source(source: "str | Path", format: "str | None"):
43
57
  """Return ``(text, format_name, location_hint, base_dir)`` for a source.
44
58
 
@@ -48,7 +62,7 @@ def _resolve_source(source: "str | Path", format: "str | None"):
48
62
  """
49
63
  if isinstance(source, Path):
50
64
  path: "Path | None" = source
51
- elif "\n" not in source and Path(source).is_file():
65
+ elif "\n" not in source and _is_existing_file(source):
52
66
  path = Path(source)
53
67
  else:
54
68
  path = None
@@ -140,7 +154,7 @@ def load(
140
154
  format: explicit format name (``"scxml"``, ``"json"``, ``"yaml"``),
141
155
  overriding extension detection and required for inline content.
142
156
  trusted: when ``False`` (default), expressions are evaluated by a restricted
143
- AST-whitelist evaluator and ``<script>`` / arbitrary Python is rejected.
157
+ AST-allowlist evaluator and ``<script>`` / arbitrary Python is rejected.
144
158
  When ``True``, expressions and scripts are evaluated as arbitrary Python.
145
159
  validate: when ``True`` (native JSON/YAML only), validate the document against
146
160
  the published JSON Schema before building (requires the ``[validation]``
@@ -117,7 +117,14 @@
117
117
  "params": { "type": "array", "items": { "$ref": "#/$defs/param" } },
118
118
  "content": { "type": "string" }
119
119
  },
120
- "additionalProperties": false
120
+ "additionalProperties": false,
121
+ "if": {
122
+ "required": ["target"],
123
+ "properties": { "target": { "enum": ["#_internal", "internal"] } }
124
+ },
125
+ "then": {
126
+ "not": { "anyOf": [{ "required": ["delay"] }, { "required": ["delayexpr"] }] }
127
+ }
121
128
  },
122
129
  "foreach": {
123
130
  "type": "object",
@@ -6,7 +6,10 @@ provides a Python datamodel and loads SCXML through the same
6
6
  :func:`~statemachine.io.load` facade as the native JSON/YAML formats.
7
7
 
8
8
  Like every format, SCXML is loaded **secure by default** (``trusted=False`` rejects
9
- ``<script>`` and evaluates expressions with the restricted AST-whitelist evaluator); pass
10
- ``trusted=True`` only for documents you control. See the security note in
11
- :mod:`statemachine.io` and the GHSA-v4jc-pm6r-3vj8 advisory.
9
+ ``<script>`` and external ``<data src>`` / ``<invoke src>`` file references, evaluates
10
+ expressions with the restricted AST-allowlist evaluator, confines write targets to public model
11
+ attributes and magnitude-caps ``**``/``*``); pass ``trusted=True`` only for documents you
12
+ control. The parser also refuses ``<!DOCTYPE>``/DTD to block XML entity-expansion bombs. See the
13
+ security note in :mod:`statemachine.io` and the GHSA-v4jc-pm6r-3vj8, GHSA-fj3w-533r-fvf6,
14
+ GHSA-v3qq-3xvg-m77g, GHSA-4857-ggqc-p3jc and GHSA-r8gj-366q-cgvj advisories.
12
15
  """
@@ -18,7 +18,7 @@ class SCXMLProcessor(Interpreter):
18
18
 
19
19
  Args:
20
20
  trusted: when ``False`` (default), datamodel expressions are evaluated by
21
- a restricted AST-whitelist evaluator and ``<script>`` is rejected, so
21
+ a restricted AST-allowlist evaluator and ``<script>`` is rejected, so
22
22
  loading a document cannot execute arbitrary code. When ``True``,
23
23
  expressions and ``<script>`` are evaluated as arbitrary Python via
24
24
  ``eval``/``exec`` — only use it for SCXML you trust (see the package
@@ -3,8 +3,8 @@ import xml.etree.ElementTree as ET
3
3
  from collections.abc import Callable
4
4
  from typing import Literal
5
5
  from typing import cast
6
- from urllib.parse import urlparse
7
6
 
7
+ from ...exceptions import InvalidDefinition
8
8
  from ..model import Action
9
9
  from ..model import AssignAction
10
10
  from ..model import CancelAction
@@ -47,8 +47,27 @@ def _parse_initial(initial_content: "str | None") -> list[str]:
47
47
  return initial_content.split()
48
48
 
49
49
 
50
+ class _NoDTDTreeBuilder(ET.TreeBuilder):
51
+ """A tree builder that refuses documents carrying a ``<!DOCTYPE>``/DTD.
52
+
53
+ Internal entity definitions (the basis of billion-laughs / quadratic-blowup
54
+ expansion bombs) require a DTD, so rejecting the DOCTYPE neutralizes them at
55
+ parse time. Expat calls this ``doctype`` hook when it meets the declaration.
56
+ """
57
+
58
+ def doctype(self, name, pubid, system): # noqa: D102 - expat callback
59
+ raise InvalidDefinition(
60
+ "DOCTYPE/DTD is not allowed in SCXML documents (XML entity-expansion protection)."
61
+ )
62
+
63
+
64
+ def _fromstring_no_dtd(scxml_content: str) -> ET.Element:
65
+ """Parse SCXML text rejecting any DOCTYPE (see :class:`_NoDTDTreeBuilder`)."""
66
+ return ET.fromstring(scxml_content, parser=ET.XMLParser(target=_NoDTDTreeBuilder()))
67
+
68
+
50
69
  def parse_scxml(scxml_content: str) -> StateMachineDefinition: # noqa: C901
51
- root = ET.fromstring(scxml_content)
70
+ root = _fromstring_no_dtd(scxml_content)
52
71
  strip_namespaces(root)
53
72
 
54
73
  scxml = root if root.tag == "scxml" else root.find(".//scxml")
@@ -114,16 +133,12 @@ def parse_datamodel(root: ET.Element) -> "DataModel | None":
114
133
  for datamodel_elem in _find_own_datamodel_elements(root):
115
134
  for data_elem in datamodel_elem.findall("data"):
116
135
  content = data_elem.text and re.sub(r"\s+", " ", data_elem.text).strip() or None
117
- src = data_elem.attrib.get("src")
118
- src_parsed = urlparse(src) if src else None
119
- if src_parsed and src_parsed.scheme == "file" and content is None:
120
- with open(src_parsed.path) as f:
121
- content = f.read()
122
-
136
+ # An external ``src`` is kept as a raw string; resolving it (reading the file)
137
+ # is deferred to datamodel compilation, where the trust policy can gate it.
123
138
  data_model.data.append(
124
139
  DataItem(
125
140
  id=data_elem.attrib["id"],
126
- src=src,
141
+ src=data_elem.attrib.get("src"),
127
142
  expr=data_elem.attrib.get("expr"),
128
143
  content=content,
129
144
  )
@@ -18,13 +18,23 @@ from typing import Any
18
18
 
19
19
  @dataclass
20
20
  class _Data:
21
- kwargs: dict
21
+ """The ``_event.data`` payload view.
22
+
23
+ The raw payload dict is stored under a private (``_``-prefixed) name so a restricted
24
+ expression cannot hand back the underlying dict via ``_event.data.kwargs``; only the
25
+ individual payload fields (``_event.data.<field>``) are reachable.
26
+ """
27
+
28
+ _kwargs: dict
22
29
 
23
30
  def __getattr__(self, name):
24
- return self.kwargs.get(name, None)
31
+ if name == "_kwargs":
32
+ # Guard against unbounded recursion before the field is set (e.g. copy/pickle).
33
+ raise AttributeError(name)
34
+ return self._kwargs.get(name, None)
25
35
 
26
36
  def get(self, name, default=None):
27
- return self.kwargs.get(name, default)
37
+ return self._kwargs.get(name, default)
28
38
 
29
39
 
30
40
  class OriginTypeSCXML(str):
@@ -51,15 +61,21 @@ class EventDataWrapper:
51
61
  """
52
62
 
53
63
  def __init__(self, event_data=None, *, trigger_data=None):
54
- self.event_data = event_data
64
+ # The engine carriers are stored under private (``_``-prefixed) names so a
65
+ # restricted expression cannot reach them (``_event.event_data`` /
66
+ # ``_event.trigger_data``) and pivot to the machine/interpreter/State. The
67
+ # restricted evaluator blocks attribute access to ``_``-prefixed names, and this
68
+ # class deliberately exposes NO public attribute or ``__getattr__`` forward that
69
+ # would hand them back (GHSA-v3qq-3xvg-m77g).
70
+ self._event_data = event_data
55
71
  if trigger_data is not None:
56
- self.trigger_data = trigger_data
72
+ self._trigger_data = trigger_data
57
73
  elif event_data is not None:
58
- self.trigger_data = event_data.trigger_data
74
+ self._trigger_data = event_data.trigger_data
59
75
  else:
60
76
  raise ValueError("Either event_data or trigger_data must be provided")
61
77
 
62
- td = self.trigger_data
78
+ td = self._trigger_data
63
79
  self.sendid = td.send_id
64
80
  self.invokeid = td.kwargs.get("_invokeid", "")
65
81
  if td.event is None or td.event.internal:
@@ -76,25 +92,20 @@ class EventDataWrapper:
76
92
  """Create an EventDataWrapper directly from a TriggerData (no EventData needed)."""
77
93
  return cls(trigger_data=trigger_data)
78
94
 
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
95
  def __eq__(self, value):
85
96
  "This makes SCXML test 329 pass. It assumes that the event is the same instance"
86
97
  return isinstance(value, EventDataWrapper)
87
98
 
88
99
  @property
89
100
  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
101
+ if self._event_data is not None:
102
+ return self._event_data.event
103
+ return str(self._trigger_data.event) if self._trigger_data.event else None
93
104
 
94
105
  @property
95
106
  def data(self):
96
107
  "Property used to access the event payload (the SCXML ``_event.data``)."
97
- td = self.trigger_data
108
+ td = self._trigger_data
98
109
  if td.kwargs:
99
110
  return _Data(td.kwargs)
100
111
  elif td.args and len(td.args) == 1:
@@ -113,15 +124,19 @@ class IOProcessor:
113
124
  """
114
125
 
115
126
  def __init__(self, interpreter, machine):
116
- self.interpreter = interpreter
117
- self.machine = machine
127
+ # Stored under private (``_``-prefixed) names so a restricted expression cannot
128
+ # reach the interpreter/machine via ``_ioprocessors.interpreter`` /
129
+ # ``_ioprocessors.machine`` and mutate the process-shared engine
130
+ # (GHSA-v3qq-3xvg-m77g). Only the SCXML-visible ``location`` stays public.
131
+ self._interpreter = interpreter
132
+ self._machine = machine
118
133
 
119
134
  def __getitem__(self, name: str):
120
135
  return self
121
136
 
122
137
  @property
123
138
  def location(self):
124
- return self.machine.name
139
+ return self._machine.name
125
140
 
126
141
  def get(self, name: str):
127
142
  return getattr(self, name)
@@ -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-06-16 23:00-0300\n"
8
+ "POT-Creation-Date: 2026-10-13 21:26-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,7 +16,7 @@ msgstr ""
16
16
  "Content-Transfer-Encoding: 8bit\n"
17
17
  "Generated-By: Babel 2.18.0\n"
18
18
 
19
- #: statemachine/callbacks.py:398 statemachine/callbacks.py:403
19
+ #: statemachine/callbacks.py:409 statemachine/callbacks.py:414
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"
@@ -29,12 +29,20 @@ 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:123
32
+ #: statemachine/dispatcher.py:127
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:94
37
+ #: statemachine/event.py:56
38
+ #, python-brace-format
39
+ msgid ""
40
+ "Event {id!r} cannot be both delayed and internal: the internal queue runs"
41
+ " within the current macrostep, which takes no time. Drop 'internal' to "
42
+ "deliver it later on the external queue, or drop 'delay' to run it now."
43
+ msgstr ""
44
+
45
+ #: statemachine/event.py:108
38
46
  #, python-brace-format
39
47
  msgid ""
40
48
  "Event() received a non-string 'id' ({cls_name}). To combine multiple "
@@ -43,12 +51,12 @@ msgstr ""
43
51
  "Event() received a non-string 'id' ({cls_name}). To combine multiple "
44
52
  "transitions under one event, use the | operator: t1 | t2."
45
53
 
46
- #: statemachine/event.py:130
54
+ #: statemachine/event.py:147
47
55
  #, python-brace-format
48
56
  msgid "Cannot add callback '{}' to an event with no transitions."
49
57
  msgstr "Cannot add callback '{}' to an event with no transitions."
50
58
 
51
- #: statemachine/event.py:163
59
+ #: statemachine/event.py:182
52
60
  #, python-brace-format
53
61
  msgid "Event {} cannot be called without a SM instance"
54
62
  msgstr "Event {} cannot be called without a SM instance"
@@ -63,7 +71,7 @@ msgstr "{!r} is not a valid state value."
63
71
  msgid "Can't {} when in {}."
64
72
  msgstr "Can't {} when in {}."
65
73
 
66
- #: statemachine/factory.py:77
74
+ #: statemachine/factory.py:91
67
75
  #, python-brace-format
68
76
  msgid ""
69
77
  "There should be one and only one initial state. Your currently have "
@@ -72,7 +80,34 @@ msgstr ""
72
80
  "There should be one and only one initial state. Your currently have "
73
81
  "these: {0}"
74
82
 
75
- #: statemachine/factory.py:194
83
+ #: statemachine/factory.py:249
84
+ #, python-brace-format
85
+ msgid "is declared in both the '{}' and the '{}' state bodies"
86
+ msgstr ""
87
+
88
+ #: statemachine/factory.py:253
89
+ #, python-brace-format
90
+ msgid "in the '{}' state body clashes with the event '{}'"
91
+ msgstr ""
92
+
93
+ #: statemachine/factory.py:257
94
+ #, python-brace-format
95
+ msgid "is declared in both the '{}' state body and the '{}' class body"
96
+ msgstr ""
97
+
98
+ #: statemachine/factory.py:261
99
+ #, python-brace-format
100
+ msgid "in the '{}' state body clashes with the '{}' attribute of '{}'"
101
+ msgstr ""
102
+
103
+ #: statemachine/factory.py:267
104
+ #, python-brace-format
105
+ msgid ""
106
+ "Callback '{}' {}. Nested state bodies share the class namespace. Rename "
107
+ "one of them."
108
+ msgstr ""
109
+
110
+ #: statemachine/factory.py:291
76
111
  #, python-brace-format
77
112
  msgid ""
78
113
  "There should be one and only one initial state. You currently have these:"
@@ -81,12 +116,12 @@ msgstr ""
81
116
  "There should be one and only one initial state. You currently have these:"
82
117
  " {!r}"
83
118
 
84
- #: statemachine/factory.py:209
119
+ #: statemachine/factory.py:306
85
120
  #, python-brace-format
86
121
  msgid "Cannot declare transitions from final state. Invalid state(s): {}"
87
122
  msgstr "Cannot declare transitions from final state. Invalid state(s): {}"
88
123
 
89
- #: statemachine/factory.py:221
124
+ #: statemachine/factory.py:318
90
125
  #, python-brace-format
91
126
  msgid ""
92
127
  "All non-final states should have at least one outgoing transition. These "
@@ -95,7 +130,7 @@ msgstr ""
95
130
  "All non-final states should have at least one outgoing transition. These "
96
131
  "states have no outgoing transition: {!r}"
97
132
 
98
- #: statemachine/factory.py:235
133
+ #: statemachine/factory.py:332
99
134
  #, python-brace-format
100
135
  msgid ""
101
136
  "All non-final states should have at least one path to a final state. "
@@ -104,7 +139,7 @@ msgstr ""
104
139
  "All non-final states should have at least one path to a final state. "
105
140
  "These states have no path to a final state: {!r}"
106
141
 
107
- #: statemachine/factory.py:248
142
+ #: statemachine/factory.py:345
108
143
  #, python-brace-format
109
144
  msgid ""
110
145
  "There are unreachable states. The statemachine graph should have a single"
@@ -113,7 +148,7 @@ msgstr ""
113
148
  "There are unreachable states. The statemachine graph should have a single"
114
149
  " component. Disconnected states: {}"
115
150
 
116
- #: statemachine/factory.py:285
151
+ #: statemachine/factory.py:382
117
152
  #, python-brace-format
118
153
  msgid ""
119
154
  "Invalid entry in 'listeners': {!r}. Expected a class, callable, or "
@@ -122,7 +157,7 @@ msgstr ""
122
157
  "Invalid entry in 'listeners': {!r}. Expected a class, callable, or "
123
158
  "listener instance."
124
159
 
125
- #: statemachine/factory.py:384
160
+ #: statemachine/factory.py:481
126
161
  #, python-brace-format
127
162
  msgid "An event in the '{}' has no id."
128
163
  msgstr "An event in the '{}' has no id."
@@ -132,15 +167,15 @@ msgstr "An event in the '{}' has no id."
132
167
  msgid "{!r} is not a valid state machine name."
133
168
  msgstr "{!r} is not a valid state machine name."
134
169
 
135
- #: statemachine/state.py:249
170
+ #: statemachine/state.py:273
136
171
  msgid "'donedata' can only be specified on final states."
137
172
  msgstr "'donedata' can only be specified on final states."
138
173
 
139
- #: statemachine/statemachine.py:167
174
+ #: statemachine/statemachine.py:168
140
175
  msgid "There are no states or transitions."
141
176
  msgstr "There are no states or transitions."
142
177
 
143
- #: statemachine/statemachine.py:237
178
+ #: statemachine/statemachine.py:242
144
179
  #, python-brace-format
145
180
  msgid "State overriding is not allowed. Trying to add '{}' to {}"
146
181
  msgstr "State overriding is not allowed. Trying to add '{}' to {}"
@@ -158,3 +193,9 @@ msgstr ""
158
193
  #, python-brace-format
159
194
  msgid "{} only supports the decorator syntax to register callbacks."
160
195
  msgstr "{} only supports the decorator syntax to register callbacks."
196
+
197
+ #: statemachine/engines/async_.py:50
198
+ msgid ""
199
+ "The task running the processing loop was cancelled while it processed "
200
+ "this event."
201
+ msgstr ""
@@ -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-06-16 23:00-0300\n"
8
+ "POT-Creation-Date: 2026-10-13 21:26-0300\n"
9
9
  "PO-Revision-Date: 2024-06-07 17:41-0300\n"
10
10
  "Last-Translator: Fernando Macedo <fgmacedo@gmail.com>\n"
11
11
  "Language: hi_IN\n"
@@ -16,7 +16,7 @@ msgstr ""
16
16
  "Content-Transfer-Encoding: 8bit\n"
17
17
  "Generated-By: Babel 2.18.0\n"
18
18
 
19
- #: statemachine/callbacks.py:398 statemachine/callbacks.py:403
19
+ #: statemachine/callbacks.py:409 statemachine/callbacks.py:414
20
20
  #, python-brace-format
21
21
  msgid "Did not found name '{}' from model or statemachine"
22
22
  msgstr "मॉडल या स्टेटमशीन में नाम '{}' नहीं मिला"
@@ -29,12 +29,24 @@ msgstr ""
29
29
  "कोई वर्तमान स्टेट सेट नहीं है। असिंक्रोनस कोड में, क्या आपने प्रारंभिक "
30
30
  "स्टेट को सक्रिय किया? (उदाहरण: `await sm.activate_initial_state()`)"
31
31
 
32
- #: statemachine/dispatcher.py:123
32
+ #: statemachine/dispatcher.py:127
33
33
  #, python-brace-format
34
34
  msgid "Failed to parse boolean expression '{}'"
35
35
  msgstr "बूलियन अभिव्यक्ति '{}' को पार्स करने में विफल रहा"
36
36
 
37
- #: statemachine/event.py:94
37
+ #: statemachine/event.py:56
38
+ #, python-brace-format
39
+ msgid ""
40
+ "Event {id!r} cannot be both delayed and internal: the internal queue runs"
41
+ " within the current macrostep, which takes no time. Drop 'internal' to "
42
+ "deliver it later on the external queue, or drop 'delay' to run it now."
43
+ msgstr ""
44
+ "इवेंट {id!r} एक साथ विलंबित और आंतरिक नहीं हो सकता: आंतरिक कतार वर्तमान "
45
+ "मैक्रोस्टेप के भीतर चलती है, जिसमें कोई समय नहीं लगता। इसे बाद में बाहरी "
46
+ "कतार से भेजने के लिए 'internal' हटाएँ, या इसे अभी चलाने के लिए 'delay' "
47
+ "हटाएँ।"
48
+
49
+ #: statemachine/event.py:108
38
50
  #, python-brace-format
39
51
  msgid ""
40
52
  "Event() received a non-string 'id' ({cls_name}). To combine multiple "
@@ -43,12 +55,12 @@ msgstr ""
43
55
  "Event() को एक गैर-स्ट्रिंग 'id' ({cls_name}) मिला। एक ही इवेंट के तहत कई "
44
56
  "ट्रांज़िशन को जोड़ने के लिए, | ऑपरेटर का उपयोग करें: t1 | t2।"
45
57
 
46
- #: statemachine/event.py:130
58
+ #: statemachine/event.py:147
47
59
  #, python-brace-format
48
60
  msgid "Cannot add callback '{}' to an event with no transitions."
49
61
  msgstr "बिना ट्रांज़िशन वाले इवेंट में कॉलबैक '{}' नहीं जोड़ सकते।"
50
62
 
51
- #: statemachine/event.py:163
63
+ #: statemachine/event.py:182
52
64
  #, python-brace-format
53
65
  msgid "Event {} cannot be called without a SM instance"
54
66
  msgstr "इवेंट {} को SM इंस्टेंस के बिना कॉल नहीं किया जा सकता"
@@ -63,14 +75,43 @@ msgstr "{!r} एक मान्य स्टेट मान नहीं ह
63
75
  msgid "Can't {} when in {}."
64
76
  msgstr "{} स्थिति में {} नहीं कर सकते।"
65
77
 
66
- #: statemachine/factory.py:77
78
+ #: statemachine/factory.py:91
67
79
  #, python-brace-format
68
80
  msgid ""
69
81
  "There should be one and only one initial state. Your currently have "
70
82
  "these: {0}"
71
83
  msgstr "एक और केवल एक प्रारंभिक स्टेट होना चाहिए। वर्तमान में आपके पास ये हैं: {0}"
72
84
 
73
- #: statemachine/factory.py:194
85
+ #: statemachine/factory.py:249
86
+ #, python-brace-format
87
+ msgid "is declared in both the '{}' and the '{}' state bodies"
88
+ msgstr "'{}' और '{}' दोनों स्टेट बॉडी में घोषित है"
89
+
90
+ #: statemachine/factory.py:253
91
+ #, python-brace-format
92
+ msgid "in the '{}' state body clashes with the event '{}'"
93
+ msgstr "'{}' स्टेट बॉडी में इवेंट '{}' से टकराता है"
94
+
95
+ #: statemachine/factory.py:257
96
+ #, python-brace-format
97
+ msgid "is declared in both the '{}' state body and the '{}' class body"
98
+ msgstr "'{}' स्टेट बॉडी और '{}' क्लास बॉडी दोनों में घोषित है"
99
+
100
+ #: statemachine/factory.py:261
101
+ #, python-brace-format
102
+ msgid "in the '{}' state body clashes with the '{}' attribute of '{}'"
103
+ msgstr "'{0}' स्टेट बॉडी में '{2}' के एट्रिब्यूट '{1}' से टकराता है"
104
+
105
+ #: statemachine/factory.py:267
106
+ #, python-brace-format
107
+ msgid ""
108
+ "Callback '{}' {}. Nested state bodies share the class namespace. Rename "
109
+ "one of them."
110
+ msgstr ""
111
+ "कॉलबैक '{}' {}। नेस्टेड स्टेट बॉडी क्लास का नेमस्पेस साझा करती हैं। इनमें"
112
+ " से एक का नाम बदलें।"
113
+
114
+ #: statemachine/factory.py:291
74
115
  #, python-brace-format
75
116
  msgid ""
76
117
  "There should be one and only one initial state. You currently have these:"
@@ -79,12 +120,12 @@ msgstr ""
79
120
  "एक और केवल एक प्रारंभिक स्टेट होना चाहिए। वर्तमान में आपके पास ये हैं: "
80
121
  "{!r}"
81
122
 
82
- #: statemachine/factory.py:209
123
+ #: statemachine/factory.py:306
83
124
  #, python-brace-format
84
125
  msgid "Cannot declare transitions from final state. Invalid state(s): {}"
85
126
  msgstr "अंतिम स्टेट से ट्रांज़िशन घोषित नहीं कर सकते। अमान्य स्टेट: {}"
86
127
 
87
- #: statemachine/factory.py:221
128
+ #: statemachine/factory.py:318
88
129
  #, python-brace-format
89
130
  msgid ""
90
131
  "All non-final states should have at least one outgoing transition. These "
@@ -93,7 +134,7 @@ msgstr ""
93
134
  "सभी गैर-अंतिम स्टेट में कम से कम एक आउटगोइंग ट्रांज़िशन होना चाहिए। इन "
94
135
  "स्टेट में कोई आउटगोइंग ट्रांज़िशन नहीं है: {!r}"
95
136
 
96
- #: statemachine/factory.py:235
137
+ #: statemachine/factory.py:332
97
138
  #, python-brace-format
98
139
  msgid ""
99
140
  "All non-final states should have at least one path to a final state. "
@@ -102,7 +143,7 @@ msgstr ""
102
143
  "सभी गैर-अंतिम स्टेट में अंतिम स्टेट तक कम से कम एक पथ होना चाहिए। इन "
103
144
  "स्टेट में अंतिम स्टेट तक कोई पथ नहीं है: {!r}"
104
145
 
105
- #: statemachine/factory.py:248
146
+ #: statemachine/factory.py:345
106
147
  #, python-brace-format
107
148
  msgid ""
108
149
  "There are unreachable states. The statemachine graph should have a single"
@@ -111,7 +152,7 @@ msgstr ""
111
152
  "कुछ स्टेट पहुंच योग्य नहीं हैं। स्टेटमशीन ग्राफ में एक ही घटक होना चाहिए।"
112
153
  " डिस्कनेक्टेड स्टेट: {}"
113
154
 
114
- #: statemachine/factory.py:285
155
+ #: statemachine/factory.py:382
115
156
  #, python-brace-format
116
157
  msgid ""
117
158
  "Invalid entry in 'listeners': {!r}. Expected a class, callable, or "
@@ -120,7 +161,7 @@ msgstr ""
120
161
  "'listeners' में अमान्य प्रविष्टि: {!r}। एक क्लास, कॉलेबल, या लिसनर "
121
162
  "इंस्टेंस अपेक्षित है।"
122
163
 
123
- #: statemachine/factory.py:384
164
+ #: statemachine/factory.py:481
124
165
  #, python-brace-format
125
166
  msgid "An event in the '{}' has no id."
126
167
  msgstr "'{}' में एक इवेंट का आईडी नहीं है।"
@@ -130,15 +171,15 @@ msgstr "'{}' में एक इवेंट का आईडी नहीं
130
171
  msgid "{!r} is not a valid state machine name."
131
172
  msgstr "{!r} एक मान्य स्टेटमशीन नाम नहीं है।"
132
173
 
133
- #: statemachine/state.py:249
174
+ #: statemachine/state.py:273
134
175
  msgid "'donedata' can only be specified on final states."
135
176
  msgstr "'donedata' केवल अंतिम स्टेट पर निर्दिष्ट किया जा सकता है।"
136
177
 
137
- #: statemachine/statemachine.py:167
178
+ #: statemachine/statemachine.py:168
138
179
  msgid "There are no states or transitions."
139
180
  msgstr "कोई स्टेट या ट्रांज़िशन नहीं हैं।"
140
181
 
141
- #: statemachine/statemachine.py:237
182
+ #: statemachine/statemachine.py:242
142
183
  #, python-brace-format
143
184
  msgid "State overriding is not allowed. Trying to add '{}' to {}"
144
185
  msgstr ""
@@ -159,6 +200,14 @@ msgstr ""
159
200
  msgid "{} only supports the decorator syntax to register callbacks."
160
201
  msgstr "{} केवल कॉलबैक रजिस्टर करने के लिए डेकोरेटर सिंटैक्स का समर्थन करता है।"
161
202
 
203
+ #: statemachine/engines/async_.py:50
204
+ msgid ""
205
+ "The task running the processing loop was cancelled while it processed "
206
+ "this event."
207
+ msgstr ""
208
+ "प्रोसेसिंग लूप चलाने वाला टास्क इस इवेंट को प्रोसेस करते समय रद्द कर दिया"
209
+ " गया।"
210
+
162
211
  #~ msgid "There are no states."
163
212
  #~ msgstr "कोई स्टेट नहीं है।"
164
213