REQUIRED_USE: display unsatisfied part
authorZac Medico <zmedico@gentoo.org>
Thu, 3 Feb 2011 23:29:50 +0000 (15:29 -0800)
committerZac Medico <zmedico@gentoo.org>
Thu, 3 Feb 2011 23:43:28 +0000 (15:43 -0800)
This will fix bug #353234.

pym/_emerge/depgraph.py
pym/portage/dep/__init__.py
pym/portage/tests/dep/testCheckRequiredUse.py

index 05f150939a15099c3786b876c8a34159ddeba2bb..8b801f5cdd026aa159d08c9e0a3f4524d5484948 100644 (file)
@@ -2598,9 +2598,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:
index ad68815a5abd5a96754b6a34e17c3632507d9d7a..7e9a18ad3392896059a612d17523bfeb3b033971 100644 (file)
@@ -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):
        """
index 4b67d62500ede769c2d6d611afa3877cf12531ba..0f7a299e9015022461fe0e2698331dc8009b588b 100644 (file)
@@ -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))