From 1df3eec53768c882f8747af4bb706642c50891d0 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Thu, 12 Aug 2010 10:59:33 +0200 Subject: [PATCH] Add portage.dep.extract_affecting_use() --- pym/portage/dep/__init__.py | 103 ++++++++++++++++++ .../tests/dep/testExtractAffectingUSE.py | 70 ++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 pym/portage/tests/dep/testExtractAffectingUSE.py diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py index 1b89afec0..f15b8a2d9 100644 --- a/pym/portage/dep/__init__.py +++ b/pym/portage/dep/__init__.py @@ -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 index 000000000..ba904b927 --- /dev/null +++ b/pym/portage/tests/dep/testExtractAffectingUSE.py @@ -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) -- 2.26.2