diff options
Diffstat (limited to 'category.py')
-rw-r--r-- | category.py | 67 |
1 files changed, 37 insertions, 30 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) |