From 8560d81173df6c7bfacb1a4ea619097bbc3b869e Mon Sep 17 00:00:00 2001 From: Ilya Gershgorin Date: Wed, 22 Jun 2016 15:38:52 -0400 Subject: [PATCH 1/5] Initial adding of repeat chars. --- .gitignore | 4 +- pyx12/map_if.py | 23 +++++---- pyx12/segment.py | 95 ++++++++++++++++++++++++++++++-------- pyx12/test/test_map_if.py | 18 ++++++++ pyx12/test/test_segment.py | 46 +++++++++++++++++- 5 files changed, 158 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 792a8cfa..9e7a26ac 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,6 @@ TestResults/ obj/ testenv/ Backup/pyx12.sln -*.htm \ No newline at end of file +*.htm +*~ +.idea/ \ No newline at end of file diff --git a/pyx12/map_if.py b/pyx12/map_if.py index 0191e55c..dacb2607 100644 --- a/pyx12/map_if.py +++ b/pyx12/map_if.py @@ -986,7 +986,7 @@ def is_valid(self, seg_data, errh): ref_des = '%02i' % (i + 1) comp_data = seg_data.get(ref_des) subele_count = child_node.get_child_count() - if seg_data.ele_len(ref_des) > subele_count and child_node.usage != 'N': + if seg_data.ele_max_len(ref_des) > subele_count and child_node.usage != 'N': subele_node = child_node.get_child_node_by_idx( subele_count + 1) err_str = 'Too many sub-elements in composite "%s" (%s)' % \ @@ -1496,17 +1496,24 @@ def is_valid(self, comp_data, errh): errh.ele_error('5', err_str, None, self.refdes) return False - if len(comp_data) > self.get_child_count(): + if comp_data.max_len() > self.get_child_count(): err_str = 'Too many sub-elements in composite "%s" (%s)' % ( self.name, self.refdes) errh.ele_error('3', err_str, None, self.refdes) valid = False - for i in range(min(len(comp_data), self.get_child_count())): - valid &= self.get_child_node_by_idx(i).is_valid(comp_data[i], errh) - for i in range(min(len(comp_data), self.get_child_count()), self.get_child_count()): - if i < self.get_child_count(): - #Check missing required elements - valid &= self.get_child_node_by_idx(i).is_valid(None, errh) + idx = 0 + for i in range(min(len(comp_data), self.get_child_count() * self.repeat)): + # repeating, reset the idx counter + if comp_data[i] == comp_data.repetition_term: + for j in range(min(idx, self.get_child_count()), self.get_child_count()): + if j < self.get_child_count(): + # Check missing required elements + valid &= self.get_child_node_by_idx(i).is_valid(None, errh) + idx = 0 + continue + valid &= self.get_child_node_by_idx(idx).is_valid(comp_data[i], errh) + idx += 1 + return valid def is_composite(self): diff --git a/pyx12/segment.py b/pyx12/segment.py index 34c87294..111cd60f 100644 --- a/pyx12/segment.py +++ b/pyx12/segment.py @@ -10,11 +10,9 @@ """ Implements an interface to a x12 segment. - A segment is comprised of a segment identifier and a sequence of elements. An element can be a simple element or a composite. A simple element is treated as a composite element with one sub-element. - All indexing is zero based. """ import re @@ -24,6 +22,7 @@ rec_seg_id = re.compile('^[A-Z][A-Z0-9]{1,2}$', re.S) + class Element(object): """ Holds a simple element, which is just a simple string. @@ -33,9 +32,9 @@ def __init__(self, ele_str): """ @param ele_str: 1::2 @type ele_str: string - """ self.value = ele_str if ele_str is not None else '' + self.repetitions = 1 def __eq__(self, other): if isinstance(other, Element): @@ -69,6 +68,12 @@ def __repr__(self): """ return self.value + def max_len(self): + """ + @rtype 1 + """ + return 1 + def format(self): """ @rtype: string @@ -109,8 +114,12 @@ def is_empty(self): else: return True + def get_repetitions(self): + return self.repetitions + # return ''.join([`num` for num in xrange(loop_count)]) - # def has_invalid_character(self, + # def has_invalid_character(self, + class Composite(object): """ @@ -120,7 +129,7 @@ class Composite(object): # Attributes: # Operations - def __init__(self, ele_str, subele_term=None): + def __init__(self, ele_str, subele_term=None, repetition_term=None): """ @type ele_str: string @raise EngineError: If a terminator is None and no default @@ -129,12 +138,29 @@ def __init__(self, ele_str, subele_term=None): raise EngineError('The sub-element terminator must be a single character, is %s' % (subele_term)) self.subele_term = subele_term self.subele_term_orig = subele_term + self.repetition_term = repetition_term if ele_str is None: raise EngineError('Element string is None') - members = ele_str.split(self.subele_term) + + if self.repetition_term: + reps = ele_str.split(self.repetition_term) + self.repetitions = len(reps) + else: + self.repetitions = 1 + self.elements = [] - for elem in members: - self.elements.append(Element(elem)) + # there's at least 1 repetition + if self.repetitions > 1: + for rep in reps: + members = rep.split(self.subele_term) + for elem in members: + self.elements.append(Element(elem)) + self.elements.append(self.repetition_term) + self.elements.pop() + else: + members = ele_str.split(self.subele_term) + for elem in members: + self.elements.append(Element(elem)) def __eq__(self, other): if isinstance(other, Composite): @@ -187,10 +213,23 @@ def __repr__(self): """ return self.format(self.subele_term) + def max_len(self): + """ + returns the maximum length for a repitition + @retype: int + """ + temp_list = list() + temp_list.append([]) + for elem in self.elements: + if elem == self.repetition_term: + temp_list.append([]) + else: + temp_list[-1].append(elem) + return max(map(len, temp_list)) + def format(self, subele_term=None): """ Format a composite - @return: string @raise EngineError: If terminator is None and no default """ @@ -201,7 +240,17 @@ def format(self, subele_term=None): for i in range(len(self.elements) - 1, -1, -1): if not self.elements[i].is_empty(): break - return subele_term.join([Element.__repr__(x) for x in self.elements[:i + 1]]) + res = [] + for itr, x in enumerate(self.elements[:i + 1]): + if isinstance(x, str): + if res[-1] == self.subele_term: + res.pop() + res.append(x) + else: + res.append(x.__repr__()) + res.append(subele_term) + res.pop() + return ''.join(res) def get_value(self): """ @@ -223,7 +272,7 @@ def is_composite(self): """ @rtype: boolean """ - if len(self.elements) > 1: + if self.max_len() > 1: return True else: return False @@ -232,7 +281,7 @@ def is_element(self): """ @rtype: boolean """ - if len(self.elements) == 1: + if self.max_len() == 1: return True else: return False @@ -252,6 +301,9 @@ def values_iterator(self): subele_ord = '{comp}'.format(comp=j+1) yield (subele_ord, self.elements[j].get_value()) + def get_repetitions(self): + return self.repetitions + class Segment(object): """ @@ -286,7 +338,7 @@ def __init__(self, seg_str, seg_term, ele_term, subele_term, repetition_term='^' #guarantee subele_term will not be matched self.elements.append(Composite(ele, ele_term)) else: - self.elements.append(Composite(ele, subele_term)) + self.elements.append(Composite(ele, subele_term, repetition_term)) def __eq__(self, other): if isinstance(other, Segment): @@ -324,7 +376,6 @@ def __repr__(self): def append(self, val): """ Append a composite to the segment - @param val: String value of composite @type val: string """ @@ -349,7 +400,6 @@ def _parse_refdes(self, ref_des): - a composite: TST03 where TST03 is a composite - a sub-element: TST03-2 - or any of the above with the segment ID omitted (02, 03, 03-1) - @param ref_des: X12 Reference Designator @type ref_des: string @rtype: tuple(ele_idx, subele_idx) @@ -408,7 +458,6 @@ def set(self, ref_des, val): """ Set the value of an element or subelement identified by the Reference Designator - @param ref_des: X12 Reference Designator @type ref_des: string @param val: New value @@ -457,6 +506,16 @@ def ele_len(self, ref_des): ele_idx = self._parse_refdes(ref_des)[0] return len(self.elements[ele_idx]) + def ele_max_len(self, ref_des): + """ + @param ref_des: X12 Reference Designator + @type ref_des: string + @return: max number of sub-elements in an element or composite repititon + @rtype: int + """ + ele_idx = self._parse_refdes(ref_des)[0] + return self.elements[ele_idx].max_len() + def set_seg_term(self, seg_term): """ @param seg_term: Segment terminator @@ -533,7 +592,7 @@ def is_empty(self): def is_seg_id_valid(self): """ Is the Segment identifier valid? - EBNF: + EBNF: ::= [] @rtype: boolean """ @@ -565,4 +624,4 @@ def values_iterator(self): if not self.elements[i].is_empty(): ele_ord = '{idx:0>2}'.format(idx=i+1) refdes = '{segid}{ele_ord}'.format(segid=self.seg_id, ele_ord=ele_ord) - yield (refdes, ele_ord, None, self.elements[i].get_value()) + yield (refdes, ele_ord, None, self.elements[i].get_value()) \ No newline at end of file diff --git a/pyx12/test/test_map_if.py b/pyx12/test/test_map_if.py index 45d62250..1226ade5 100644 --- a/pyx12/test/test_map_if.py +++ b/pyx12/test/test_map_if.py @@ -827,3 +827,21 @@ def test_get_composite_node_path_refdes(self): refdes = 'STC01-01' newnode = node.parent.parent.getnodebypath2(refdes) self.assertEqual(newnode.get_path(), '/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000A/2000B/2200B/STC01-01') + + +class RepeatElement(unittest.TestCase): + """ + Verify that we can handle repeat elements + """ + def setUp(self): + param = pyx12.params.params() + self.map = pyx12.map_if.load_map_file('834.5010.X220.A1.xml', param) + self.errh = pyx12.error_handler.errh_null() + + def test_composite_segment_validity(self): + self.errh.err_cde = None + seg_data = pyx12.segment.Segment('DMG*D8*19670722*M*I*:RET:2106-3^:RET:1002-5*1~', '~', '*', ':', '^') + node = self.map.getnodebypath('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100A/DMG') + result = node.is_valid(seg_data, self.errh) + self.assertTrue(result, '%s should be valid' % (seg_data.format())) + self.assertEqual(self.errh.err_cde, None) diff --git a/pyx12/test/test_segment.py b/pyx12/test/test_segment.py index 6930952c..55d56f91 100644 --- a/pyx12/test/test_segment.py +++ b/pyx12/test/test_segment.py @@ -107,13 +107,26 @@ class Composite(unittest.TestCase): def setUp(self): seg_str = 'TST*AA:1:Y*BB:5*ZZ' self.seg = pyx12.segment.Segment(seg_str, '~', '*', ':') + seg_str_repeats = 'TST*AA:1:Y^AA:2:Z*BB:5*Z' + self.repeat_seg = pyx12.segment.Segment(seg_str_repeats, '~', '*', ':', '^') def test_composite_is_a(self): self.assertTrue(self.seg.is_composite('TST01')) self.assertFalse(self.seg.is_element('TST01')) + self.assertTrue(self.repeat_seg.is_composite('TST01')) + self.assertFalse(self.repeat_seg.is_element('TST01')) def test_composite_len(self): self.assertEqual(len(self.seg.get('01')), 3) + self.assertEqual(len(self.repeat_seg.get('01')), 7) + + def test_composite_max_len(self): + self.assertEqual(self.seg.get('01').max_len(), 3) + self.assertEqual(self.repeat_seg.get('01').max_len(), 3) + + def test_repetitions(self): + self.assertEqual(self.seg.get('01').get_repetitions(), 1) + self.assertEqual(self.repeat_seg.get('01').get_repetitions(), 2) def test_composite_indexing(self): self.assertEqual(self.seg.get_value('TST01-1'), 'AA') @@ -121,19 +134,39 @@ def test_composite_indexing(self): self.assertEqual(self.seg.get_value('TST01-4'), None) #self.assertRaises(IndexError, lambda x: self.seg.get('TST01-%i' % (x)), 4) + self.assertEqual(self.repeat_seg.get_value('TST01-1'), 'AA') + self.assertEqual(self.repeat_seg.get_value('TST01-3'), 'Y') + self.assertEqual(self.repeat_seg.get_value('TST01-4'), '^') + self.assertEqual(self.repeat_seg.get_value('TST01-5'), 'AA') + self.assertEqual(self.repeat_seg.get_value('TST01-6'), '2') + self.assertEqual(self.repeat_seg.get_value('TST01-8'), None) + class Simple(unittest.TestCase): def setUp(self): - seg_str = 'TST*AA*1*Y*BB:5*ZZ' + seg_str = 'TST*AA*1*Y*BB*ZZ' + repeat_seg_str = 'TST*AA^BB*1*Y*BB*ZZ' self.seg = pyx12.segment.Segment(seg_str, '~', '*', ':') + self.repeat_seg = pyx12.segment.Segment(repeat_seg_str, '~', '*', ':', '^') def test_simple_is_a(self): self.assertTrue(self.seg.is_element('TST01')) self.assertFalse(self.seg.is_composite('TST01')) + self.assertTrue(self.repeat_seg.is_element('TST01')) + self.assertFalse(self.repeat_seg.is_composite('TST01')) def test_simple_len(self): self.assertEqual(len(self.seg.get('01')), 1) + self.assertEqual(len(self.repeat_seg.get('01')), 3) + + def test_simple_max_len(self): + self.assertEqual(self.seg.get('01').max_len(), 1) + self.assertEqual(self.repeat_seg.get('01').max_len(), 1) + + def test_repetitions(self): + self.assertEqual(self.seg.get('01').get_repetitions(), 1) + self.assertEqual(self.repeat_seg.get('01').get_repetitions(), 2) def test_simple_indexing(self): self.assertEqual(self.seg.get_value('TST01'), 'AA') @@ -143,6 +176,17 @@ def test_simple_indexing(self): self.assertEqual(self.seg.get_value('TST06'), None) #self.assertRaises(IndexError, lambda x: self.seg[0][x].get_value(), 1) + self.assertEqual(self.repeat_seg.get_value('TST01'), 'AA^BB') + self.assertEqual(self.seg.get_value('TST02'), '1') + self.assertEqual(self.seg.get_value('TST03'), 'Y') + self.assertEqual(self.seg.get_value('TST05'), 'ZZ') + self.assertEqual(self.seg.get_value('TST06'), None) + + def test_indexing(self): + self.assertEqual(self.repeat_seg.get_value('TST01-1'), 'AA') + self.assertEqual(self.repeat_seg.get_value('TST01-2'), '^') + self.assertEqual(self.repeat_seg.get_value('TST01-3'), 'BB') + def test_simple_spaces(self): seg_str = 'TST*AA* *BB~' seg = pyx12.segment.Segment(seg_str, '~', '*', ':') From d404e1582d55d37641a462209a1a0634e8ab80aa Mon Sep 17 00:00:00 2001 From: Ilya Gershgorin Date: Wed, 22 Jun 2016 16:40:39 -0400 Subject: [PATCH 2/5] Adding max rep validation. --- pyx12/map_if.py | 12 ++++++++---- pyx12/test/test_map_if.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pyx12/map_if.py b/pyx12/map_if.py index dacb2607..001cfde7 100644 --- a/pyx12/map_if.py +++ b/pyx12/map_if.py @@ -987,13 +987,13 @@ def is_valid(self, seg_data, errh): comp_data = seg_data.get(ref_des) subele_count = child_node.get_child_count() if seg_data.ele_max_len(ref_des) > subele_count and child_node.usage != 'N': - subele_node = child_node.get_child_node_by_idx( - subele_count + 1) err_str = 'Too many sub-elements in composite "%s" (%s)' % \ - (subele_node.name, subele_node.refdes) + (child_node.name, child_node.refdes) err_value = seg_data.get_value(ref_des) errh.ele_error('3', err_str, err_value, ref_des) - valid &= child_node.is_valid(comp_data, errh) + valid = False + else: + valid &= child_node.is_valid(comp_data, errh) elif child_node.is_element(): # Validate Element if i == 1 and seg_data.get_seg_id() == 'DTP' \ @@ -1501,6 +1501,10 @@ def is_valid(self, comp_data, errh): self.name, self.refdes) errh.ele_error('3', err_str, None, self.refdes) valid = False + if self.repeat < comp_data.get_repetitions(): + err_str = 'Too many repetitions in composite "%s" (%s)' % (self.name, self.refdes) + errh.ele_error('12', err_str, None, self.refdes) + valid = False idx = 0 for i in range(min(len(comp_data), self.get_child_count() * self.repeat)): # repeating, reset the idx counter diff --git a/pyx12/test/test_map_if.py b/pyx12/test/test_map_if.py index 1226ade5..859af517 100644 --- a/pyx12/test/test_map_if.py +++ b/pyx12/test/test_map_if.py @@ -845,3 +845,19 @@ def test_composite_segment_validity(self): result = node.is_valid(seg_data, self.errh) self.assertTrue(result, '%s should be valid' % (seg_data.format())) self.assertEqual(self.errh.err_cde, None) + + def test_composite_segment_err_in_repetition(self): + self.errh.err_cde = None + seg_data = pyx12.segment.Segment('DMG*D8*19670722*M*I*:RET:2106-3^:RET:1002-5:*1~', '~', '*', ':', '^') + node = self.map.getnodebypath('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100A/DMG') + result = node.is_valid(seg_data, self.errh) + self.assertFalse(result, '%s should not be valid' % (seg_data.format())) + self.assertEqual(self.errh.err_cde, '3') + + def test_composite_segment_too_long(self): + self.errh.err_cde = None + seg_data = pyx12.segment.Segment('DMG*D8*19670722*M*I*:RET:2106-3^:RET:1002-5^:RET:1002-5^:RET:1002-5^:RET:1002-5^:RET:1002-5^:RET:1002-5^:RET:1002-5^:RET:1002-5^:RET:1002-5^:RET:1002-5*1~', '~', '*', ':', '^') + node = self.map.getnodebypath('/ISA_LOOP/GS_LOOP/ST_LOOP/DETAIL/2000/2100A/DMG') + result = node.is_valid(seg_data, self.errh) + self.assertFalse(result, '%s should not be valid' % (seg_data.format())) + self.assertEqual(self.errh.err_cde, '12') From 4aaca4c8abee330cb5877cf8cdd0c02f70e03c04 Mon Sep 17 00:00:00 2001 From: Ilya Gershgorin Date: Wed, 22 Jun 2016 16:52:06 -0400 Subject: [PATCH 3/5] Fix missing validation. --- pyx12/map_if.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyx12/map_if.py b/pyx12/map_if.py index 001cfde7..4d3d2238 100644 --- a/pyx12/map_if.py +++ b/pyx12/map_if.py @@ -1512,11 +1512,16 @@ def is_valid(self, comp_data, errh): for j in range(min(idx, self.get_child_count()), self.get_child_count()): if j < self.get_child_count(): # Check missing required elements - valid &= self.get_child_node_by_idx(i).is_valid(None, errh) + valid &= self.get_child_node_by_idx(j).is_valid(None, errh) idx = 0 continue valid &= self.get_child_node_by_idx(idx).is_valid(comp_data[i], errh) idx += 1 + # validate the non-repeating segments (or last repeat seg) + for j in range(min(idx, self.get_child_count()), self.get_child_count()): + if j < self.get_child_count(): + # Check missing required elements + valid &= self.get_child_node_by_idx(j).is_valid(None, errh) return valid From a2894ffcec0ab75f58a6ea5df8b6141dc52a53bd Mon Sep 17 00:00:00 2001 From: Ilya Gershgorin Date: Wed, 22 Jun 2016 16:55:24 -0400 Subject: [PATCH 4/5] Bump version. --- pyx12/version.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyx12/version.py b/pyx12/version.py index 3a5935a2..ef6497d0 100644 --- a/pyx12/version.py +++ b/pyx12/version.py @@ -1 +1 @@ -__version__ = "2.3.1" +__version__ = "2.3.2" diff --git a/setup.py b/setup.py index cb8be00a..f103575a 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ setup( name="pyx12", - version="2.3.1", + version="2.3.2", long_description=long_description, license='BSD', description="HIPAA X12 validator, parser and converter", From 3ed47118a5131378d23bb5868864e8cdac28f78c Mon Sep 17 00:00:00 2001 From: Ilya Gershgorin Date: Mon, 27 Jun 2016 18:24:41 -0400 Subject: [PATCH 5/5] Adding a get_values_list function for repetition. --- pyx12/segment.py | 56 +++++++++++++++++++++++++++++++++++--- pyx12/test/test_segment.py | 20 ++++++++++++++ 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/pyx12/segment.py b/pyx12/segment.py index 111cd60f..cf6266cb 100644 --- a/pyx12/segment.py +++ b/pyx12/segment.py @@ -80,6 +80,12 @@ def format(self): """ return self.value + def format_list(self): + """ + @rtype: list(string) + """ + return [self.value] + def get_value(self): """ @rtype: string @@ -243,15 +249,46 @@ def format(self, subele_term=None): res = [] for itr, x in enumerate(self.elements[:i + 1]): if isinstance(x, str): - if res[-1] == self.subele_term: - res.pop() res.append(x) else: res.append(x.__repr__()) - res.append(subele_term) - res.pop() + if itr + 1 < len(self.elements) and not isinstance(self.elements[itr+1], str): + res.append(subele_term) + if itr+1 < len(self.elements) and self.elements[itr+1].is_empty(): + res.pop() return ''.join(res) + def format_list(self, subele_term=None): + """ + Format a composite and return a tuple of repetitions + @return: list(string) + @raise EngineError: If terminator is None and no default + """ + if subele_term is None: + subele_term = self.subele_term + if subele_term is None: + raise EngineError('subele_term is None') + for i in range(len(self.elements) - 1, -1, -1): + if not self.elements[i].is_empty(): + break + res = [] + rep = [] + for itr, x in enumerate(self.elements[:i + 1]): + if isinstance(x, str): + if itr+1 < len(self.elements) and self.elements[itr+1].is_empty(): + rep.pop() + res.append(''.join(rep)) + rep = [] + else: + rep.append(x.__repr__()) + if itr + 1 < len(self.elements) and not isinstance(self.elements[itr+1], str): + rep.append(subele_term) + if len(rep) > 0: + if itr+1 < len(self.elements) and self.elements[itr+1].is_empty(): + rep.pop() + res.append(''.join(rep)) + return res + def get_value(self): """ Get value of simple element @@ -446,6 +483,17 @@ def get_value(self, ref_des): else: return comp1.format() + def get_values_list(self, ref_des): + """ + @param ref_des: X12 Reference Designator + @type ref_des: string + """ + comp1 = self.get(ref_des) + if comp1 is None: + return None + else: + return comp1.format_list() + def get_value_by_ref_des(self, ref_des): """ @param ref_des: X12 Reference Designator diff --git a/pyx12/test/test_segment.py b/pyx12/test/test_segment.py index 55d56f91..e7d62a22 100644 --- a/pyx12/test/test_segment.py +++ b/pyx12/test/test_segment.py @@ -128,6 +128,26 @@ def test_repetitions(self): self.assertEqual(self.seg.get('01').get_repetitions(), 1) self.assertEqual(self.repeat_seg.get('01').get_repetitions(), 2) + def test_simple_indexing(self): + self.assertEqual(self.seg.get_value('TST01'), 'AA:1:Y') + self.assertEqual(self.seg.get_value('TST02'), 'BB:5') + self.assertEqual(self.seg.get_value('TST03'), 'ZZ') + self.assertEqual(self.seg.get_value('TST06'), None) + + self.assertEqual(self.repeat_seg.get_value('TST01'), 'AA:1:Y^AA:2:Z') + self.assertEqual(self.repeat_seg.get_value('TST02'), 'BB:5') + self.assertEqual(self.repeat_seg.get_value('TST03'), 'Z') + self.assertEqual(self.repeat_seg.get_value('TST06'), None) + + self.assertEqual(self.seg.get_values_list('TST01'), ['AA:1:Y']) + self.assertEqual(self.seg.get_values_list('TST02'), ['BB:5']) + self.assertEqual(self.seg.get_values_list('TST03'), ['ZZ']) + self.assertEqual(self.seg.get_values_list('TST06'), None) + + self.assertEqual(self.repeat_seg.get_values_list('TST01'), ['AA:1:Y', 'AA:2:Z']) + self.assertEqual(self.repeat_seg.get_values_list('TST02'), ['BB:5']) + self.assertEqual(self.repeat_seg.get_values_list('TST03'), ['Z']) + def test_composite_indexing(self): self.assertEqual(self.seg.get_value('TST01-1'), 'AA') self.assertEqual(self.seg.get_value('TST01-3'), 'Y')