Skip to content

Commit

Permalink
This fixes #647
Browse files Browse the repository at this point in the history
  • Loading branch information
aleneum committed Jun 10, 2024
1 parent ce0e46f commit 4a3f06a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 11 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## 0.9.2 ()

- Bug #610: Decorate models appropriately when `HierarchicalMachine` is passed to `add_state` (thanks @e0lithic)
- Bug #647: Let `may_<trigger>` check all parallel states in processing order (thanks @spearsear)
- Bug: `HSM.is_state` works with parallel states now

## 0.9.1 (May 2024)

Expand Down
47 changes: 47 additions & 0 deletions tests/test_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,53 @@ def test_model_state_conversion(self):
self.assertEqual(tree, m.build_state_tree(states, sep))
self.assertEqual(states, _build_state_list(tree, sep))

def test_may_transition_with_parallel(self):
states = ['A',
{'name': 'P',
'parallel': [
{'name': '1', 'states': ["a", "b"], "initial": 'a'},
{'name': '2', 'states': ["a", "b"], "initial": 'a',
'transitions': [['valid', 'a', 'b']]}
]}]
m = self.machine_cls(states=states, initial="A")
assert not m.may_valid()
assert m.to_P()
assert m.is_P(allow_substates=True)
assert m.is_P_1_a()
assert m.is_P_2_a()
assert m.may_valid()
assert m.valid()
assert m.is_P_1_a()
assert not m.is_P_2_a()
assert m.is_P_2_b()

def test_is_state_parallel(self):
states = ['A',
{'name': 'P',
'parallel': [
'1',
{'name': '2', 'parallel': [
{'name': 'a'},
{'name': 'b', 'parallel': [
{'name': 'x', 'parallel': ['1', '2']}, 'y'
]}
]},
]}]
m = self.machine_cls(states=states, initial="A")
assert m.is_A()
assert not m.is_P_2()
assert not m.is_P_2_a()
assert not m.is_P_2_b()
assert not m.is_P_2_b_x()
assert not m.is_P(allow_substates=True)
m.to_P()
assert m.is_P_1()
assert m.is_P_2_a()
assert not m.is_P_2()
assert m.is_P(allow_substates=True)
assert m.is_P_2(allow_substates=True)
assert not m.is_A(allow_substates=True)


@skipIf(pgv is None, "pygraphviz is not available")
class TestParallelWithPyGraphviz(TestParallel):
Expand Down
25 changes: 14 additions & 11 deletions transitions/extensions/nesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,9 +759,11 @@ def remove_transition(self, trigger, source="*", dest="*"):
def _can_trigger(self, model, trigger, *args, **kwargs):
state_tree = self.build_state_tree(getattr(model, self.model_attribute), self.state_cls.separator)
ordered_states = resolve_order(state_tree)
for state_path in ordered_states:
with self():
return self._can_trigger_nested(model, trigger, state_path, *args, **kwargs)
with self():
return any(
self._can_trigger_nested(model, trigger, state_path, *args, **kwargs)
for state_path in ordered_states
)

def _can_trigger_nested(self, model, trigger, path, *args, **kwargs):
if trigger in self.events:
Expand Down Expand Up @@ -822,14 +824,15 @@ def has_trigger(self, trigger, state=None):
return trigger in state.events or any(self.has_trigger(trigger, sta) for sta in state.states.values())

def is_state(self, state, model, allow_substates=False):
if allow_substates:
current = getattr(model, self.model_attribute)
current_name = self.state_cls.separator.join(self._get_enum_path(current))\
if isinstance(current, Enum) else current
state_name = self.state_cls.separator.join(self._get_enum_path(state))\
if isinstance(state, Enum) else state
return current_name.startswith(state_name)
return getattr(model, self.model_attribute) == state
tree = self.build_state_tree(listify(getattr(model, self.model_attribute)),
self.state_cls.separator)

path = self._get_enum_path(state) if isinstance(state, Enum) else state.split(self.state_cls.separator)
for elem in path:
if elem not in tree:
return False
tree = tree[elem]
return len(tree) == 0 or allow_substates

def on_enter(self, state_name, callback):
"""Helper function to add callbacks to states in case a custom state separator is used.
Expand Down

0 comments on commit 4a3f06a

Please sign in to comment.