diff --git a/optionloop/optionloop.py b/optionloop/optionloop.py index cc5c686..14326b4 100644 --- a/optionloop/optionloop.py +++ b/optionloop/optionloop.py @@ -80,12 +80,12 @@ """ -from collections import namedtuple from collections import defaultdict -class OptionLoop(object): +class OptionLoop(object): class optionloopconcat(object): + def __init__(self, oploop_list): self.oplooplist = oploop_list self.master_index = 0 @@ -95,7 +95,7 @@ def check_all(self): Checks to see that all option loops are unstarted """ return all(oploop.index == 0 for oploop in self.oplooplist) \ - and self.master_index == 0 + and self.master_index == 0 def __next__(self): if self.master_index < len(self.oplooplist): @@ -107,6 +107,12 @@ def __next__(self): else: raise StopIteration() + def copy(self): + newlist = [] + for oploop in self.oplooplist: + newlist.append(oploop.copy()) + return OptionLoop.optionloopconcat(newlist) + def __iter__(self): return self @@ -123,16 +129,14 @@ def __add__(self, other): elif isinstance(other, OptionLoop): return OptionLoop.optionloopconcat(self.oplooplist[:] + [other]) - next = __next__ #python 2 compatiblity - - + next = __next__ # python 2 compatiblity def __init__(self, initializing_dictionary, default_dict_factory=None): """ Initializes the OptionLoop. @param initializing_dictionary : - The basis of the option loop. + The basis of the option loop. The various options to iterate are the keys of the dictionary, while the value(s) associated with the key are iterated over @@ -142,10 +146,10 @@ def __init__(self, initializing_dictionary, default_dict_factory=None): assert isinstance(initializing_dictionary, dict) - self.mydict = initializing_dictionary + self.mydict = initializing_dictionary.copy() self.index = 0 - self.index_index = None - for key, value in self.mydict.items(): + self.end_index = None + for key, value in self.mydict.iteritems(): if isinstance(value, (str, bytes)): self.mydict[key] = [value] size = 1 @@ -155,48 +159,54 @@ def __init__(self, initializing_dictionary, default_dict_factory=None): except TypeError: self.mydict[key] = [value] size = len([value]) - - if self.index_index is None: - self.index_index = 1 + + if self.end_index is None: + self.end_index = 1 # the maximum index is the multiplicative sum # of the length of all interior arrays - self.index_index *= size + self.end_index *= size self.default_dict_factory = default_dict_factory self.use_dd = default_dict_factory is not None + def copy(self): + return OptionLoop(self.mydict, + self.default_dict_factory) + def __next__(self): if self.use_dd: value_list = defaultdict(self.default_dict_factory) else: value_list = {} startlen = 1 - if self.index_index is not None and self.index < self.index_index: - for key, value in self.mydict.items(): - value_list[key] = value[int((self.index / startlen) % len(value))] + if self.index < self.end_index: + for key, value in self.mydict.iteritems(): + value_list[key] = value[(self.index / startlen) % len(value)] startlen *= len(value) self.index += 1 else: - raise StopIteration() + raise StopIteration() return value_list def __add__(self, other): assert isinstance(other, OptionLoop) or isinstance(other, self.optionloopconcat), \ - "Adding object of type {} to option loop undefined".format(type(other)) + "Adding object of type {} to option loop undefined".format( + type(other)) if isinstance(other, self.optionloopconcat): if self.index > 0 or not other.check_all(): - raise Exception('Cannot add option loops once iteration has begun...') + raise Exception( + 'Cannot add option loops once iteration has begun...') return self.optionloopconcat([self] + other.oplooplist) if self.index > 0 or other.index > 0: - raise Exception('Cannot add option loops once iteration has begun...') + raise Exception( + 'Cannot add option loops once iteration has begun...') return self.optionloopconcat([self, other]) def __iter__(self): return self - next = __next__ #python 2 compatiblity - + next = __next__ # python 2 compatiblity diff --git a/optionloop/tests/test.py b/optionloop/tests/test.py index 92818da..57f4000 100644 --- a/optionloop/tests/test.py +++ b/optionloop/tests/test.py @@ -6,7 +6,9 @@ from ..optionloop import OptionLoop + class TestOptionLoop(unittest.TestCase): + def test_empty(self): d = {} op = OptionLoop(d) @@ -16,7 +18,7 @@ def test_empty(self): self.assertTrue(i is None) def test_default_dict(self): - d = {'test' : [True]} + d = {'test': [True]} op = OptionLoop(d, lambda: False) for i in op: self.assertTrue(i['notakey'] == False) @@ -25,19 +27,19 @@ def test_default_dict(self): self.assertTrue(i['notakey'] == True) def test_string1(self): - d = {'a' : 'a'} + d = {'a': 'a'} op = OptionLoop(d) for i in op: self.assertTrue(i['a'] == 'a') def test_string2(self): - d = {'a' : 'abc'} + d = {'a': 'abc'} op = OptionLoop(d) for i in op: self.assertTrue(i['a'] == 'abc') def test_multiple_values1(self): - d = {'a' : [False, True]} + d = {'a': [False, True]} op = OptionLoop(d) for i, state in enumerate(op): self.assertTrue(state['a'] == i) @@ -63,15 +65,15 @@ def test_multiple_values3(self): self.assertTrue(state['c'] == int(i / 2) + 1) def test_no_len(self): - d = {'a' : None} + d = {'a': None} op = OptionLoop(d) for i in op: self.assertTrue(i['a'] is None) def test_add_oploops(self): - d = {'a' : [False, True]} + d = {'a': [False, True]} op1 = OptionLoop(d) - d = {'a' : [1, 2]} + d = {'a': [1, 2]} op2 = OptionLoop(d) op = op1 + op2 for i, state in enumerate(op): @@ -81,12 +83,12 @@ def test_add_oploops(self): self.assertTrue(state['a'] == i - 1) def test_add_oploops2(self): - d = {'a' : [False]} + d = {'a': [False]} op1 = OptionLoop(d) - d = {'a' : [True]} + d = {'a': [True]} op2 = OptionLoop(d) op = op1 + op2 - d = {'a' : [1, 2]} + d = {'a': [1, 2]} op3 = OptionLoop(d) op = op + op3 for i, state in enumerate(op): @@ -96,12 +98,12 @@ def test_add_oploops2(self): self.assertTrue(state['a'] == i - 1) def test_add_oploops2(self): - d = {'a' : [False]} + d = {'a': [False]} op1 = OptionLoop(d) - d = {'a' : [True]} + d = {'a': [True]} op2 = OptionLoop(d) op = op1 + op2 - d = {'a' : [1, 2]} + d = {'a': [1, 2]} op3 = OptionLoop(d) op = op3 + op for i, state in enumerate(op): @@ -110,9 +112,8 @@ def test_add_oploops2(self): else: self.assertTrue(state['a'] == i + 1) - def test_add_oploops_bad(self): - d = {'a' : [False, True]} + d = {'a': [False, True]} op1 = OptionLoop(d) for i, state in enumerate(op1): op2 = OptionLoop(d) @@ -122,6 +123,53 @@ def test_add_oploops_bad(self): except Exception: self.assertTrue(True) + def test_copy(self): + d = {'a': [False, True]} + # test copy interface of oploop + op1 = OptionLoop(d, lambda: False) + + # run out loop + for i, state in enumerate(op1): + pass + + op2 = op1.copy() + + # copy should have same dictionary properties + self.assertTrue(op2.mydict == + op1.mydict) + self.assertTrue(op2.default_dict_factory == + op1.default_dict_factory) + # and a reset index + self.assertTrue(op2.index == 0) + # but a same end index + self.assertTrue(op2.end_index == op1.end_index) + + def test_copy_concat(self): + d = {'a': [False, True]} + d2 = {'b': ['a', 'b']} + # test copy interface of oploop + op1 = OptionLoop(d, lambda: False) + op2 = OptionLoop(d2, lambda: False) + + op = op1 + op2 + + # run out loop + for i, state in enumerate(op): + pass + + op_copy = op.copy() + + # copy should have same dictionary properties + self.assertTrue(len(op_copy.oplooplist) == len(op.oplooplist)) + for i in range(len(op.oplooplist)): + # check that the dicts of each oploop in list are same + self.assertTrue(op.oplooplist[i].mydict == + op_copy.oplooplist[i].mydict) + self.assertTrue(op.oplooplist[i].default_dict_factory == + op_copy.oplooplist[i].default_dict_factory) + # and a reset index + self.assertTrue(op_copy.master_index == 0) + if __name__ == '__main__': unittest.main()