From 70743ff9a8d97400858141e5a9ee535a811b6d91 Mon Sep 17 00:00:00 2001 From: kunzimariano Date: Fri, 1 Aug 2014 15:24:24 -0300 Subject: [PATCH] An alternative for pull request #15 -Added string_utils module with the function split_attribute. - Replaced the string split on dots for the new function. - Added tests for split_attribute. --- v1pysdk/query.py | 4 ++-- v1pysdk/string_utils.py | 23 ++++++++++++++++++++ v1pysdk/tests/string_utils_tests.py | 33 +++++++++++++++++++++++++++++ v1pysdk/v1meta.py | 10 +++++---- 4 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 v1pysdk/string_utils.py create mode 100644 v1pysdk/tests/string_utils_tests.py diff --git a/v1pysdk/query.py b/v1pysdk/query.py index 8381c22..6ea928a 100755 --- a/v1pysdk/query.py +++ b/v1pysdk/query.py @@ -1,5 +1,5 @@ - from urllib import urlencode +from string_utils import split_attribute class V1Query(object): """A fluent query object. Use .select() and .where() to add items to the @@ -72,7 +72,7 @@ def select(self, *args, **kw): without further network traffic""" for sel in args: - parts = sel.split('.') + parts = split_attribute(sel) for i in range(1, len(parts)): self.sel_list.append('.'.join(parts[:i])) self.sel_list.append(sel) diff --git a/v1pysdk/string_utils.py b/v1pysdk/string_utils.py new file mode 100644 index 0000000..06046a8 --- /dev/null +++ b/v1pysdk/string_utils.py @@ -0,0 +1,23 @@ +def split_attribute(input): + """ + properly split apart attribute strings, even when they have sub-attributes declated between []. + :param input:the attribute string to split + :return: dict containing the elements of the top level attribute string. + Sub-attribute strings between '[]'s are appended to their parent, without processing, even if they contain '.' + """ + ret = [] + # zero means we are not inside square brackets + squareBrackets = 0 + lastIndex = 0 + for i in range(len(input)): + if input[i] == '[': + squareBrackets +=1 + elif input[i] == ']': + squareBrackets -=1 + elif input[i] == '.' and squareBrackets == 0: + ret.append(input[lastIndex:i]) + lastIndex =i+1 + + #last element + ret.append(input[lastIndex:]) + return ret \ No newline at end of file diff --git a/v1pysdk/tests/string_utils_tests.py b/v1pysdk/tests/string_utils_tests.py new file mode 100644 index 0000000..0e8334d --- /dev/null +++ b/v1pysdk/tests/string_utils_tests.py @@ -0,0 +1,33 @@ +import unittest +from v1pysdk.string_utils import split_attribute + +class TestStringUtils(unittest.TestCase): + def test_split_attribute(self): + self.assertEquals(['[testing]]'],split_attribute('[testing]]')) + self.assertEquals(['[[testing]'],split_attribute('[[testing]')) + self.assertEquals(['testing','a','sentence','is','difficult'],split_attribute('testing.a.sentence.is.difficult')) + self.assertEquals(['testing','[a.sentence]','is','difficult'],split_attribute('testing.[a.sentence].is.difficult')) + self.assertEquals(['testing[.a.sentence]','is', 'difficult'],split_attribute('testing[.a.sentence].is.difficult')) + self.assertEquals(['testing','a[.sentence.]is','difficult'],split_attribute('testing.a[.sentence.]is.difficult')) + self.assertEquals(['testing','a','sentence','is','difficult]'],split_attribute('testing.a.sentence.is.difficult]')) + self.assertEquals(['testing', 'a','sentence','is',']difficult'],split_attribute('testing.a.sentence.is.]difficult')) + self.assertEquals(['[testing.a.sentence.is]','difficult'],split_attribute('[testing.a.sentence.is].difficult')) + self.assertEquals(['[testing.][a.sentence.is.difficult]'],split_attribute('[testing.][a.sentence.is.difficult]')) + self.assertEquals(['[testing]','[a]','[sentence]','[is]','[difficult]'], + split_attribute('[testing].[a].[sentence].[is].[difficult]')) + self.assertEquals(['testing','[[a.sentence.]is]','difficult'], + split_attribute('testing.[[a.sentence.]is].difficult')) + self.assertEquals(["History[Status.Name='Done']"],split_attribute("History[Status.Name='Done']")) + self.assertEquals(["ParentMeAndUp[Scope.Workitems.@Count='2']"], + split_attribute("ParentMeAndUp[Scope.Workitems.@Count='2']") ) + self.assertEquals(["Owners","OwnedWorkitems[ChildrenMeAndDown=$]","@DistinctCount"], + split_attribute("Owners.OwnedWorkitems[ChildrenMeAndDown=$].@DistinctCount") ) + self.assertEquals(["Workitems[ParentAndUp[Scope=$].@Count='1']"], + split_attribute("Workitems[ParentAndUp[Scope=$].@Count='1']") ) + self.assertEquals(["RegressionPlan","RegressionSuites[AssetState!='Dead']","TestSets[AssetState!='Dead']","Environment", "@DistinctCount"] + ,split_attribute("RegressionPlan.RegressionSuites[AssetState!='Dead'].TestSets[AssetState!='Dead'].Environment.@DistinctCount") ) + self.assertEquals(["Scope","ChildrenMeAndDown","Workitems:Story[ChildrenMeAndDown.ToDo.@Sum!='0.0']","Estimate","@Sum"] + ,split_attribute("Scope.ChildrenMeAndDown.Workitems:Story[ChildrenMeAndDown.ToDo.@Sum!='0.0'].Estimate.@Sum") ) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/v1pysdk/v1meta.py b/v1pysdk/v1meta.py index 118b173..ec27a0e 100755 --- a/v1pysdk/v1meta.py +++ b/v1pysdk/v1meta.py @@ -8,6 +8,7 @@ from cache_decorator import memoized from special_class_methods import special_classes from none_deref import NoneDeref +from string_utils import split_attribute class V1Meta(object): def __init__(self, *args, **kw): @@ -204,15 +205,16 @@ def add_attribute_to_output(self, output, relation, values): output[relation] = values[0] def is_attribute_qualified(self, relation): - return relation.find('.') >= 0 + parts = split_attribute(relation) + return len(parts) > 1 def split_relation_to_container_and_leaf(self, relation): - parts = relation.split('.') - return ('.'.join(relation.split('.')[:-1]), parts[-1]) + parts = split_attribute(relation) + return ('.'.join(parts[:-1]), parts[-1]) def get_related_assets(self, output, relation): if self.is_attribute_qualified(relation): - parts = relation.split('.') + parts = split_attribute(relation) assets = output[parts[0]]