在'之前提高例外情况是安全的。回调过渡。机器模型?

时间:2016-09-06 08:29:37

标签: python transitions fsm

我正在使用transitions FSM库。想象一下,使用以下代码创建应用程序FSM:

from transitions import Machine

import os

class Application(object):

  states = ["idle", "data_loaded"]

  def __init__(self):
    self.data = None
    machine = Machine(model=self, states=Application.states, initial="idle")
    machine.add_transition("filename_dropped",
                           source="idle",
                           dest="data_loaded",
                           before="load_data",
                           conditions="is_valid_filename")
    self.machine = machine

  def drop_filename(self, filename):
    try:
      self.filename_dropped(filename)
    except IOError as exc:
      print "Oops: %s" % str(exc)

  def load_data(self, filename):
    with open(filename) as file:
      self.data = file.read()

  def is_valid_filename(self, filename):
    return os.path.isfile(filename)

它可以在load_data中抛出IOError。我的问题是,在before回调中引发异常(在本例中是隐式的还是显式的)是否安全?如果IOError没有发生转换,则此示例的状态仍为idle,并且未调用任何after回调。但是,我想知道机器实例的内部状态是否会被破坏。

附加问题:是否有更好的方法可以向应用程序发出具体信息错误信号?在这个例子中,我可以使用条件加载文件,但这看起来很难看,我需要一些额外的属性来跟踪错误等。

感谢您提供任何帮助或建议。

1 个答案:

答案 0 :(得分:2)

  

但是,我想知道机器实例的内部状态是否会被破坏。

除非您打算使用转换的queued功能,否则可以在回调函数中使用异常。

按以下顺序执行转换:

prepare -> conditions -> before -> on_exit -> set_state -> on_enter -> after

如果set_state之前的任何内容引发异常或conditions中的某个功能未返回True,则会暂停转换。

您的模型可能处于未定义状态。如果你依赖一些'清理'或者'拆除'在State.on_enterafter

from transitions import Machine
class Model:
    def __init__(self):
        self.busy = False
    def before(self):
        self.busy = True
        raise Exception('oops')
    def after(self):
        # if state transition is done, reset busy
        self.busy = False

model = Model()
m = Machine(model, states=['A','B'], initial='A',
            transitions=[{'trigger':'go', 'source':'A', 'dest':'B',
                          'before':'before', 'after':'after'}])
try:
  model.go()
except Exception as e:
  print "Exception: %s" % e # Exception: oops
print "State: %s" % model.state # State: A
print "Model busy: %r" % model.busy # Model busy: True
  

有没有更好的方法来向应用程序发出具体信息错误信号?

这取决于你想要达到的目标。提高错误/异常通常会暂停当前任务的执行。在我的意见中,这几乎是传播问题的方式。如果你想处理错误并将错误表示为状态,我不会考虑使用conditions丑陋。具有相同trigger的有效转换按其添加顺序进行评估。考虑到这一点以及unless使用conditions的否定符号,您的代码可能如下所示:

from transitions import Machine
import os


class Application(object):
  states = ["idle", "data_loaded", "filename_invalid", "data_invalid"]

  transitions = [
    {'trigger': 'filename_dropped', 'source': 'idle', 
     'dest': 'filename_invalid', 'unless': 'is_valid_filename'},
    {'trigger':'filename_dropped', 'source': 'idle',
     'dest':'data_invalid', 'unless': 'is_valid_data'},
    {'trigger':'filename_dropped', 'source': 'idle',
     'dest':'data_loaded'}
  ]

  def __init__(self):
    self.data = None
    machine = Machine(model=self, states=Application.states,
                      transitions=Application.transitions, initial="idle")
    self.machine = machine

  def drop_filename(self, filename):
      self.filename_dropped(filename)
      if self.is_data_loaded():
          print "Data loaded"

  # renamed load_data
  def is_valid_data(self, filename):
    try:
      with open(filename) as file:
        self.data = file.read()
    except IOError as exc:
      print "File loading error: %s" % str(exc)
      return False
    return True

  def is_valid_filename(self, filename):
    return os.path.isfile(filename)

app = Application()
app.drop_filename('test.txt')
# >>> Data loaded
print app.state
# >>> data_loaded
app.to_idle()
app.drop_filename('missing.txt')
print app.state
# >>> filename_invalid
app.to_idle()
app.drop_filename('secret.txt')
# >>> File loading error: [Errno 13] Permission denied: 'secret.txt'
print app.state
# >>> data_invalid

这将创建此状态机:

enter image description here