From c9ed39f98c62760333c9fe4d4ef5b8caa06a9e16 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 3 Feb 2011 15:29:50 -0800 Subject: REQUIRED_USE: display unsatisfied part This will fix bug #353234. --- pym/_emerge/depgraph.py | 15 ++++- pym/portage/dep/__init__.py | 92 +++++++++++++++++++++++++-- pym/portage/tests/dep/testCheckRequiredUse.py | 37 ++++++++++- 3 files changed, 136 insertions(+), 8 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 8f04c44f2..811eda68e 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -2593,9 +2593,22 @@ class depgraph(object): noiselevel=-1) writemsg_stdout("\n The following REQUIRED_USE flag constraints " + \ "are unsatisfied:\n", noiselevel=-1) + reduced_noise = check_required_use( + pkg.metadata["REQUIRED_USE"], + self._pkg_use_enabled(pkg), + pkg.iuse.is_valid_flag).tounicode() writemsg_stdout(" %s\n" % \ - human_readable_required_use(pkg.metadata["REQUIRED_USE"]), + human_readable_required_use(reduced_noise), noiselevel=-1) + normalized_required_use = \ + " ".join(pkg.metadata["REQUIRED_USE"].split()) + if reduced_noise != normalized_required_use: + writemsg_stdout("\n The above constraints " + \ + "are a subset of the following complete expression:\n", + noiselevel=-1) + writemsg_stdout(" %s\n" % \ + human_readable_required_use(normalized_required_use), + noiselevel=-1) writemsg_stdout("\n", noiselevel=-1) elif show_missing_use: diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py index ad68815a5..7e9a18ad3 100644 --- a/pym/portage/dep/__init__.py +++ b/pym/portage/dep/__init__.py @@ -2062,6 +2062,63 @@ def get_required_use_flags(required_use): return frozenset(used_flags) +class _RequiredUseLeaf(object): + + __slots__ = ('_satisfied', '_token') + + def __init__(self, token, satisfied): + self._token = token + self._satisfied = satisfied + + def tounicode(self): + return self._token + +class _RequiredUseBranch(object): + + __slots__ = ('_children', '_operator', '_parent', '_satisfied') + + def __init__(self, operator=None, parent=None): + self._children = [] + self._operator = operator + self._parent = parent + self._satisfied = False + + def __bool__(self): + return self._satisfied + + def tounicode(self): + + tokens = [] + if self._operator is not None: + tokens.append(self._operator) + + if self._parent is not None: + tokens.append("(") + + complex_nesting = False + node = self + while node != None and not complex_nesting: + if node._operator in ("||", "^^"): + complex_nesting = True + else: + node = node._parent + + if complex_nesting: + for child in self._children: + tokens.append(child.tounicode()) + else: + for child in self._children: + if not child._satisfied: + tokens.append(child.tounicode()) + + if self._parent is not None: + tokens.append(")") + + return " ".join(tokens) + + if sys.hexversion < 0x3000000: + __nonzero__ = __bool__ + def check_required_use(required_use, use, iuse_match): """ Checks if the use flags listed in 'use' satisfy all @@ -2108,10 +2165,17 @@ def check_required_use(required_use, use, iuse_match): mysplit = required_use.split() level = 0 stack = [[]] + tree = _RequiredUseBranch() + node = tree need_bracket = False for token in mysplit: if token == "(": + if not need_bracket: + child = _RequiredUseBranch(parent=node) + node._children.append(child) + node = child + need_bracket = False stack.append([]) level += 1 @@ -2127,18 +2191,27 @@ def check_required_use(required_use, use, iuse_match): if stack[level][-1] in ("||", "^^"): ignore = True op = stack[level].pop() - stack[level].append(is_satisfied(op, l)) + satisfied = is_satisfied(op, l) + stack[level].append(satisfied) + node._satisfied = satisfied elif not isinstance(stack[level][-1], bool) and \ stack[level][-1][-1] == "?": if is_active(stack[level][-1][:-1]): op = stack[level].pop() - stack[level].append(is_satisfied(op, l)) + satisfied = is_satisfied(op, l) + stack[level].append(satisfied) + node._satisfied = satisfied else: stack[level].pop() + node._satisfied = True ignore = True if l and not ignore: - stack[level].append(all(x for x in l)) + satisfied = False not in l + stack[level].append(satisfied) + node._satisfied = satisfied + + node = node._parent else: raise InvalidDependString( _("malformed syntax: '%s'") % required_use) @@ -2148,6 +2221,9 @@ def check_required_use(required_use, use, iuse_match): _("malformed syntax: '%s'") % required_use) need_bracket = True stack[level].append(token) + child = _RequiredUseBranch(operator=token, parent=node) + node._children.append(child) + node = child else: if need_bracket or "(" in token or ")" in token or \ "|" in token or "^" in token: @@ -2157,14 +2233,20 @@ def check_required_use(required_use, use, iuse_match): if token[-1] == "?": need_bracket = True stack[level].append(token) + child = _RequiredUseBranch(operator=token, parent=node) + node._children.append(child) + node = child else: - stack[level].append(is_active(token)) + satisfied = is_active(token) + stack[level].append(satisfied) + node._children.append(_RequiredUseLeaf(token, satisfied)) if level != 0 or need_bracket: raise InvalidDependString( _("malformed syntax: '%s'") % required_use) - return (False not in stack[0]) + tree._satisfied = False not in stack[0] + return tree def extract_affecting_use(mystr, atom): """ diff --git a/pym/portage/tests/dep/testCheckRequiredUse.py b/pym/portage/tests/dep/testCheckRequiredUse.py index 4b67d6250..0f7a299e9 100644 --- a/pym/portage/tests/dep/testCheckRequiredUse.py +++ b/pym/portage/tests/dep/testCheckRequiredUse.py @@ -1,4 +1,4 @@ -# Copyright 2010 Gentoo Foundation +# Copyright 2010-2011 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from portage.tests import TestCase @@ -103,9 +103,42 @@ class TestCheckRequiredUse(TestCase): ) for required_use, use, iuse, expected in test_cases: - self.assertEqual(check_required_use(required_use, use, iuse.__contains__), \ + self.assertEqual(bool(check_required_use(required_use, use, iuse.__contains__)), \ expected, required_use + ", USE = " + " ".join(use)) for required_use, use, iuse in test_cases_xfail: self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \ InvalidDependString, check_required_use, required_use, use, iuse.__contains__) + + def testCheckRequiredUseFilterSatisfied(self): + """ + Test filtering of satisfied parts of REQUIRED_USE, + in order to reduce noise for bug #353234. + """ + test_cases = ( + ( + "bindist? ( !amr !faac !win32codecs ) cdio? ( !cdparanoia !cddb ) dvdnav? ( dvd )", + ("cdio", "cdparanoia"), + "cdio? ( !cdparanoia )" + ), + ( + "|| ( !amr !faac !win32codecs ) cdio? ( !cdparanoia !cddb ) ^^ ( foo bar )", + ["cdio", "cdparanoia", "foo"], + "cdio? ( !cdparanoia )" + ), + ( + "^^ ( || ( a b ) c )", + ("a", "b", "c"), + "^^ ( || ( a b ) c )" + ), + ( + "^^ ( || ( ( a b ) ) ( c ) )", + ("a", "b", "c"), + "^^ ( || ( ( a b ) ) ( c ) )" + ) + ) + for required_use, use, expected in test_cases: + result = check_required_use(required_use, use, lambda k: True).tounicode() + self.assertEqual(result, expected, + "REQUIRED_USE = '%s', USE = '%s', '%s' != '%s'" % \ + (required_use, " ".join(use), result, expected)) -- cgit v1.2.3-1-g7c22