aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'portage/restrictions/boolean.py')
-rw-r--r--portage/restrictions/boolean.py411
1 files changed, 411 insertions, 0 deletions
diff --git a/portage/restrictions/boolean.py b/portage/restrictions/boolean.py
new file mode 100644
index 0000000..05bd820
--- /dev/null
+++ b/portage/restrictions/boolean.py
@@ -0,0 +1,411 @@
+# Copyright: 2005 Gentoo Foundation
+# Author(s): Brian Harring (ferringb@gentoo.org)
+# License: GPL2
+# $Header: /local/data/ulm/cvs/history/var/cvsroot/gentoo-src/portage/portage/restrictions/boolean.py,v 1.1 2005/08/16 00:19:57 ferringb Exp $
+
+"""
+This module provides classes that can be used to combine arbitrary collections of restrictions in AND, NAND, OR, NOR, XOR, XNOR
+style operations.
+"""
+
+from itertools import imap, islice
+from portage.util.iterate import enumerate
+
+__all__ = ("AndRestriction", "OrRestriction", "XorRestriction")
+import restriction
+
+class base(restriction.base):
+ __slots__ = tuple(["restrictions"] + restriction.base.__slots__)
+ required_base = None
+
+ def __init__(self, *restrictions, **kwds):
+ """Optionally hand in (positionally) restrictions to use as the basis of this restriction
+ finalize=False, set it to True to notify this instance to internally finalize itself (no way to reverse it yet)
+ negate=False, controls whether matching results are negated
+ """
+ if "finalize" in kwds:
+ finalize = kwds["finalize"]
+ del kwds["finalize"]
+ else:
+ finalize = False
+ super(base, self).__init__(**kwds)
+ for x in restrictions:
+ if not isinstance(x, restriction.base):
+ #bad monkey.
+ raise TypeError, x
+
+ if finalize:
+ self.restrictions = tuple(restrictions)
+ else:
+ self.restrictions = list(restrictions)
+
+
+ def add_restriction(self, *new_restrictions):
+ """add restriction(s), must be isinstance of required_base
+ """
+ if len(new_restrictions) == 0:
+ raise TypeError("need at least one restriction handed in")
+ for r in new_restrictions:
+ if not isinstance(r, self.required_base):
+ raise TypeError("instance '%s' isn't a derivative '%s'" % (r, self.required_base))
+
+ self.restrictions.extend(new_restrictions)
+
+ def finalize(self):
+ self.restrictions = tuple(self.restrictions)
+
+ def total_len(self): return sum(imap(lambda x: x.total_len(), self.restrictions)) + 1
+
+ def __len__(self): return len(self.restrictions)
+
+ def __iter__(self): return iter(self.restrictions)
+
+ def match(self, action, *vals):
+ raise NotImplementedError
+
+ force_False, force_True = match, match
+
+ def __getitem__(self, key):
+ return self.restrictions[key]
+
+
+# this beast, handles N^2 permutations. convert to stack based.
+def iterative_quad_toggling(pkg, pvals, restrictions, starting, end, truths, filter, desired_false=None, desired_true=None, kill_switch=None):
+ if desired_false == None:
+ desired_false = lambda r, a:r.force_False(*a)
+ if desired_true == None:
+ desired_true = lambda r, a:r.force_True(*a)
+
+# import pdb;pdb.set_trace()
+ reset = True
+ if starting == 0:
+ if filter(truths):
+ yield True
+ for index, rest in enumerate(restrictions, starting, end):
+ if reset:
+ entry = pkg.changes_count()
+ reset = False
+ if truths[index]:
+ if desired_false(rest, pvals):
+ reset = True
+ t = truths[:]
+ t[index] = False
+ if filter(t):
+ yield True
+ for x in iterative_quad_toggling(pkg, pvals, restrictions, index + 1, end, t, filter,
+ desired_false=desired_false, desired_true=desired_true, kill_switch=kill_switch):
+# import pdb;pdb.set_trace()
+ yield True
+ reset = True
+ else:
+ if kill_switch != None and kill_switch(truths, index):
+ return
+ else:
+ if desired_true(rest, pvals):
+ reset = True
+ t = truths[:]
+ t[index] = True
+ if filter(t):
+ yield True
+ for x in iterative_quad_toggling(pkg, pvals, restrictions, index + 1, end, t, filter,
+ desired_false=desired_false, desired_true=desired_true):
+# import pdb;pdb.set_trace()
+ yield True
+ reset = True
+ elif index == end:
+ if filter(truths):
+# import pdb;pdb.set_trace()
+ yield True
+ else:
+ if kill_switch != None and kill_switch(truths, index):
+ return
+
+ if reset:
+ pkg.rollback(entry)
+
+
+class AndRestriction(base):
+ """Boolean AND grouping of restrictions. negation is a NAND"""
+ __slots__ = tuple(base.__slots__)
+
+ def match(self, vals):
+ if self.negate:
+ # 1|1 == 0, 1|0 == 0|1 == 0|0 == 1
+ missed = False
+ for rest in self.restrictions:
+ if not rest.match(vals):
+ missed = True
+ elif missed:
+ return True
+ return False
+ for rest in self.restrictions:
+ if not rest.match(vals):
+ return False
+ return True
+
+ def force_True(self, pkg, *vals):
+ pvals = [pkg]
+ pvals.extend(vals)
+ entry_point = pkg.changes_count()
+ # get the simple one out of the way first.
+ if not self.negate:
+ for r in self.restrictions:
+ if not r.force_True(*pvals):
+ pkg.rollback(entry_point)
+ return
+ yield True
+ return
+
+ # <insert page long curse here>, NAND logic, len(restrictions)**2 potential solutions.
+ # 0|0 == 0, 0|1 == 1|0 == 0|0 == 1.
+ # XXX this is quadratic. patches welcome to dodge the requirement to push through all potential
+ # truths.
+ truths = [r.match(*pvals) for r in self.restrictions]
+ def filter(truths):
+ return False in truths
+
+ for x in iterative_quad_toggling(pkg, pvals, self.restrictions, 0, len(self.restrictions), truths, filter):
+ yield True
+
+ def force_False(self, pkg, *vals):
+ pvals = [pkg]
+ pvals.extend(vals)
+ entry_point = pkg.changes_count()
+ # get the simple one out of the way first.
+ if self.negate:
+ for r in self.restrictions:
+ if not r.force_True(*pvals):
+ pkg.rollback(entry_point)
+ return
+ yield True
+ return
+
+ # <insert page long curse here>, NAND logic, (len(restrictions)^2)-1 potential solutions.
+ # 1|1 == 0, 0|1 == 1|0 == 0|0 == 1.
+ # XXX this is quadratic. patches welcome to dodge the requirement to push through all potential
+ # truths.
+ truths = [r.match(*pvals) for r in self.restrictions]
+ def filter(truths):
+ return False in truths
+ for x in iterative_quad_toggling(pkg, pvals, self.restrictions, 0, len(self.restrictions), truths, filter):
+ yield True
+
+ def __str__(self):
+ if self.negate: return "not ( %s )" % " && ".join(imap(str, self.restrictions))
+ return "( %s )" % " && ".join(imap(str, self.restrictions))
+
+
+class OrRestriction(base):
+ """Boolean OR grouping of restrictions."""
+ __slots__ = base.__slots__
+
+ def match(self, vals):
+ if self.negate:
+ # 1|1 == 1|0 == 0|1 == 0, 0|0 == 1
+ for rest in self.restrictions:
+ if rest.match(vals):
+ return
+ return True
+ for rest in self.restrictions:
+ if rest.match(vals):
+ return True
+ return False
+
+ def force_True(self, pkg, *vals):
+ pvals = [pkg]
+ pvals.extend(vals)
+ entry_point = pkg.changes_count()
+ # get the simple one out of the way first.
+ if self.negate:
+ for r in self.restrictions:
+ if not r.force_False(*pvals):
+ pkg.rollback(entry_point)
+ return
+ yield True
+ return
+
+ # <insert page long curse here>, OR logic, len(restrictions)**2-1 potential solutions.
+ # 0|0 == 0, 0|1 == 1|0 == 1|1 == 1.
+ # XXX this is quadratic. patches welcome to dodge the requirement to push through all potential
+ # truths.
+ truths = [r.match(*pvals) for r in self.restrictions]
+ def filter(truths):
+ return True in truths
+ for x in iterative_quad_toggling(pkg, pvals, self.restrictions, 0, len(self.restrictions), truths, filter):
+ yield True
+
+
+ def force_False(self, pkg, *vals):
+ pvals = [pkg]
+ pvals.extend(vals)
+ entry_point = pkg.changes_count()
+ # get the simple one out of the way first.
+ if not self.negate:
+ for r in self.restrictions:
+ if not r.force_False(*vals):
+ pkg.rollback(entry_point)
+ return
+ yield True
+ return
+
+ # <insert page long curse here>, OR logic, (len(restrictions)**2)-1 potential solutions.
+ # 0|0 == 0, 0|1 == 1|0 == 1|1 == 1.
+ # XXX this is quadratic. patches welcome to dodge the requirement to push through all potential
+ # truths.
+ truths = [r.match(*pvals) for r in self.restrictions]
+ def filter(truths):
+ return True in truths
+ for x in iterative_quad_toggling(pkg, pvals, self.restrictions, 0, len(self.restrictions), truths, filter):
+ yield True
+
+
+ def __str__(self):
+ if self.negate: return "not ( %s )" % " || ".join(imap(str, self.restrictions))
+ return "( %s )" % " || ".join(imap(str, self.restrictions))
+
+
+class XorRestriction(base):
+ """Boolean XOR grouping of restrictions."""
+ __slots__ = tuple(base.__slots__)
+
+ def match(self, vals):
+ if len(self.restrictions) == 0:
+ return not self.negate
+
+ if self.negate:
+ # 1|1 == 0|0 == 1, 0|1 == 1|0 == 0
+ armed = self.restrictions[0].match(*vals)
+ for rest in islice(self.restrictions, 1, len(self.restrictions)):
+ if armed != rest.match(vals):
+ return False
+ return True
+ # 0|1 == 1|0 == 1, 0|0 == 1|1 == 0
+ armed = False
+ for rest in self.restrictions:
+ if armed == rest.match(vals):
+ if armed:
+ return False
+ else:
+ if not armed:
+ armed = True
+ if armed:
+ return True
+ return False
+
+ def force_True(self, pkg, *vals):
+ pvals = [pkg]
+ pvals.extend(vals)
+ entry_point = pkg.changes_count()
+ truths = [r.match(*pvals) for r in self.restrictions]
+ count = truths.count(True)
+ # get the simple one out of the way first.
+ l = len(truths)
+ if self.negate:
+ f = lambda r: r.force_False(*pvals)
+ t = lambda r: r.force_True(*pvals)
+ if count > l/2: order = ((t, count, True), (f, l - count, False))
+ else: order = ((f, l - count, False), (t, count, True))
+ for action, current, desired in order:
+ if current == l:
+ yield True
+ continue
+ for x, r in enumerate(self.restrictions):
+ if truths[x] != desired:
+ if action(r):
+ current += 1
+ else:
+ break
+ if current == l:
+ yield True
+ pkg.rollback(entry_point)
+ return
+
+ stack = []
+ for x, val in enumerate(truths):
+ falses = filter(None, val)
+ if truths[x]:
+ falses.remove(x)
+ stack.append((falses, None))
+ else:
+ stack.append((falses, x))
+
+ if count == 1:
+ yield True
+ del stack[truths.index(True)]
+
+ for falses, truths in stack:
+ failed = False
+ for x in falses:
+ if not self.restrictions[x].force_False(*pvals):
+ failed = True
+ break
+ if not failed:
+ if trues != None:
+ if self.restrictions[x].force_True(*pvals):
+ yield True
+ else:
+ yield True
+ pkg.rollback(entry_point)
+
+
+ def force_False(self, pkg, *vals):
+ pvals = [pkg]
+ pvals.extend(vals)
+ entry_point = pkg.changes_count()
+ truths = [r.match(*pvals) for r in self.restrictions]
+ count = truths.count(True)
+ # get the simple one out of the way first.
+ l = len(truths)
+ if not self.negate:
+ f = lambda r: r.force_False(*pvals)
+ t = lambda r: r.force_True(*pvals)
+ if count > l/2: order = ((t, count, True), (f, l - count, False))
+ else: order = ((f, l - count, False), (t, count, True))
+ for action, current, desired in order:
+ if current == l:
+ yield True
+ continue
+ for x, r in enumerate(self.restrictions):
+ if truths[x] != desired:
+ if action(r):
+ current += 1
+ else:
+ break
+ if current == l:
+ yield True
+ pkg.rollback(entry_point)
+ return
+ # the fun one.
+ stack = []
+ for x, val in enumerate(truths):
+ falses = filter(None, val)
+ if truths[x]:
+ falses.remove(x)
+ stack.append((falses, None))
+ else:
+ stack.append((falses, x))
+
+ if count == 1:
+ yield True
+ del stack[truths.index(True)]
+
+ for falses, truths in stack:
+ failed = False
+ for x in falses:
+ if not self.restrictions[x].force_False(*pvals):
+ failed = True
+ break
+ if not failed:
+ if trues != None:
+ if self.restrictions[x].force_True(*pvals):
+ yield True
+ else:
+ yield True
+ pkg.rollback(entry_point)
+
+
+ def __str__(self):
+ if self.negate: return "not ( %s )" % " ^^ ".join(imap(str, self.restrictions))
+ return "( %s )" % " ^^ ".join(imap(str, self.restrictions))
+
+