import unittest from dejavu import codewalk nums = codewalk.numeric_opcodes import datetime try: import fixedpoint except ImportError: fixedpoint = None amount = 5 a = [1,2,3,4,5] f = [lambda x: x in a, lambda x: x + datetime.date(2004, 1, 1), lambda x, **kw: (x.Date == datetime.date(2004, 1, 1) and x.Qty < kw['Size']), # Mix names from globals, locals, attrs lambda x, amount: (4 != x.amount) or (amount * 3 > 20), lambda x: 3 * 4 * 5 * x, lambda x: a[2:4] == -x['offset'], lambda x: amount == 5 or amount == x.Qty, lambda x: not (x.a == 3 and (x.b > 1 or x.b < -10)), # Unicode const lambda x: x.Name == u'Dimsdale', # getattr lambda x: getattr(x, 'Name') == u'Dimsdale', # multiple args lambda x, y, z, **kw: x.Qty > 1 and y.Qty > 20 and z.Type == 'A', ] if fixedpoint: f.append(lambda x: x.Amount > fixedpoint.FixedPoint(3, 2)) # Closure example def foo(): a = 5 def bar(): return a + 5 return bar f.append(foo()) fcode = [func.func_code.co_code for func in f] class VisitorTests(unittest.TestCase): def test_safe_tuple(self): l = ['logic', 'icontains', 'getattr', 'x', 'field', 'criteria', u'GroupName'] self.assertEqual(codewalk.safe_tuple(l), ('logic', 'icontains', 'getattr', 'x', 'field', 'criteria', 'GroupName')) def test_Localizer(self): r = codewalk.Localizer(f[0]).bytecode() self.assertEqual(r, nums(['LOAD_FAST', 0, 0, 'LOAD_CONST', 1, 0, 'COMPARE_OP', 6, 0, 'RETURN_VALUE'])) r = codewalk.Localizer(f[1]).bytecode() self.assertEqual(r, nums(['LOAD_FAST', 0, 0, 'LOAD_CONST', 3, 0, 'LOAD_ATTR', 2, 0, 'LOAD_CONST', 1, 0, 'LOAD_CONST', 2, 0, 'LOAD_CONST', 2, 0, 'CALL_FUNCTION', 3, 0, 'BINARY_ADD', 'RETURN_VALUE'])) r = codewalk.Localizer(f[2]).bytecode() self.assertEqual(r, nums(['LOAD_FAST', 0, 0, 'LOAD_ATTR', 1, 0, 'LOAD_CONST', 4, 0, 'LOAD_ATTR', 3, 0, 'LOAD_CONST', 1, 0, 'LOAD_CONST', 2, 0, 'LOAD_CONST', 2, 0, 'CALL_FUNCTION', 3, 0, 'COMPARE_OP', 2, 0, 'JUMP_IF_FALSE', 17, 0, 'POP_TOP', 'LOAD_FAST', 0, 0, 'LOAD_ATTR', 4, 0, 'LOAD_FAST', 1, 0, 'LOAD_CONST', 3, 0, 'BINARY_SUBSCR', 'COMPARE_OP', 0, 0, 'RETURN_VALUE'])) r = codewalk.Localizer(f[3]).bytecode() self.assertEqual(r, nums(['LOAD_CONST', 1, 0, 'LOAD_FAST', 0, 0, 'LOAD_ATTR', 1, 0, 'COMPARE_OP', 3, 0, 'JUMP_IF_TRUE', 14, 0, 'POP_TOP', 'LOAD_FAST', 1, 0, 'LOAD_CONST', 2, 0, 'BINARY_MULTIPLY', 'LOAD_CONST', 3, 0, 'COMPARE_OP', 4, 0, 'RETURN_VALUE'])) def test_EarlyBinder(self): r = codewalk.EarlyBinder(f[1]).bytecode() self.assertEqual(r, nums(['LOAD_FAST', 0, 0, 'LOAD_CONST', 5, 0, 'BINARY_ADD', 'RETURN_VALUE'])) r = codewalk.EarlyBinder(f[2]).bytecode() self.assertEqual(r, nums(['LOAD_FAST', 0, 0, 'LOAD_ATTR', 1, 0, 'LOAD_CONST', 6, 0, 'COMPARE_OP', 2, 0, 'JUMP_IF_FALSE', 17, 0, 'POP_TOP', 'LOAD_FAST', 0, 0, 'LOAD_ATTR', 4, 0, 'LOAD_FAST', 1, 0, 'LOAD_CONST', 3, 0, 'BINARY_SUBSCR', 'COMPARE_OP', 0, 0, 'RETURN_VALUE'])) r = codewalk.EarlyBinder(f[4]).bytecode() self.assertEqual(r, nums(['LOAD_CONST', 5, 0, 'LOAD_FAST', 0, 0, 'BINARY_MULTIPLY', 'RETURN_VALUE'])) r = codewalk.EarlyBinder(f[5]).bytecode() self.assertEqual(r, nums(['LOAD_CONST', 5, 0, 'LOAD_FAST', 0, 0, 'LOAD_CONST', 3, 0, 'BINARY_SUBSCR', 'UNARY_NEGATIVE', 'COMPARE_OP', 2, 0, 'RETURN_VALUE'])) r = codewalk.EarlyBinder(f[6]).bytecode() self.assertEqual(r, nums(['LOAD_CONST', 2, 0, 'JUMP_IF_TRUE', 13, 0, 'POP_TOP', 'LOAD_CONST', 1, 0, 'LOAD_FAST', 0, 0, 'LOAD_ATTR', 2, 0, 'COMPARE_OP', 2, 0, 'RETURN_VALUE'])) r = codewalk.EarlyBinder(f[9]).bytecode() self.assertEqual(r, nums(['LOAD_FAST', 0, 0, # 2, since co_names was ('getattr', 'x'). 'LOAD_ATTR', 2, 0, 'LOAD_CONST', 2, 0, 'COMPARE_OP', 2, 0, 'RETURN_VALUE'])) # Test a tainted (late-bound) function e = lambda x: x.FirstDate >= datetime.date.today() r = codewalk.EarlyBinder(e, bind_late=[datetime.date.today]).bytecode() self.assertEqual(r, nums(['LOAD_FAST', 0, 0, 'LOAD_ATTR', 1, 0, 'LOAD_CONST', 3, 0, 'CALL_FUNCTION', 0, 0, 'COMPARE_OP', 5, 0, 'RETURN_VALUE'])) # Closures (dereferencing of func_closure) r = codewalk.EarlyBinder(f[12]).bytecode() self.assertEqual(r, nums(['LOAD_CONST', 2, 0, 'RETURN_VALUE', 'LOAD_CONST', 0, 0, 'RETURN_VALUE'])) def test_LambdaDecompiler(self): s = ['lambda x: x in a', 'lambda x: x + datetime.date(2004, 1, 1)', "lambda x, **kw: (x.Date == datetime.date(2004, 1, 1)) and (x.Qty < kw['Size'])", "lambda x, amount: (4 != x.amount) or (amount * 3 > 20)", "lambda x: 3 * 4 * 5 * x", "lambda x: a[2:4] == -(x['offset'])", "lambda x: (amount == 5) or (amount == x.Qty)", "lambda x: not ((x.a == 3) and ((x.b > 1) or (x.b < -10)))", "lambda x: x.Name == u'Dimsdale'", "lambda x: getattr(x, 'Name') == u'Dimsdale'", "lambda x, y, z, **kw: (x.Qty > 1) and ((y.Qty > 20) and (z.Type == 'A'))", ] if fixedpoint: s.append("lambda x: x.Amount > fixedpoint.FixedPoint(3, 2)") for funcitem, stritem in zip(f, s): r = codewalk.LambdaDecompiler(funcitem).code() self.assertEqual(r, stritem) def test_BranchTracker(self): r = codewalk.BranchTracker(f[6]).branches() self.assertEqual(r, [9, 22]) def test_KeywordInspector(self): e = lambda x, **kw: x.Size > kw['Size'] self.assertEqual(codewalk.KeywordInspector(e).kwargs(), ["Size"]) e = lambda x: x.Size > kw['Size'] self.assertRaises(ValueError, codewalk.KeywordInspector, e) e = lambda x, **kw: x.Date > x.newdate(kw['Year'], 1, 1) self.assertEqual(codewalk.KeywordInspector(e).kwargs(), ["Year"]) def run_tests(): try: unittest.main(__name__) except SystemExit: # unittest.main normally raises SystemExit when complete. pass if __name__ == "__main__": run_tests()