diff options
author | Brian Harring <ferringb@gentoo.org> | 2005-08-16 00:19:57 +0000 |
---|---|---|
committer | Brian Harring <ferringb@gentoo.org> | 2005-08-16 00:19:57 +0000 |
commit | 4247fb899526f02f6382d9c8ae8bd691b9b1b861 (patch) | |
tree | e6f0eb159db71349ea5c4ce7d99bb82d4a7a1c87 | |
parent | adjusted filename on tercel's request (diff) | |
download | portage-cvs-4247fb899526f02f6382d9c8ae8bd691b9b1b861.tar.gz portage-cvs-4247fb899526f02f6382d9c8ae8bd691b9b1b861.tar.bz2 portage-cvs-4247fb899526f02f6382d9c8ae8bd691b9b1b861.zip |
restriction subsystem slight redesign.
bugs probably exist in force_(Fail,True), although containmentmatch appears to work (use deps are working now)
packages is package level restrictions
values is value level restrictions; package restrictions gank the attributes, and hand them off to value restrictions (effectively)
collapsed is package level.
boolean is a base of boolean logic classes, properly support the full potential combinations NAND and OR have (that sucked).
same with ContainmentMatch
-rw-r--r-- | portage/restrictions/boolean.py | 411 | ||||
-rw-r--r-- | portage/restrictions/packages.py | 140 | ||||
-rw-r--r-- | portage/restrictions/restrictionSet.py | 167 | ||||
-rw-r--r-- | portage/restrictions/values.py | 286 |
4 files changed, 837 insertions, 167 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)) + + diff --git a/portage/restrictions/packages.py b/portage/restrictions/packages.py new file mode 100644 index 0000000..ae44150 --- /dev/null +++ b/portage/restrictions/packages.py @@ -0,0 +1,140 @@ +# 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/packages.py,v 1.1 2005/08/16 00:19:57 ferringb Exp $ + +import restriction +import boolean +from portage.util.lists import unique +import values + +class base(restriction.base): + package_matching = True + +class PackageRestriction(base): + """cpv data restriction. Inherit for anything that's more then cpv mangling please""" + + __slots__ = tuple(["attr_split", "attr", "restriction"] + base.__slots__) + + def __init__(self, attr, restriction, **kwds): + super(PackageRestriction, self).__init__(**kwds) + self.attr_split = attr.split(".") + self.attr = attr + if not isinstance(restriction, values.base): + raise TypeError("restriction must be of a restriction type") + self.restriction = restriction + + def __pull_attr(self, pkg): + try: + o = pkg + for x in self.attr_split: + o = getattr(o, x) + return o + except AttributeError,ae: + logging.debug("failed getting attribute %s from %s, exception %s" % \ + (self.attr, str(pkg), str(ae))) + raise + + def match(self, pkg): + try: + return self.restriction.match(self.__pull_attr(pkg)) ^ self.negate + except AttributeError: + return self.negate + + + def force_False(self, pkg): +# import pdb;pdb.set_trace() + if self.negate: + i = self.restriction.force_True(pkg, self.attr, self.__pull_attr(pkg)) + else: + i = self.restriction.force_False(pkg, self.attr, self.__pull_attr(pkg)) + if isinstance(i, bool): + yield i + else: + for x in i: + yield True + return + + def force_True(self, pkg): +# import pdb;pdb.set_trace() + if self.negate: + i = self.restriction.force_False(pkg, self.attr, self.__pull_attr(pkg)) + else: + i = self.restriction.force_True(pkg, self.attr, self.__pull_attr(pkg)) + if isinstance(i, bool): + yield i + else: + for x in i: + yield True + return + + + def __getitem__(self, key): + if not isinstance(self.restriction, boolean.base): + if key != 0: + raise IndexError("restriction isn't indexable") + else: + return self + try: + g = self.restriction[key] + except TypeError: + if key == 0: + return self.restriction + raise IndexError("index out of range") + + def __len__(self): + if not isinstance(self.restriction, boolean.base): + return 1 + return len(self.restriction) + 1 + + def intersect(self, other): + if self.negate != other.negate or self.attr != other.attr: + return None + if isinstance(self.restriction, other.restriction.__class__): + s = self.restriction.intersect(other.restriction) + elif isinstance(other.restriction, self.restriction.__class__): + s = other.restriction.intersect(self.restriction) + else: return None + if s == None: + return None + if s == self.restriction: return self + elif s == other.restriction: return other + + # this can probably bite us in the ass self or other is a derivative, and the other isn't. + return self.__class__(self.attr, s) + + def __eq__(self, other): + return self.negate == self.negate and self.attr == other.attr and self.restriction == other.restriction + + def __str__(self): + s = self.attr + if self.negate: self.attr += "not " + return s + str(self.restriction) + + +class AndRestriction(base, boolean.AndRestriction): + __slots__ = tuple(unique(list(boolean.AndRestriction.__slots__) + base.__slots__)) + required_base = base + + +class OrRestriction(base, boolean.OrRestriction): + __slots__ = tuple(unique(list(boolean.OrRestriction.__slots__) + base.__slots__)) + required_base = base + +class XorRestriction(base, boolean.XorRestriction): + __slots__ = tuple(unique(list(boolean.XorRestriction.__slots__) + base.__slots__)) + required_base = base + +class AlwaysBoolRestriction(restriction.AlwaysBoolMatch, base): + __slots__=("negate") +def __init__(self, val): + self.negate = val +def match(self, *a): + return self.negate +def force_False(self, *a): + return self.negate == False +def force_True(self, *a): + return self.negate == True + +AlwaysTrue = AlwaysBoolRestriction(True) +AlwaysFalse = AlwaysBoolRestriction(True) diff --git a/portage/restrictions/restrictionSet.py b/portage/restrictions/restrictionSet.py deleted file mode 100644 index 67288d1..0000000 --- a/portage/restrictions/restrictionSet.py +++ /dev/null @@ -1,167 +0,0 @@ -# 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/Attic/restrictionSet.py,v 1.9 2005/08/09 07:51:09 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. -""" - -import restriction -from itertools import imap -__all__ = ("AndRestrictionSet", "OrRestrictionSet", "XorRestrictionSet") - -class RestrictionSet(restriction.base): - __slots__ = tuple(["restrictions"] + restriction.base.__slots__) - - 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(RestrictionSet, 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, **kwds): - """add restriction(s) - strict=True, set to false to disable isinstance checks to ensure all restrictions are restriction.base derivatives - """ - if len(new_restrictions) == 0: - raise TypeError("need at least one restriction handed in") - if kwds.get("strict", True): - for r in new_restrictions: - if not isinstance(r, restriction.base): - raise TypeError("instance '%s' isn't a restriction.base, and strict is on" % r) - - 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 __getitem__(self, key): - return self.restrictions[key] - -def unwind_changes(pkg, pop_count, negate): - while pop_count: - pkg.pop_change() - pop_count-=1 - if negate: - return pkg - return None - -class AndRestrictionSet(RestrictionSet): - """Boolean AND grouping of restrictions.""" - __slots__ = tuple(RestrictionSet.__slots__) - - def match(self, packagedataInstance): - for rest in self.restrictions: - if not rest.match(packagedataInstance): - return self.negate - return not self.negate - - def cmatch(self, pkg): - entry_point = pkg.changes_count() - for rest in self.restrictions: - if c.match(pkg) == False: - pkg.rollback_changes(entry_point) - if self.negate: return pkg - return self.negate - - # for this to be reached, things went well. - if self.negate: - pkg.rollback_changes(entry_point) - # yes, normally it's "not negate", but we no negates status already via the if - return False - return True - - def __str__(self): - if self.negate: return "not ( %s )" % " && ".join(imap(str, self.restrictions)) - return "( %s )" % " && ".join(imap(str, self.restrictions)) - - -class OrRestrictionSet(RestrictionSet): - """Boolean OR grouping of restrictions.""" - __slots__ = tuple(RestrictionSet.__slots__) - - def match(self, packagedataInstance): - for rest in self.restrictions: - if rest.match(packagedataInstance): - return not self.negate - return self.negate - - def cmatch(self, pkg): - entry_point = pkg.changes_count() - for rest in self.restrictions: - if rest.cmatch(pkg) == True: - if self.negate: - pkg.rollback_changes(entry_point) - return not self.negate - else: - pkg.rollback_changes(entry_point) - - if self.negate: - pkg.rollback_changes(entry_point) - return self.negate - - def __str__(self): - if self.negate: return "not ( %s )" % " || ".join(imap(str, self.restrictions)) - return "( %s )" % " || ".join(imap(str, self.restrictions)) - - -class XorRestrictionSet(RestrictionSet): - """Boolean XOR grouping of restrictions.""" - __slots__ = tuple(RestrictionSet.__slots__) - - def match(self, pkginst): - armed = False - for rest in self.restrictions: - if rest.match(pkginst): - if armed: - return self.negate - armed = True - return armed ^ self.negate - - def cmatch(self, pkg): - entry_point = None - armed = False - for ret in self.restrictions: - node_entry_point = pkg.changes_count() - if rest.cmatch(pkg): - if armed: - pkg.rollback_changes(entry_point) - return self.negate - armed = True - else: - pkg.rollback_changes(node_entry_point) - - if self.negate and entry_point != None: - pkg.rollback_changes(entry_point) - return armed ^ self.negate - - def __str__(self): - if self.negate: return "not ( %s )" % " ^^ ".join(imap(str, self.restrictions)) - return "( %s )" % " ^^ ".join(imap(str, self.restrictions)) - - -bases = (AndRestrictionSet, OrRestrictionSet, XorRestrictionSet) diff --git a/portage/restrictions/values.py b/portage/restrictions/values.py new file mode 100644 index 0000000..8e96442 --- /dev/null +++ b/portage/restrictions/values.py @@ -0,0 +1,286 @@ +# 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/values.py,v 1.1 2005/08/16 00:19:57 ferringb Exp $ + +import re, logging +import restriction +import boolean + +class base(restriction.base): + """base restriction matching object; overrides setattr to provide the usual write once trickery + all derivatives *must* be __slot__ based""" + + __slots__ = restriction.base.__slots__ + + def force_True(self, pkg, attr, val): + if self.match(val) ^ self.negate: return True + elif self.negate: return pkg.request_disable(attr, val) + else: return pkg.request_enable(attr, val) + + def force_False(self, pkg, attr, val): + if self.match(val) ^ self.negate: return True + elif self.negate: return pkg.request_enable(attr, val) + else: return pkg.request_disable(attr, val) + + +class VersionRestriction(base): + """use this as base for version restrictions, gives a clue to what the restriction does""" + pass + + +class StrMatch(base): + """ Base string matching restriction. all derivatives must be __slot__ based classes""" + __slots__ = ["flags"] + base.__slots__ + pass + + +class StrRegexMatch(StrMatch): + #potentially redesign this to jit the compiled_re object + __slots__ = tuple(["regex", "compiled_re"] + StrMatch.__slots__) + + def __init__(self, regex, CaseSensitive=True, **kwds): + super(StrRegexMatch, self).__init__(**kwds) + self.regex = regex + flags = 0 + if not CaseSensitive: + flags = re.I + self.flags = flags + self.compiled_re = re.compile(regex, flags) + + def match(self, value): + return (self.compiled_re.match(str(value)) != None) ^ self.negate + + def intersect(self, other): + if self.regex == other.regex and self.negate == other.negate and self.flags == other.flags: + return self + return None + + def __eq__(self, other): + return self.regex == other.regex and self.negate == other.negate and self.flags == other.flags + + def __str__(self): + if self.negate: return "not like %s" % self.regex + return "like %s" % self.regex + + +class StrExactMatch(StrMatch): + __slots__ = tuple(["exact", "flags"] + StrMatch.__slots__) + + def __init__(self, exact, CaseSensitive=True, **kwds): + super(StrExactMatch, self).__init__(**kwds) + if not CaseSensitive: + self.flags = re.I + self.exact = str(exact).lower() + else: + self.flags = 0 + self.exact = str(exact) + + def match(self, value): + if self.flags & re.I: return (self.exact == str(value).lower()) ^ self.negate + else: return (self.exact == str(value)) ^ self.negate + + def intersect(self, other): + s1, s2 = self.exact, other.exact + if other.flags and not self.flags: + s1 = s1.lower() + elif self.flags and not other.flags: + s2 = s2.lower() + if s1 == s2 and self.negate == other.negate: + if other.flags: + return other + return self + return None + + def __eq__(self, other): + return self.exact == other.exact and self.negate == other.negate and self.flags == other.flags + + def __str__(self): + if self.negate: return "!= "+self.exact + return "== "+self.exact + + +class StrGlobMatch(StrMatch): + __slots__ = tuple(["glob"] + StrMatch.__slots__) + def __init__(self, glob, CaseSensitive=True, **kwds): + super(StrGlobMatch, self).__init__(**kwds) + if not CaseSensitive: + self.flags = re.I + self.glob = str(glob).lower() + else: + self.flags = 0 + self.glob = str(glob) + + def match(self, value): + if isinstance(value, (list, tuple)): + for x in value: + print "trying %s against %s" % (x, self.glob) + if self.match(x): + return not self.negate + return self.negate + else: + value = str(value) + if self.flags & re.I: value = value.lower() + return value.startswith(self.glob) ^ self.negate + + def intersect(self, other): + if self.match(other.glob): + if self.negate == other.negate: + return other + elif other.match(self.glob): + if self.negate == other.negate: + return self + return None + + def __eq__(self, other): + return self.glob == other.glob and self.negate == other.negate and self.flags == other.flags + + def __str__(self): + if self.negate: return "not "+self.glob+"*" + return self.glob+"*" + + +class ContainmentMatch(base): + """used for an 'in' style operation, 'x86' in ['x86','~x86'] for example + note that negation of this *does* not result in a true NAND when all is on.""" + __slots__ = tuple(["vals", "vals_len", "all"] + base.__slots__) + + def __init__(self, *vals, **kwds): + """vals must support a contaiment test + if all is set to True, all vals must match""" + + if "all" in kwds: + self.all = kwds["all"] + del kwds["all"] + else: + self.all = False + super(ContainmentMatch, self).__init__(**kwds) + self.vals = set(vals) + self.vals_len = len(self.vals) + + def match(self, val): + if isinstance(val, (str, unicode)): + return val in self.vals ^ self.negate + rem = set(self.vals) + try: + # assume our lookup is faster, since we don't know if val is constant lookup or not + for x in val: + if x in rem: + if self.all: + rem.remove(x) + if len(rem) == 0: + return not self.negate + else: + return not self.negate + return self.negate + except TypeError: + return self.negate +# return val in self.vals ^ self.negate + + def force_False(self, pkg, attr, val): + if isinstance(val, (str, unicode)): + # unchangable + if self.all: + if len(self.vals) != 1: + yield False + else: + yield (self.vals[0] in val) ^ self.negate + else: + yield (val in self.vals) ^ self.negate + return + + entry = pkg.changes_count() + if self.negate: + if self.all: + def filter(truths): return False in truths + def true(r, pvals): return pkg.request_enable(attr, r) + def false(r, pvals): return pkg.request_disable(attr, r) + + truths = [x in val for x in self.vals] + + for x in boolean.iterative_quad_toggling(pkg, None, list(self.vals), 0, len(self.vals), truths, filter, + desired_false=false, desired_true=true): + yield True + else: + if pkg.request_disable(attr, *self.vals): + yield True + return + + if not self.all: + if pkg.request_disable(attr, *self.vals): + yield True + else: + l = len(self.vals) + def filter(truths): return truths.count(True) < l + def true(r, pvals): return pkg.request_enable(attr, r) + def false(r, pvals): return pkg.request_disable(attr, r) + truths=[x in val for x in self.vals] + for x in boolean.iterative_quad_toggling(pkg, None, list(self.vals), 0, l, truths, filter, + desired_false=false, desired_true=true): + yield True + + return + + + def force_True(self, pkg, attr, val): + import pdb;pdb.set_trace() + if isinstance(val, (str, unicode)): + # unchangable + if self.all: + if len(self.vals) != 1: + yield False + else: + yield (self.vals[0] in val) ^ self.negate + else: + yield (val in self.vals) ^ self.negate + return + + entry = pkg.changes_count() + if not self.negate: + if not self.all: + def filter(truths): return True in truths + def true(r, pvals): return pkg.request_enable(attr, r) + def false(r, pvals): return pkg.request_disable(attr, r) + + truths = [x in val for x in self.vals] + + for x in boolean.iterative_quad_toggling(pkg, None, list(self.vals), 0, len(self.vals), truths, filter, + desired_false=false, desired_true=true): + yield True + else: + if pkg.request_enable(attr, *self.vals): + yield True + return + + # negation + if not self.all: + if pkg.request_disable(attr, *self.vals): + yield True + else: + def filter(truths): return True not in truths + def true(r, pvals): return pkg.request_enable(attr, r) + def false(r, pvals): return pkg.request_disable(attr, r) + truths=[x in val for x in self.vals] + for x in boolean.iterative_quad_toggling(pkg, None, list(self.vals), 0, len(self.vals), truths, filter, + desired_false=false, desired_true=true): + yield True + return + + + def __str__(self): + if self.negate: s="not contains [%s]" + else: s="contains [%s]" + return s % ', '.join(map(str, self.vals)) + +def get_val(pkg, attr): + attr_list = '.'.split(attr) + o=pkg + try: + for x in attr: + o=getattr(o, x) + return x + except AttributeError, ae: + logger.warn("impossible happened, unable to get attr '%s' from pkg '%s', yet it was handed into my parent" + % (attr, pkg)) + raise + |