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.
- {python_statemachine-3.2.0.dist-info → python_statemachine-3.2.2.dist-info}/METADATA +7 -6
- python_statemachine-3.2.2.dist-info/RECORD +73 -0
- {python_statemachine-3.2.0.dist-info → python_statemachine-3.2.2.dist-info}/WHEEL +1 -1
- statemachine/__init__.py +1 -1
- statemachine/callbacks.py +11 -0
- statemachine/contrib/diagram/__init__.py +15 -6
- statemachine/contrib/diagram/extract.py +3 -3
- statemachine/contrib/diagram/formatter.py +4 -2
- statemachine/contrib/diagram/graphviz.py +66 -0
- statemachine/contrib/diagram/sphinx_ext.py +28 -7
- statemachine/dispatcher.py +8 -3
- statemachine/engines/async_.py +224 -180
- statemachine/engines/base.py +257 -245
- statemachine/engines/sync.py +224 -108
- statemachine/event.py +29 -10
- statemachine/event_data.py +19 -3
- statemachine/events.py +5 -2
- statemachine/exceptions.py +1 -1
- statemachine/factory.py +126 -29
- statemachine/io/__init__.py +18 -11
- statemachine/io/actions.py +124 -15
- statemachine/io/evaluators.py +37 -9
- statemachine/io/invoke.py +12 -0
- statemachine/io/loader.py +17 -3
- statemachine/io/schemas/statechart.schema.json +8 -1
- statemachine/io/scxml/__init__.py +6 -3
- statemachine/io/scxml/processor.py +1 -1
- statemachine/io/scxml/reader.py +24 -9
- statemachine/io/system_variables.py +34 -19
- statemachine/locale/en/LC_MESSAGES/statemachine.po +58 -17
- statemachine/locale/hi_IN/LC_MESSAGES/statemachine.po +66 -17
- statemachine/locale/pt_BR/LC_MESSAGES/statemachine.po +66 -17
- statemachine/locale/zh_CN/LC_MESSAGES/statemachine.po +60 -17
- statemachine/orderedset.py +12 -3
- statemachine/registry.py +6 -1
- statemachine/spec_parser.py +151 -21
- statemachine/state.py +47 -8
- statemachine/statemachine.py +42 -19
- python_statemachine-3.2.0.dist-info/RECORD +0 -72
- {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-
|
|
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
|
|
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-
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
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-
|
|
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
|
statemachine/io/scxml/reader.py
CHANGED
|
@@ -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 =
|
|
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
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
72
|
+
self._trigger_data = trigger_data
|
|
57
73
|
elif event_data is not None:
|
|
58
|
-
self.
|
|
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.
|
|
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.
|
|
91
|
-
return self.
|
|
92
|
-
return str(self.
|
|
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.
|
|
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
|
-
|
|
117
|
-
|
|
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.
|
|
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-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
178
|
+
#: statemachine/statemachine.py:168
|
|
138
179
|
msgid "There are no states or transitions."
|
|
139
180
|
msgstr "कोई स्टेट या ट्रांज़िशन नहीं हैं।"
|
|
140
181
|
|
|
141
|
-
#: statemachine/statemachine.py:
|
|
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
|
|