def test_str_repr(self): @node def f(*args): return args @star @node def g(*x): return x @star def h(*x): return x cmp = (a | b) | star(f) star_a = star(a) merge_star = star(a + b) chain_star = star(a | b) self.assertEqual(str(cmp), "(a | b | star(f))") self.assertEqual(str(star_a), "star(a)") self.assertEqual(str(g), "star(g)") self.assertEqual(str(merge_star), "star(a + b)") self.assertEqual(str(chain_star), "star(a | b)") # You can technically apply star to a regular function, and it'll become a SimpleFunction self.assertEqual( str(h), "star({})".format(h._function.__closure__[0].cell_contents) ) # reprs remain the same self.assertEqual(repr(star_a), "SimpleFunction({})".format(star_a._function))
def test_multi_arg_map(self): @node def f(*args): return args m = concurrent(mmap(f)) with self.assertRaises(CompositionError): # Because star returns a simple function, we can't upgrade it. starmap = concurrent(star(mmap(f))) # we have to wrap concurrent in star instead. starmap = star(concurrent(mmap(f))) mapstar = concurrent(mmap(star(f))) self.assertEqual(m([1, 2, 3], [4, 5, 6]), ((1, 4), (2, 5), (3, 6))) self.assertEqual(m([1, 2, 3]), ((1, ), (2, ), (3, ))) with self.assertRaises(TypeError): starmap([1, 2, 3]) self.assertEqual(starmap([[1, 2, 3]]), m([1, 2, 3])) cmp = ([1, 2, 3], [4, 5, 6]) | starmap self.assertEqual(cmp(), ((1, 4), (2, 5), (3, 6))) cmp = ([1, 2, 3], [4, 5, 6]) | mapstar self.assertEqual(cmp(), ((1, 2, 3), (4, 5, 6)))
def test_concurrent(self): # Concurrent and star can work together, although this organization no longer makes sense with self.assertRaises(exceptions.CompositionError): aabbcc = a & b & c | concurrent(star(a & b & c)) # self.assertEqual(aabbcc('_'), '_aa_bb_cc') aabbcc = (a & b & c) | star(concurrent(a & b & c)) self.assertEqual(aabbcc("_"), ("_aa", "_bb", "_cc"))
def test_simple_star(self): @node def f(*args): return args cmp = (a | b) | star(f) self.assertEqual(cmp("_"), ("_", "a", "b")) self.assertEqual(star(f)([1, 2, 3]), (1, 2, 3))
def test_len_mismatch(self): # If len(inputs) <= len(functions), call remaining functions with no args. @node def f(x=None): if x: return x + "f" return "F" cmp = (a & b) | star(f & f & f & f) self.assertEqual(cmp("_"), ("_af", "_bf", "F", "F")) # if len(inputs) > len(functions), fail. cmp = (a & b & c) | star(f + f) with self.assertRaises(exceptions.CallError): cmp("_")
def test_multi_arg(self): @node def f(*args): return args m = mmap(f) starmap = star(mmap(f)) mapstar = mmap(star(f)) self.assertEqual(m([1, 2, 3], [4, 5, 6]), ((1, 4), (2, 5), (3, 6))) self.assertEqual(m([1, 2, 3]), ((1, ), (2, ), (3, ))) with self.assertRaises(TypeError): starmap([1, 2, 3]) self.assertEqual(starmap([[1, 2, 3]]), m([1, 2, 3])) cmp = ([1, 2, 3], [4, 5, 6]) | starmap self.assertEqual(cmp(), ((1, 4), (2, 5), (3, 6))) cmp = ([1, 2, 3], [4, 5, 6]) | mapstar self.assertEqual(cmp(), ((1, 2, 3), (4, 5, 6)))
def test_binary_functions(self): # The issue here is that f + f + f + f is not converted to a single FunctionMerge. Rather # it becomes nested FunctionMerges: (((f + f) + f) + f). Ideally we would be able to # handle this. One potential solution is to 'flatten' the FunctionMerge, but this doesn't # work for functions that aren't commutative. E.g., (a / b / c) != (a / (b / c)). I'm # leaving this test for now as a todo. @node def f(x=None): if x: return x + "f" return "F" cmp = (a & b) | star(f + f + f + f) self.assertEqual(cmp("_"), "_af_bfFF")
def __rmatmul__(self, other): from metafunctions.api import star return FunctionChain.combine(other, star(self))
def test_recursive_upgrade(self): aabbcc = (a & b & c) | star(a + b + c) self.assertEqual(aabbcc("_"), "_aa_bb_cc")