【问题标题】:Is raising exceptions safe within 'before' callback of a transitions.Machine model?在 transitions.Machine 模型的“之前”回调中引发异常是否安全?
【发布时间】:2016-09-06 08:29:37
【问题描述】:

我正在使用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 回调。但是,我想知道机器实例的内部状态是否会损坏。

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

感谢您的任何帮助或建议。

【问题讨论】:

  • 关于您的附加问题:您不喜欢引发异常以通知您的应用程序错误吗?
  • 我可以提出异常,我指的是文件加载示例,我不想打开该文件两次(一次在条件中,一次在“之前”插槽中) .我可能在这里以一种不寻常的方式使用条件功能。它可能意味着基于一些附加信息允许的一些事情。

标签: python fsm pytransitions


【解决方案1】:

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

除非您打算使用转换的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

这将创建这个状态机:

【讨论】:

  • 非常感谢您的详细回答。我现在了解条件/除非功能更好。我想我用错了。这个“文件阅读器”的例子更有意义。
猜你喜欢
  • 1970-01-01
  • 2015-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-05
  • 1970-01-01
相关资源
最近更新 更多