From b746901eb0eaf46e163ec1a52211f86fdfbc45f1 Mon Sep 17 00:00:00 2001 From: Casey Dahlin Date: Mon, 22 Dec 2008 02:58:28 -0500 Subject: Modify system to use proper patterns rather than just strings and blanks --- category.py | 67 +++++++++++++++++++++++++++++++-------------------------- statemachine.py | 38 +++++++++++++++++--------------- 2 files changed, 58 insertions(+), 47 deletions(-) diff --git a/category.py b/category.py index 51a36b8..61458dc 100644 --- a/category.py +++ b/category.py @@ -1,5 +1,7 @@ __docformat__ = 'restructuredtext' +from pattern import Pattern + class Category: """ A Category is a "set" of states. The category specifies a name, certain @@ -13,26 +15,38 @@ class Category: Categories are constructed with a `name` and a series of keyword arguments. `name` is a string. Each keyword argument that is specified is the name of a state argument a state must posess to be considered to - be a member of the category. If the value of the keyword argument is - `None` then the state argument can have any value, otherwise the value is a string - and specifies a mandatory value for the state argument. + be a member of the category. The value of the keyword arguments are + patterns that must match the value of the state argument. """ self.args = args self.name = name def equiv(self, other): """ - Returns True if any state in the Category `other` is in this Category. Note that - this is *intransitive*; ``a.equiv(b)`` is not the same as ``b.equiv(a)``. + A Category is equivalent to another Category if: + - The other category mandates the same or more arguments + - The intersection of the two is nonempty + Its an odd relationship, brought about to match expected user behavior """ if self.name != other.name: return False for key, value in self.args.iteritems(): if not other.args.has_key(key): return False - if value == None: continue - if other.args[key] == None: continue - if not other.args[key] == value: return False + if not other.args[key].intersect(value).nonempty(): return False return True - + + def subtract(self, other): + """ + returns a set of categories that, when combined, include all states + that are in `self` but *not* in `other` + """ + retval = set() + for key, value in other.args.iteritems(): + args = {} + for k, v in self.args.iteritems(): args[k] = v + args[key].intersect(value.complement()) + if args[key].nonempty(): + retval.add(Category(self.name, **args)) + def intersect(self, other): """ Returns a new category consisting only of states that are in this @@ -45,28 +59,19 @@ class Category: args[key] = other.args[key] elif not other.args.has_key(key): args[key] = self.args[key] - elif self.args[key] == None: - args[key] = other.args[key] - elif other.args[key] == None: - args[key] = self.args[key] - elif self.args[key] != other.args[key]: - return None - else: # self.args[key] == other.args[key] - args[key] = self.args[key] + else: + val = self.args[key].intersect(other.args[key]) + if not val.nonempty(): + return None + args[key] = val return Category(self.name, **args) def __str__(self): - string = self.name + "(" - had = False - for key, val in self.args.iteritems(): - if had: - string += ", " - had = True - if val == None: - string += "%%%s" % key - else: - string += "%s: %s" % (key, val) - return string + ")" + return "%s(%s)" % ( + self.name, + ", ".join(["%s: %s" % (key, val) \ + for key, val in self.args.iteritems()]) + ) def __repr__(self): return self.__str__() @@ -95,8 +100,10 @@ class Category: """ args = {} for key, value in self.args.iteritems(): - if value != None or not info.has_key(key): + if not info.has_key(key): args[key] = value - else: + elif not value.singular(): args[key] = info[key] + else: + args[key] = value return Category(self.name, **args) diff --git a/statemachine.py b/statemachine.py index 5c396eb..cc90793 100644 --- a/statemachine.py +++ b/statemachine.py @@ -1,6 +1,7 @@ __docformat__ = 'restructuredtext' from category import Category +from pattern import Pattern class StateMachine: """ @@ -20,10 +21,11 @@ class StateMachine: Move states in the given Category `cat` from down to up. """ found = None - for dependency in self.get_applicable_deps(cat): + for (match, dependency) in self.get_applicable_deps(cat): res = self.get_satisfied_states(cat, dependency) + print dependency, res if len(res) == 0: - return False + res = cat.subtract(match) if found == None: found = res else: @@ -44,18 +46,12 @@ class StateMachine: least one category in the returned list. """ retval = set() - found = set() for x in cats1: for y in cats2: - if x == y: continue inter = x.intersect(y) if inter != None: retval.add(inter) - found.add(x) - found.add(y) - if len(found) == 0: - return cats1 & cats2 - return (retval | ((cats1 & cats2) - found)) + return retval def add_hold(self, cat): """ @@ -85,15 +81,23 @@ class StateMachine: retval = [] for (x, y) in self.deps: if x.equiv(cat): - retval.append(y.fill(cat.intersect(x).args)) + retval.append((x, y.fill(cat.intersect(x).args))) return retval + def __str__(self): + return "\n".join(["%s: %s" % (v, k) for k, v in self.holds.iteritems()]) + + def __repr__(self): + return str(self) + if __name__ == "__main__": sm = StateMachine() - sm.deps.append((Category("mounted", type="nfs"), Category("network_up"))) - sm.deps.append((Category("mounted", uuid=None, devname=None, label=None), Category("found_disk", uuid=None, devname=None, label=None))) - sm.deps.append((Category("mounted", uuid=None, devname=None, label=None), Category("vol_conf", uuid=None, devname=None, label=None))) - sm.assert_state(Category("vol_conf", uuid=None, devname=None, label="myroot", type="ext3", mountpoint="/")) - sm.assert_state(Category("found_disk", uuid="d3adb3ef", devname="/dev/sda", label="myroot")) - sm.assert_state(Category("mounted", uuid=None, type="ext3", devname=None, label=None, mountpoint=None)) - print sm.holds + sm.deps.append((Category("mounted", type=Pattern(True, "nfs")), Category("network_up"))) + sm.deps.append((Category("mounted", uuid=Pattern(False), devname=Pattern(False), label=Pattern(False), type=Pattern(False, "nfs")), Category("found_disk", uuid=Pattern(False), devname=Pattern(False), label=Pattern(False)))) + sm.deps.append((Category("mounted", uuid=Pattern(False), devname=Pattern(False), label=Pattern(False)), Category("vol_conf", uuid=Pattern(False), devname=Pattern(False), label=Pattern(False)))) + sm.assert_state(Category("network_up")) + sm.assert_state(Category("vol_conf", uuid=Pattern(False), devname=Pattern(False), label=Pattern(True, "myroot"), type=Pattern(True, "ext3"), mountpoint=Pattern(True, "/"))) + sm.assert_state(Category("vol_conf", uuid=Pattern(False), devname=Pattern(True, "foosrv.com:/vol/home"), label=Pattern(False), type=Pattern(True, "nfs"), mountpoint=Pattern(True, "/home"))) + sm.assert_state(Category("found_disk", uuid=Pattern(True, "d3adb3ef"), devname=Pattern(True, "/dev/sda"), label=Pattern(True, "myroot"))) + sm.assert_state(Category("mounted", uuid=Pattern(False), type=Pattern(False), devname=Pattern(False), label=Pattern(False), mountpoint=Pattern(False))) + print sm -- cgit