如果转换无效,是否可以阻止触发器的执行?

Is it possible to block execution of a trigger if the transition isn't valid?

似乎触发方法仍然 运行 然后在从当前状态转换无效时引发 MachineError 异常。有没有办法阻止触发器的执行,以便在模型上调用触发器只会引发异常而不执行触发器?

抱歉,忘记提及使用常见问题解答中的覆盖 _checked_assignment,这可能是导致此行为的原因。

from transitions import State, Machine

class StateMachine(Machine):

    def _checked_assignment(self, model, name, func):
        if hasattr(model, name):
            predefined_func = getattr(model, name)
            def nested_func(*args, **kwargs):
                predefined_func()
                func(*args, **kwargs)
            setattr(model, name, nested_func)
        else:
            setattr(model, name, func)

class Rocket(StateMachine):
    def __init__():
        StateMachine.__init__(
            self,
            states=["on_pad", "fueling", "ready", "launched", "meco", "second_stage", "orbit"],
            transitions=[
                {'trigger': 'fuel', 'source': 'on_pad', 'dest': 'fueling'},
                {'trigger': 'power_on', 'source': 'fueling', 'dest': 'ready'},
                {'trigger': 'launch', 'source': 'ready', 'dest': 'launched'}
            ],
            initial='on_pad'
        )

    def fuel():
        print("cryos loading...")

    def launch():
        print("launching")

def main():
    rocket = Rocket()
    rocket.launch()  # prints "launching" then throws Machine Error, need to block actual method execution

在我输入示例时发现问题,需要在 _checked_assignment 内的 predefined_func() 之前调用传入的 func()。此方法应在 FAQ 上更新以保留原始转换功能。

    def _checked_assignment(self, model, name, func):
        if hasattr(model, name):
            predefined_func = getattr(model, name)
            def nested_func(*args, **kwargs):
                func(*args, **kwargs)  # need to call before pre-defined
                predefined_func()
            setattr(model, name, nested_func)
        else:
            setattr(model, name, func)

虽然您可以按照 answer, I'd recommend tying methods that should be called in the context of a transition to its callbacks. Callbacks can be called on multiple occasions during a transition as described in the documentation's chapter Callback execution order 中的描述使用 Machine._checked_assignment 的覆盖来包装您的回调。需要注意的是,回调不能与预期的触发器同名,但这通常是一个小挫折,并且还可以让您向同一事件添加多个回调。我稍微修改了你的例子。 Rocket 充当有状态模型,但机器本身已分离。如果您计划使用多个实例,您还可以完全独立于 Rocket 管理状态机。一台机器可以处理多个有状态对象。此外,我稍微重命名了您的回调并将它们传递给转换的 before 关键字。如前所述,这也可以是一个列表({'before': ['on_launch']} 也有效)。这样,它们将在转换发生之前立即被调用,并且当 a) Rocket 不处于正确状态或 b) 相关转换的条件检查失败时将不会被调用。

from transitions import Machine, MachineError


class Rocket:
    
    def __init__(self):
        self.machine = Machine(
            self,
            states=["on_pad", "fueling", "ready", "launched", "meco", "second_stage", "orbit"],
            transitions=[
                {'trigger': 'fuel', 'source': 'on_pad', 'dest': 'fueling', 'before': 'on_fueling'},
                {'trigger': 'power_on', 'source': 'fueling', 'dest': 'ready'},
                {'trigger': 'launch', 'source': 'ready', 'dest': 'launched', 'before': 'on_launch'}
            ],
            initial='on_pad'
        )

    def on_fueling(self):
        print("cryos loading...")

    def on_launch(self):
        print("launching")


rocket = Rocket()
try:
    rocket.launch()
    assert False
except MachineError:
    pass

rocket.fuel()  # >>> cryos loading...
rocket.power_on() 
rocket.launch()  # >>> launching