portage.dep: Rewrite paren_reduce and add tests
authorSebastian Luther <SebastianLuther@gmx.de>
Tue, 10 Aug 2010 06:41:10 +0000 (08:41 +0200)
committerZac Medico <zmedico@gentoo.org>
Tue, 10 Aug 2010 07:02:11 +0000 (00:02 -0700)
pym/portage/dep/__init__.py
pym/portage/tests/dep/test_paren_reduce.py [new file with mode: 0644]

index 542c17680b934c4e94ad2e37267d670715983116..19e347cfb581a10015466c2efc9a3884a04e4290 100644 (file)
@@ -81,72 +81,61 @@ def strip_empty(myarr):
        """
        return [x for x in myarr if x]
 
-_paren_whitespace_re = re.compile(r'\S(\(|\))|(\(|\))\S')
-
-def paren_reduce(mystr,tokenize=1):
+def paren_reduce(mystr):
        """
-       Take a string and convert all paren enclosed entities into sublists, optionally
-       futher splitting the list elements by spaces.
+       Take a string and convert all paren enclosed entities into sublists and
+       split the list elements by spaces.
 
        Example usage:
-               >>> paren_reduce('foobar foo ( bar baz )',1)
+               >>> paren_reduce('foobar foo ( bar baz )')
                ['foobar', 'foo', ['bar', 'baz']]
-               >>> paren_reduce('foobar foo ( bar baz )',0)
-               ['foobar foo ', [' bar baz ']]
 
        @param mystr: The string to reduce
        @type mystr: String
-       @param tokenize: Split on spaces to produces further list breakdown
-       @type tokenize: Integer
        @rtype: Array
        @return: The reduced string in an array
        """
-       global _dep_check_strict, _paren_whitespace_re
-       if _dep_check_strict:
-               m = _paren_whitespace_re.search(mystr)
-               if m is not None:
-                       raise portage.exception.InvalidDependString(
-                               _("missing space by parenthesis: '%s'") % m.group(0))
-       mylist = []
-       while mystr:
-               left_paren = mystr.find("(")
-               has_left_paren = left_paren != -1
-               right_paren = mystr.find(")")
-               has_right_paren = right_paren != -1
-               if not has_left_paren and not has_right_paren:
-                       freesec = mystr
-                       subsec = None
-                       tail = ""
-               elif mystr[0] == ")":
-                       return [mylist,mystr[1:]]
-               elif has_left_paren and not has_right_paren:
-                       raise portage.exception.InvalidDependString(
-                               _("missing right parenthesis: '%s'") % mystr)
-               elif has_left_paren and left_paren < right_paren:
-                       freesec,subsec = mystr.split("(",1)
-                       sublist = paren_reduce(subsec, tokenize=tokenize)
-                       if len(sublist) != 2:
+       mysplit = mystr.split()
+       level = 0
+       stack = [[]]
+       need_bracket = False
+       
+       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)
-                       subsec, tail = sublist
-               else:
-                       subsec,tail = mystr.split(")",1)
-                       if tokenize:
-                               subsec = strip_empty(subsec.split(" "))
-                               return [mylist+subsec,tail]
-                       return mylist+[subsec],tail
-               if not isinstance(tail, basestring):
-                       raise portage.exception.InvalidDependString(
-                               _("malformed syntax: '%s'") % mystr)
-               mystr = tail
-               if freesec:
-                       if tokenize:
-                               mylist = mylist + strip_empty(freesec.split(" "))
+                       if level > 0:
+                               level -= 1
+                               stack[level].append(stack.pop())
                        else:
-                               mylist = mylist + [freesec]
-               if subsec is not None:
-                       mylist = mylist + [subsec]
-       return mylist
+                               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)
+
+       if level != 0 or need_bracket:
+               raise portage.exception.InvalidDependString(
+                       _("malformed syntax: '%s'") % mystr)
+       
+       return stack[0]
 
 class paren_normalize(list):
        """Take a dependency structure as returned by paren_reduce or use_reduce
diff --git a/pym/portage/tests/dep/test_paren_reduce.py b/pym/portage/tests/dep/test_paren_reduce.py
new file mode 100644 (file)
index 0000000..3277ab5
--- /dev/null
@@ -0,0 +1,46 @@
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from portage.tests import TestCase
+from portage.dep import paren_reduce
+from portage.exception import InvalidDependString
+
+class TestParenReduce(TestCase):
+
+       def testParenReduce(self):
+
+               test_cases = (
+                       ( "A", ["A"]),
+                       ( "( A )", [["A"]]),
+                       ( "|| ( A B )", [ "||", ["A", "B"] ]),
+                       ( "|| ( A || ( B C ) )", [ "||", ["A", "||", ["B", "C"]]]),
+                       ( "|| ( A || ( B C D ) )", [ "||", ["A", "||", ["B", "C", "D"]] ]),
+                       ( "|| ( A || ( B || ( C D ) E ) )", [ "||", ["A", "||", ["B", "||", ["C", "D"], "E"]] ]),
+                       ( "a? ( A )", ["a?", ["A"]]),
+               )
+               
+               test_cases_xfail = (
+                       "( A",
+                       "A )",
+
+                       "||( A B )",
+                       "|| (A B )",
+                       "|| ( A B)",
+                       "|| ( A B",
+                       "|| A B )",
+
+                       "|| A B",
+                       "|| ( A B ) )",
+                       "|| || B C",
+                       
+                       "|| ( A B || )",
+                       
+                       "a? A",
+               )
+
+               for dep_str, expected_result in test_cases:
+                       self.assertEqual(paren_reduce(dep_str), expected_result)
+
+               for dep_str in test_cases_xfail:
+                       self.assertRaisesMsg(dep_str,
+                               InvalidDependString, paren_reduce, dep_str)