Closed memetb closed 2 years ago
Hello @memetb,
Machine
contains a method called _checked_assignment
that you can use to achieve this, I guess.
This is the default implementation:
def _checked_assignment(self, model, name, func):
if hasattr(model, name):
_LOGGER.warning("%sModel already contains an attribute '%s'. Skip binding.", self.name, name)
else:
setattr(model, name, func)
So your use case could be achieved with
from transitions import Machine
class ColonMachine(Machine):
def _checked_assignment(self, model, name, func):
super()._checked_assignment(model, name.replace(":", "_"), func)
s = ColonMachine(states=["foo:a", "foo:b"], initial="foo:a")
assert s.is_foo_a()
s.to_foo_b()
print(s.state)
Thanks @aleneum: can you please maybe help me out with this following test case?
When using "_" for the nested state separator, the following basic example works as expected (I get some transitions and my on_enter
callbacks work as expected). I'd like to be able to do this with ":" instead of "_" for the NestedState.separator
.
from transitions.extensions import HierarchicalMachine
from transitions.extensions.nesting import NestedState
c = '_' # <-- replace this with ':' to test desired test scenario
NestedState.separator = c
class Foo(HierarchicalMachine):
def _checked_assignment(self, model, name, func):
#print(f"check: {model} {name} {func}")
super()._checked_assignment(model, name.replace(":", "_"), func)
def conditionA(self) : return self.tick % 5 == 0
def conditionB(self) : return self.tick % 5 == 1
def conditionC(self) : return self.tick % 5 == 2
def conditionD(self) : return self.tick % 5 == 3
def conditionE(self) : return self.tick % 5 == 4
def on_enter_foo(self):
print("entered foo")
def on_enter_bar(self):
print("entered bar")
def on_enter_foo_A(self):
print("entered foo:A")
def on_enter_foo_B(self):
print("entered foo:B")
def __init__(self):
states = [
{ "name": "foo", "children" : [ "A", "B"], "initial": "A"},
{ "name": "bar", "children" : [ "B", "C", "D"]}
]
HierarchicalMachine.__init__(self, states=states, initial='foo')
self.add_transition(trigger='impl', source='*', dest=f'foo{c}A', conditions='conditionE')
self.add_transition(trigger='impl', source=f'foo{c}A', dest=f'foo{c}B', conditions='conditionB')
self.add_transition(trigger='impl', source='foo', dest=f'bar{c}C', conditions='conditionC')
self.tick = 0
def run(self):
self.tick += 1
self.impl()
fsm = Foo()
for i in range(10):
fsm.run()
When using HSMs transitions
will NOT decorate models with to_stateA_stateB
when the separator has been changed. See the documentation:
This means that in the standard configuration, state names in HSMs MUST NOT contain underscores. For transitions it's impossible to tell whether machine.add_state('state_name') should add a state named state_name or add a substate name to the state state. In some cases this is not sufficient however. For instance if state names consists of more than one word and you want/need to use underscore to separate them instead of CamelCase.
Instead of to_C_3_a() auto transition is called as to_C.s3.a(). If your substate starts with a digit, transitions adds a prefix 's' ('3' becomes 's3') to the auto transition FunctionWrapper to comply with the attribute naming scheme of Python. If interactive completion is not required, to('C↦3↦a') can be called directly.
Since StateA:StateA_1
and StateA:StateA:1
would basically both result in to_StateA_StateA_1
or on_enter_StateA_StateA_1
the above mentioned workaround can be used.
Thank you for your response. I understand the complication.
I'd like to be able to name my states with identifiers that aren't necessarily valid python identifiers: e.g. "high:active" and "high:idle" might be two states I want to have (these states allow me to create statechart compatible fsm's). But I'd also like to be able to declare
on_enter_high_idle
.This could be implemented for instance, by changing the following line (and all other relevant spots):
To:
where
make_python_token
is default implemented as follows:This would allow any user of the library to inherit and specialize the
make_python_token
method.