diff options
Diffstat (limited to 'statemachine.py')
-rw-r--r-- | statemachine.py | 110 |
1 files changed, 72 insertions, 38 deletions
diff --git a/statemachine.py b/statemachine.py index deb8bb7..647b35b 100644 --- a/statemachine.py +++ b/statemachine.py @@ -3,6 +3,7 @@ __docformat__ = 'restructuredtext' from category import Category from pattern import Pattern +from confparse import Conf, WhenSpec, OnSpec, ExecSpec import setcross class StateMachine: @@ -11,15 +12,50 @@ class StateMachine: of states which are "up." """ - def __init__(self): + def __init__(self, conf_str = None, conf_file = None): """ - Create a new state machine + Create a new state machine. `conf_file` if specified is a file to load + configuration from. `conf_str` if specified is a configuration string. + If both are specified `conf_file` takes precedence, and `conf_str` is + used on error. """ self.up = set() self.wanted = set() - self.valid_supercats = set() self.deps = [] self.event_triggers = [] + if conf_file != None: + try: + file = open(conf_file) + conf_str = file.read() + file.close() + except: + pass + if conf_str != None: + self.load_conf(conf_str) + + def load_conf(self, str): + """ + Load some string configuration into this state machine. + """ + conf = Conf(str) + for block in conf.propmatches: + self.load_conf_block(block) + + def load_conf_block(self, block): + """ + Load a single block of text configuration. `block` is the parse tree + node at the root of the block. + """ + cat = block.pattern.to_category() + for prop in block.properties: + prop = prop.spec + if prop.__class__ == WhenSpec: + self.deps.append((cat, prop.waiting_for.to_category())) + elif prop.__class__ == OnSpec: + self.event_triggers.append( + (prop.waiting_for.to_category(), cat)) + else: # prop.__class__ == ExecSpec + pass #TODO: Exec support def emit(self, event): """ @@ -33,18 +69,6 @@ class StateMachine: def bring_up(self, cat, wanted=True): """ - Move states in the given Category `cat` from down to up. - """ - retval = False - for supercat in self.valid_supercats: - newcat = supercat.intersect(cat) - if newcat == None: - continue - retval = retval or self.__bring_up(newcat, wanted) - return retval - - def __bring_up(self, cat, wanted=True): - """ Move states in the given Category `cat` from down to up. Don't check to see if the category makes sense first. """ @@ -58,13 +82,15 @@ class StateMachine: else: found.append(res) if found == None: - self.add_hold(cat, wanted) - sm.emit(Category("ε")) + if self.add_hold(cat, wanted): + sm.emit(Category("ε")) return True to_add = self.cat_cross(found) + added = False for x in to_add: - self.add_hold(x, wanted) - sm.emit(Category("ε")) + added = self.add_hold(x, wanted) or added + if added: + sm.emit(Category("ε")) return True def cat_cross(self, found): @@ -89,17 +115,19 @@ class StateMachine: def add_hold(self, cat, wanted): """ - Add a hold to a state. Does not check dependencies. + Add a hold to a state. Does not check dependencies. Returns False if the + state was up, True if it wasn't. """ for x in self.up: if cat.subset_of(x): - return + return False if x.subset_of(cat): self.up.remove(x) self.up.add(cat) - return + return True self.up.add(cat) if wanted: self.wanted.add(cat) + return True def bring_down(self, cat, rec=False): """ @@ -140,11 +168,13 @@ class StateMachine: return a new Category that contains only the states in `dependents` that could match states in `dependencies`. """ - retval = [] + retval = set() for cat in self.up: if dependencies.superset_of(cat): - retval.append(dependents.intersect_args(**cat.args)) - return set(retval) | dependents.inverse_set() + retval.add(dependents.intersect_args(**cat.args)) + if None in retval: + retval.remove(None) + return retval | dependents.inverse_set() def get_applicable_deps(self, cat): """ @@ -168,27 +198,31 @@ def nm(*args): return Pattern(False, *args) # Reads as "don't match." See above def any(): return nm() # Match anything. Implementation reads "don't match nothing" if __name__ == "__main__": - sm = StateMachine() - sm.deps.append((Category("mounted", type=m("nfs")), Category("network_up"))) - sm.deps.append((Category("mounted", type=nm("nfs")), Category("found_disk"))) - sm.deps.append((Category("mounted"), Category("vol_conf"))) + sm = StateMachine(conf_str = """ +mounted(type: nfs): + when network_up + +mounted(type: ! nfs): + when found_disk - sm.valid_supercats.add(Category("network_up")) - sm.valid_supercats.add(Category("vol_conf")) - sm.valid_supercats.add(Category("found_disk")) - sm.valid_supercats.add(Category("mounted", devname=any(), mountpoint=any())) +mounted: + when vol_conf - sm.event_triggers.append((Category("fstab_line"), Category("vol_conf", src=m("fstabd")))) +mounted(auto: 1): auto +vol_conf(src: fstabd) on fstab_line +""") sm.bring_up(Category("network_up")) sm.bring_up(Category("found_disk", uuid=m("d3adb3ef"), devname=m("/dev/sda"), label=m("myroot"))) sm.bring_up(Category("mounted")) print sm print "--" - sm.emit(Category("fstab_line", label=m("myroot"), type=m("ext3"), mountpoint=m("/"))) - sm.emit(Category("fstab_line", devname=m("foosrv.com:/vol/home"), type=m("nfs"), mountpoint=m("/home"))) - sm.emit(Category("fstab_line", devname=m("foosrv.com:/vol/beefs"), type=m("nfs"), mountpoint=m("/beefs"))) - sm.bring_up(Category("mounted", devname=any(), mountpoint=any())) + sm.emit(Category("fstab_line", label=m("myroot"), type=m("ext3"), mountpoint=m("/"), auto=m("1"))) + sm.emit(Category("fstab_line", devname=m("foosrv.com:/vol/home"), type=m("nfs"), mountpoint=m("/home"), auto=m("0"))) + sm.emit(Category("fstab_line", devname=m("foosrv.com:/vol/beefs"), type=m("nfs"), mountpoint=m("/beefs"), auto=m("0"))) + print sm + print "--" + sm.bring_up(Category("mounted")) print sm print "--" sm.bring_down(Category("network_up")) |