Skip to content

Commit

Permalink
fix: Fix some edge cases of initial state configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
fgmacedo committed Dec 23, 2024
1 parent 30576f1 commit be5bfb8
Show file tree
Hide file tree
Showing 11 changed files with 36 additions and 181 deletions.
5 changes: 1 addition & 4 deletions statemachine/engines/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,10 +602,7 @@ def add_descendant_states_to_enter(
)
elif state and state.is_compound:
states_for_default_entry.add(info)
initial_state = next(s for s in state.states if s.initial)
transition = next(
t for t in state.transitions if t.initial and t.target == initial_state
)
transition = next(t for t in state.transitions if t.initial)
info_initial = StateTransition(
transition=transition,
target=transition.target,
Expand Down
3 changes: 2 additions & 1 deletion statemachine/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ def _initials_by_document_order(
for s in states:
s.document_order = order
order += 1
cls._initials_by_document_order(s.states, s, order)
if s.states:
cls._initials_by_document_order(s.states, s, order)
if s.initial:
initial = s
if not initial and states:
Expand Down
2 changes: 1 addition & 1 deletion statemachine/io/scxml/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from .schema import ScriptAction

logger = logging.getLogger(__name__)
protected_attrs = _event_data_kwargs | {"_sessionid", "_ioprocessors", "_name"}
protected_attrs = _event_data_kwargs | {"_sessionid", "_ioprocessors", "_name", "_event"}


class ParseTime:
Expand Down
25 changes: 24 additions & 1 deletion statemachine/io/scxml/parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re
import xml.etree.ElementTree as ET
from typing import Iterable
from typing import Set

from .schema import Action
Expand Down Expand Up @@ -33,13 +34,20 @@ def strip_namespaces(tree: ET.Element):
attrib[new_name] = attrib.pop(name)

Check warning on line 34 in statemachine/io/scxml/parser.py

View check run for this annotation

Codecov / codecov/patch

statemachine/io/scxml/parser.py#L33-L34

Added lines #L33 - L34 were not covered by tests


def visit_states(states: Iterable[State], parents: list[State]):
for state in states:
yield state, parents
if state.states:
yield from visit_states(state.states.values(), parents + [state])


def _parse_initial(initial_content: "str | None") -> Set[str]:
if initial_content is None:
return set()
return set(initial_content.split())


def parse_scxml(scxml_content: str) -> StateMachineDefinition:
def parse_scxml(scxml_content: str) -> StateMachineDefinition: # noqa: C901
root = ET.fromstring(scxml_content)
strip_namespaces(root)

Expand Down Expand Up @@ -75,6 +83,21 @@ def parse_scxml(scxml_content: str) -> StateMachineDefinition:
for s in definition.initial_states:
definition.states[s].initial = True

# If the initial states definition does not contain any first level state,
# we find the first level states that are ancestor of the initial states
# and also set them as the initial states.
if not set(definition.states.keys()) & definition.initial_states:
not_found = set(definition.initial_states)
for state, parents in visit_states(definition.states.values(), []):
if state.id in definition.initial_states:
not_found.remove(state.id)
if parents:
topmost_state = parents[0]
topmost_state.initial = True
definition.initial_states.add(topmost_state.id)
if not not_found:
break

return definition


Expand Down
8 changes: 6 additions & 2 deletions statemachine/io/scxml/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,16 @@ def process_definition(self, definition, location: str):
if definition.datamodel:
datamodel = create_datamodel_action_callable(definition.datamodel)
if datamodel:
initial_state = next(s for s in iter(states_dict.values()) if s["initial"])
try:
initial_state = next(s for s in iter(states_dict.values()) if s.get("initial"))
except StopIteration:

Check warning on line 70 in statemachine/io/scxml/processor.py

View check run for this annotation

Codecov / codecov/patch

statemachine/io/scxml/processor.py#L70

Added line #L70 was not covered by tests
# If there's no explicit initial state, use the first one
initial_state = next(iter(states_dict.values()))

Check warning on line 72 in statemachine/io/scxml/processor.py

View check run for this annotation

Codecov / codecov/patch

statemachine/io/scxml/processor.py#L72

Added line #L72 was not covered by tests

if "enter" not in initial_state:
initial_state["enter"] = []
if isinstance(initial_state["enter"], list):
initial_state["enter"].insert(0, datamodel)

self._add(location, {"states": states_dict, "prepare_event": self._prepare_event})

def _prepare_event(self, *args, **kwargs):
Expand Down
38 changes: 0 additions & 38 deletions tests/scxml/w3c/mandatory/test346.fail.md

This file was deleted.

59 changes: 0 additions & 59 deletions tests/scxml/w3c/mandatory/test355.fail.md

This file was deleted.

1 change: 1 addition & 0 deletions tests/scxml/w3c/mandatory/test355.scxml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ we enter s0 first we succeed, if s1, failure. -->

<state id="s0">
<transition target="pass" />
<transition target="s1" cond="1 &lt; 0" /><!-- fgm: added to have a single component in graph -->
</state>

<state id="s1">
Expand Down
38 changes: 0 additions & 38 deletions tests/scxml/w3c/mandatory/test372.fail.md

This file was deleted.

37 changes: 0 additions & 37 deletions tests/scxml/w3c/mandatory/test413.fail.md

This file was deleted.

1 change: 1 addition & 0 deletions tests/scxml/w3c/mandatory/test413.scxml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ states we should not enter all have immediate transitions to failure in them -->
we're in
either s2p112 or s2p122, but not both of them -->
<transition target="fail" />
<transition target="s1" cond="1 &lt; 0" /><!-- fgm: added to have a single component in graph -->

<state id="s2p11" initial="s2p111">
<state id="s2p111">
Expand Down

0 comments on commit be5bfb8

Please sign in to comment.