Add portage.dep.extract_affecting_use()
authorSebastian Luther <SebastianLuther@gmx.de>
Thu, 12 Aug 2010 08:59:33 +0000 (10:59 +0200)
committerZac Medico <zmedico@gentoo.org>
Thu, 12 Aug 2010 10:40:45 +0000 (03:40 -0700)
pym/portage/dep/__init__.py
pym/portage/tests/dep/testExtractAffectingUSE.py [new file with mode: 0644]

index 1b89afec06d6b29a500e2b5ea4d7d68b5a03f447..f15b8a2d95abdb7ebed487f739b5d825264e3c0b 100644 (file)
@@ -1458,3 +1458,106 @@ def check_required_use(required_use, use, iuse):
                        _("malformed syntax: '%s'") % required_use)
 
        return (False not in stack[0])
+
+def extract_affecting_use(mystr, atom):
+       """
+       Take a dep string and an atom and return the use flags
+       that decide if the given atom is in effect.
+
+       Example usage:
+               >>> extract_use_cond('sasl? ( dev-libs/cyrus-sasl ) \
+                       !minimal? ( cxx? ( dev-libs/cyrus-sasl ) )', 'dev-libs/cyrus-sasl')
+               (['sasl', 'minimal', 'cxx'])
+
+       @param dep: The dependency string
+       @type mystr: String
+       @param atom: The atom to get into effect
+       @type atom: String
+       @rtype: Tuple of two lists of strings
+       @return: List of use flags that need to be enabled, List of use flag that need to be disabled
+       """
+       mysplit = mystr.split()
+       level = 0
+       stack = [[]]
+       need_bracket = False
+       affecting_use = set()
+       atom_seen = False
+
+       def flag(conditional):
+               if conditional[0] == "!":
+                       flag = conditional[1:-1]
+               else:
+                       flag = conditional[:-1]
+
+               if not flag:
+                       raise portage.exception.InvalidDependString(
+                               _("malformed syntax: '%s'") % mystr)
+
+               return flag
+
+       for token in mysplit:
+               if token == "(":
+                       need_bracket = False
+                       stack.append([])
+                       level += 1
+               elif token == ")":
+                       if need_bracket:
+                               raise portage.exception.InvalidDependString(
+                                       _("malformed syntax: '%s'") % mystr)
+                       if level > 0:
+                               level -= 1
+                               l = stack.pop()
+
+                               if l:
+                                       if not stack[level] or (stack[level][-1] != "||" and not stack[level][-1][-1] == "?"):
+                                               #Optimize: ( ( ... ) ) -> ( ... )
+                                               stack[level].extend(l)
+                                       elif len(l) == 1 and stack[level][-1] == "||":
+                                               #Optimize: || ( A ) -> A
+                                               stack[level].pop()
+                                               stack[level].extend(l)
+                                       elif len(l) == 2 and (l[0] == "||" or l[0][-1] == "?") and stack[level][-1] in (l[0], "||"):
+                                               #Optimize:      || ( || ( ... ) ) -> || ( ... )
+                                               #                       foo? ( foo? ( ... ) ) -> foo? ( ... )
+                                               #                       || ( foo? ( ... ) ) -> foo? ( ... )
+                                               stack[level].pop()
+                                               stack[level].extend(l)
+                                               if l[0][-1] == "?":
+                                                       affecting_use.add(flag(l[0]))
+                                       else:
+                                               if stack[level] and stack[level][-1][-1] == "?":
+                                                       affecting_use.add(flag(stack[level][-1]))
+                                               stack[level].append(l)
+                               else:
+                                       if stack[level] and (stack[level][-1] == "||" or stack[level][-1][-1] == "?"):
+                                               stack[level].pop()
+                       else:
+                               raise portage.exception.InvalidDependString(
+                                       _("malformed syntax: '%s'") % mystr)
+               elif token == "||":
+                       if need_bracket:
+                               raise portage.exception.InvalidDependString(
+                                       _("malformed syntax: '%s'") % mystr)
+                       need_bracket = True
+                       stack[level].append(token)
+               else:
+                       if need_bracket or "(" in token or ")" in token or "|" in token:
+                               raise portage.exception.InvalidDependString(
+                                       _("malformed syntax: '%s'") % mystr)
+
+                       if token[-1] == "?":
+                               need_bracket = True
+                               stack[level].append(token)
+                       elif token == atom:
+                               atom_seen = True
+                               stack[level].append(token)
+
+       if level != 0 or need_bracket:
+               raise portage.exception.InvalidDependString(
+                       _("malformed syntax: '%s'") % mystr)
+
+       if not atom_seen:
+               raise portage.exception.IncorrectParameter(
+                       _("extract_affecting_use: atom '%s' not in dep string: '%s'") % (atom, mystr))
+
+       return affecting_use
diff --git a/pym/portage/tests/dep/testExtractAffectingUSE.py b/pym/portage/tests/dep/testExtractAffectingUSE.py
new file mode 100644 (file)
index 0000000..ba904b9
--- /dev/null
@@ -0,0 +1,70 @@
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from portage.tests import TestCase
+from portage.dep import extract_affecting_use
+from portage.exception import InvalidDependString, IncorrectParameter
+
+class TestExtractAffectingUSE(TestCase):
+
+       def testExtractAffectingUSE(self):
+               test_cases = (
+                       ("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "A", ("a",)),
+                       ("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "B", ("b",)),
+                       ("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "C", ("c",)),
+                       ("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "D", ("d",)),
+                       
+                       ("a? ( b? ( AB ) )", "AB", ("a", "b")),
+                       ("a? ( b? ( c? ( ABC ) ) )", "ABC", ("a", "b", "c")),
+
+                       ("a? ( A b? ( c? ( ABC ) AB ) )", "A", ("a",)),
+                       ("a? ( A b? ( c? ( ABC ) AB ) )", "AB", ("a", "b")),
+                       ("a? ( A b? ( c? ( ABC ) AB ) )", "ABC", ("a", "b", "c")),
+                       ("a? ( A b? ( c? ( ABC ) AB ) ) X", "X", []),
+                       ("X a? ( A b? ( c? ( ABC ) AB ) )", "X", []),
+
+                       ("ab? ( || ( A B ) )", "A", ("ab",)),
+                       ("!ab? ( || ( A B ) )", "B", ("ab",)),
+                       ("ab? ( || ( A || ( b? ( || ( B C ) ) ) ) )", "A", ("ab",)),
+                       ("ab? ( || ( A || ( b? ( || ( B C ) ) ) ) )", "B", ("ab", "b")),
+                       ("ab? ( || ( A || ( b? ( || ( B C ) ) ) ) )", "C", ("ab", "b")),
+
+                       ("( ab? ( || ( ( A ) || ( b? ( ( ( || ( B ( C ) ) ) ) ) ) ) ) )", "A", ("ab",)),
+                       ("( ab? ( || ( ( A ) || ( b? ( ( ( || ( B ( C ) ) ) ) ) ) ) ) )", "B", ("ab", "b")),
+                       ("( ab? ( || ( ( A ) || ( b? ( ( ( || ( B ( C ) ) ) ) ) ) ) ) )", "C", ("ab", "b")),
+               )
+
+               test_cases_xfail = (
+                       ("? ( A )", "A"),
+                       ("!? ( A )", "A"),
+                       ("( A", "A"),
+                       ("A )", "A"),
+                       
+                       ("||( A B )", "A"),
+                       ("|| (A B )", "A"),
+                       ("|| ( A B)", "A"),
+                       ("|| ( A B", "A"),
+                       ("|| A B )", "A"),
+                       ("|| A B", "A"),
+                       ("|| ( A B ) )", "A"),
+                       ("|| || B C", "A"),
+                       ("|| ( A B || )", "A"),
+                       ("a? A", "A"),
+                       ("( || ( || || ( A ) foo? ( B ) ) )", "A"),
+                       ("( || ( || bar? ( A ) foo? ( B ) ) )", "A"),
+
+                       ("a? ( A )", "B"),
+               )
+
+               for dep, atom, expected in test_cases:
+                       expected = set(expected)
+                       result = extract_affecting_use(dep, atom)
+                       fail_msg = "dep: " + dep + ", atom: " + atom + ", got: " + \
+                               " ".join(sorted(result)) + ", expected: " + " ".join(sorted(expected))
+                       self.assertEqual(result, expected, fail_msg)
+
+               for dep, atom in test_cases_xfail:
+                       fail_msg = "dep: " + dep + ", atom: " + atom + ", got: " + \
+                               " ".join(sorted(result)) + ", expected: " + " ".join(sorted(expected))
+                       self.assertRaisesMsg(fail_msg, \
+                               (InvalidDependString, IncorrectParameter), extract_affecting_use, dep, atom)