Transition - validate against model/machine
See original GitHub issue- Python State Machine version: 0.7.1
- Python version: 3.6.
- Operating System: macOS 10.14.6
Description
Hi!
I’m trying to figure out how to include the machine’s model or even the machine itself into validation parameters - let me illustrate it by an example.
Assume we have this machine with a single “run” transition and a class representing a runner. Of course running requires some energy, so a side effect of it is, naturally, a decrease in energy:
from statemachine import StateMachine, State
class RunningMachine(StateMachine):
start = State('start', initial=True)
end = State('end')
run = start.to(end)
def on_run(self):
self.model.energy -= 10
class Runner:
def __init__(self, energy):
self.energy = energy
self.state = 'start'
runner = Runner(15)
RunningMachine(runner).run()
print(runner.energy) # 5
print(runner.state) # end
So far so good!
Now what I’d like to do is to make sure that Runner
has enough energy to run in the first place, so I add a validation like this:
def has_enough_energy(runner):
assert runner.energy >= 10
class RunningMachine:
...
run = start.to(end)
run.validators = [has_enough_energy]
The problem is that in order to use it I now have to pass a Runner
instance as an argument for transition, otherwise it won’t make it to the validation function:
class RunningMachine:
...
def on_run(self, runner):
runner.energy -= 10
machine = RunningMachine(runner)
machine.run(runner) # wait a second, don't you have reference to the 'runner' already?
It can get even worse since the runner I pass into it might in fact be a different runner! Which of course leads to an inconsistency:
fresh_runner = Runner(10)
tired_runner = Runner(0)
machine = RunningMachine(tired_runner)
machine.run(fresh_runner) # oops...
print(fresh_runner.energy) # 0
print(fresh_runner.state) # start
print(tired_runner.state) # end
What I Did
Well there’s not much I can do about it but make sure to pass the same Runner
instance to the transition itself. It doesn’t come handy and raises some issues I’ve outlined above so it all comes down to the following questions:
- Am I missing something / doing something wrong? I.e. is there a (better) way to include the model/machine into the validation process?
- If there’s none,
validators
functionality could probably be improved by allowingstr
arguments, e.g.:
class RunningMachine:
run = a.to(b)
def can_run(self):
assert self.runner.energy >= 10
run.validators = ['can_run']
or by allowing class method (this would require some checks so that transition would know if it should pass model
to the validator in order to not break compatibility):
class RunningMachine:
def can_run(self):
assert self.model.energy >= 10
@start.to(end, validators=can_run)
def run(self):
self.model.energy -= 10
Of course I’d be happy to create a PR implementing either of these approaches - or any other if that would make more sense to you.
Thanks!
Issue Analytics
- State:
- Created 4 years ago
- Comments:8 (2 by maintainers)
Top GitHub Comments
Hi @claverru! Thanks for your continued attempts to solve my issue! 😃 apparently I’m not that good when it comes to highlighting the important stuff hehe; anyway, let me elaborate one more time.
My main and only issue is adding validators to transitions. Even if I convert my StateMachine into Runner (which I’d like to avoid - I prefer my models being simple), it doesn’t solve validation issue since validators as they are right now do not have access to model nor state machine itself. Instead, they only have access to the same arguments you pass into transition.
E.g. In this example
if you add a validator to the
on_run
transition, your validator will only receivefoo
argument and that’s it. Therefore, if you want to validate against StateMachine’smodel
, you have to pass thismodel
as an argument to the transition, which might potentially break data consistency (see my previous examples).Hi @arseniy-panfilov , nice snippet! It really shows that something is missing. We can’t keep up with this
validators
API.Let me share what I’m thinking about.
I’m reading about
statecharts
since SCXML (Statechart XML), is a W3C standard and it defines a lot of the semantics and specifies how to deal with certain edge cases. A statechart is essentially a state machine that allows any state to include more machines, in a hierarchical fashion. This is already a feature request at https://github.com/fgmacedo/python-statemachine/issues/246.With that in mind, my goal to this library may turn to be as much as possible compatible with
statecharts
but using a different syntax. A pythonic interface to statecharts.So, related with your issue, we should implement ‘guard’.