From 8e7a2082f726b64f3742266148b89e7cb4101ee4 Mon Sep 17 00:00:00 2001 From: Sebastian Luther Date: Tue, 10 Aug 2010 08:41:10 +0200 Subject: portage.dep: Rewrite paren_reduce and add tests --- pym/portage/dep/__init__.py | 95 +++++++++++++----------------- pym/portage/tests/dep/test_paren_reduce.py | 46 +++++++++++++++ 2 files changed, 88 insertions(+), 53 deletions(-) create mode 100644 pym/portage/tests/dep/test_paren_reduce.py diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py index 542c17680..19e347cfb 100644 --- a/pym/portage/dep/__init__.py +++ b/pym/portage/dep/__init__.py @@ -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 index 000000000..3277ab58b --- /dev/null +++ b/pym/portage/tests/dep/test_paren_reduce.py @@ -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) -- cgit v1.2.3-1-g7c22