Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

root/trunk/geniusql/test/test_logic.py

Revision 320 (checked in by lakin, 2 months ago)

properly deal with our new python jumps and fix the related tests. all of test_codewalk, test_astwalk and test_logic should now run properly in python 2.6 and 2.7

  • Property svn:eol-style set to native
Line 
1 import datetime
2 import pickle
3 import sys
4 import unittest
5
6 from geniusql import codewalk, logic, logicfuncs
7 # Might as well test with the geniusql custom logicfuncs
8 logicfuncs.init()
9
10 nums = logic.codewalk.numeric_opcodes
11 lx = "logic.Expression(lambda x: "
12
13
14 class ExpressionTests(unittest.TestCase):
15
16     def test_Expression_creation(self):
17         e = logic.Expression(lambda x: icontains(x.Status, 'c'))
18         self.assertEqual(repr(e), lx + "icontains(x.Status, 'c'))")
19
20         # 4/28/04: This one failed in endue.html.nav,
21         # because 'field' was a Unicode string, which crashed
22         # the interpreter when fed as part of co_names.
23         def build_expr(field, criteria):
24             k = lambda x: icontains(getattr(x, field), criteria)
25             return logic.Expression(k)
26         e = build_expr(u'GroupName', u'Westmin')
27         self.assertEqual(repr(e), lx + "icontains(x.GroupName, u'Westmin'))")
28
29         # Test that tainted functions don't get bound early.
30         e = logic.Expression(lambda x: x.FirstDate > today())
31         self.assertEqual(repr(e), lx + "x.FirstDate > today())")
32
33         # Test unary unit functions (as opposed to attributes)
34         e = logic.Expression(lambda x: x.has('Job') and x.Field == 'BC')
35         self.assertEqual(repr(e), lx + "(x.has('Job')) and (x.Field == 'BC'))")
36         e = logic.Expression(lambda x: not x.has('Job') and x.Field == 'BC')
37         self.assertEqual(repr(e), lx + "(not (x.has('Job'))) and (x.Field == 'BC'))")
38
39         # Test multiple args.
40         e = logic.Expression(lambda x, y, z: x.Field == 'BC' and
41                                              y.Qty > 3 and z.Qty < 20)
42         self.assertEqual(repr(e),
43                          "logic.Expression(lambda x, y, z: (x.Field == 'BC')"
44                          " and ((y.Qty > 3) and (z.Qty < 20)))")
45
46         # Test the 'in' operator.
47         e = logic.Expression(lambda x: x.Name in ['George', 'John'])
48         if sys.version_info >= (2, 5):
49             # Python 2.5 collapses the list into a single tuple CONST
50             self.assertEqual(repr(e), lx + "x.Name in ('George', 'John'))")
51         else:
52             self.assertEqual(repr(e), lx + "x.Name in ['George', 'John'])")
53
54         # Test varying return values.
55         e = logic.Expression(lambda x: x + 1)
56         self.assertEqual(repr(e), lx + "x + 1)")
57         e = logic.Expression(lambda x: x.Val + 3)
58         self.assertEqual(repr(e), lx + "x.Val + 3)")
59         e = logic.Expression(lambda x, y: x ** y)
60         self.assertEqual(repr(e), "logic.Expression(lambda x, y: x ** y)")
61         e = logic.Expression(lambda x: {'a': 1, 'b': 2})
62         self.assertEqual(repr(e), lx + "{'a': 1, 'b': 2})")
63         e = logic.Expression(lambda x: {'a': 1, 'b': 2}[x.Name[0]])
64         self.assertEqual(repr(e), lx + "{'a': 1, 'b': 2}[x.Name[0]])")
65
66     def test_pickling(self):
67         # Test __setstate__
68         e = logic.Expression(lambda x: True)
69         e.__setstate__(("lambda x: x.TripStatus != 'Inquiry' and x.Field "
70                         "== 'BC' and x.StartDate >= 3", {}))
71         self.assertEqual(e.code(),
72                          "lambda x: (x.TripStatus != 'Inquiry') and ((x.Field "
73                          "== 'BC') and (x.StartDate >= 3))")
74
75         # Test Expression pickling
76         e = logic.Expression(lambda x: x.LastDate > datetime.date(2004, 3, 1))
77         p = pickle.dumps(e)
78         f = pickle.loads(p)
79         self.assertEqual(repr(e), repr(f))
80
81         e = logic.Expression(lambda x: icontains(x.Status, 'c'))
82         p = pickle.dumps(e)
83         f = pickle.loads(p)
84         self.assertEqual(repr(e), repr(f))
85
86     def test_Expression_addition(self):
87         a = lambda x: x.Date == 3
88         b = lambda x: x.Qty == 5
89         e = logic.Expression(a)
90         e += logic.Expression(b)
91         self.assertEqual(e.code(), "lambda x: (x.Date == 3) and (x.Qty == 5)")
92
93         # This failed in endue.price_filter on 11/14/2005,
94         # due to bug #25 (Python 2.4 changed JUMP targets).
95         f = logic.Expression(lambda x: ((x.DateFrom == None or x.DateFrom <= datetime.date(2005, 11, 17))
96                                         and (x.DateTo == None or x.DateTo >= datetime.date(2005, 11, 17))))
97         f += logic.Expression(lambda x: x.DirectoryID == None or x.DirectoryID == 0)
98         self.assertEqual(f.code(),
99             'lambda x: ((x.DateFrom == None) or (x.DateFrom <= datetime.date(2005, 11, 17))) '
100             'and (((x.DateTo == None) or (x.DateTo >= datetime.date(2005, 11, 17))) '
101             'and ((x.DirectoryID == None) or (x.DirectoryID == 0)))')
102
103         # TODO: figure out what this test is testing.  As of right now
104         #       we can't directly compare the bytecodes of athe lambda that this
105         #       represents vs the bytecode we generate as we rewrite the portion
106         #       that loads birthmonth from a closure LOAD_DEREF into a constant.
107         birth_month = 3
108         base_expr = logic.Expression(lambda x: x.BirthDate != None)
109         stnd_expr = logic.Expression(lambda x: month(x.BirthDate) == birth_month and
110                                      day(x.BirthDate) == 12)
111         combo = base_expr + stnd_expr
112
113         if sys.version_info == (2, 7):
114             self.assertEqual(map(ord, combo.func.func_code.co_code),
115                              nums(['LOAD_FAST', 0, 0,
116                                    'LOAD_ATTR', pyversion.attr_index, 0,
117                                    'LOAD_CONST', 0, 0,
118                                    'COMPARE_OP', 3, 0,
119                                    'JUMP_IF_FALSE_OR_POP', 53, 0,
120                                    'POP_TOP',
121                                    'LOAD_GLOBAL', pyversion.global_index + 2, 0,
122                                    'LOAD_FAST', 0, 0,
123                                    'LOAD_ATTR', pyversion.attr_index, 0,
124                                    'CALL_FUNCTION', 1, 0,
125                                    'LOAD_CONST', 1, 0,
126                                    'COMPARE_OP', 2, 0,
127                                    'JUMP_IF_FALSE_OR_POP', 53, 0,
128                                    'POP_TOP',
129                                    'LOAD_GLOBAL', pyversion.global_index + 3, 0,
130                                    'LOAD_FAST', 0, 0,
131                                    'LOAD_ATTR', pyversion.attr_index, 0,
132                                    'CALL_FUNCTION', 1, 0,
133                                    'LOAD_CONST', 2, 0,
134                                    'COMPARE_OP', 2, 0,
135                                    'RETURN_VALUE']))
136         if sys.version_info == (2, 6):
137             self.assertEqual(map(ord, combo.func.func_code.co_code),
138                              nums(['LOAD_FAST', 0, 0,
139                                    'LOAD_ATTR', pyversion.attr_index, 0,
140                                    'LOAD_CONST', 0, 0,
141                                    'COMPARE_OP', 3, 0,
142                                    'JUMP_IF_FALSE', 41, 0,
143                                    'POP_TOP',
144                                    'LOAD_GLOBAL', pyversion.global_index + 2, 0,
145                                    'LOAD_FAST', 0, 0,
146                                    'LOAD_ATTR', pyversion.attr_index, 0,
147                                    'CALL_FUNCTION', 1, 0,
148                                    'LOAD_CONST', 1, 0,
149                                    'COMPARE_OP', 2, 0,
150                                    'JUMP_IF_FALSE', 19, 0,
151                                    'POP_TOP',
152                                    'LOAD_GLOBAL', pyversion.global_index + 3, 0,
153                                    'LOAD_FAST', 0, 0,
154                                    'LOAD_ATTR', pyversion.attr_index, 0,
155                                    'CALL_FUNCTION', 1, 0,
156                                    'LOAD_CONST', 2, 0,
157                                    'COMPARE_OP', 2, 0,
158                                    'RETURN_VALUE']))
159
160     def toBytecode(self, co_code):
161         return map(ord, co_code)
162
163     def assertBytecodeEqual(self, bc1, bc2):
164         bc1_string = codewalk.named_opcodes(bc1)
165         bc2_string = codewalk.named_opcodes(bc2)
166         self.assertEqual(bc1_string, bc2_string)
167
168     def test_Aggregator_co_name_rewriting(self):
169         self.maxDiff = None
170         # This one failed on junct.membership, because the co_name
171         # mapping was screwed up (I assumed co_names[0] == arg[0]).
172         ag = logic.Aggregator(lambda x, **kw: x.GroupID == u'4')
173         newfunc = lambda x, **kw: x.UserID == u'rbre'
174         ag.and_combine(newfunc)
175
176         lambda_ag = lambda x, **kw: x.GroupID == u'4' and x.UserID == u'rbre'
177         self.assertBytecodeEqual(
178             ag.bytecode(),
179             self.toBytecode(lambda_ag.func_code.co_code)
180         )
181         self.assertEqual(list(ag.co_names), list(lambda_ag.func_code.co_names))
182         self.assertEqual(list(ag.co_consts), list(lambda_ag.func_code.co_consts))
183
184         # the exact length of this array depends on the version of
185         # python - but we can get this test to be mostly useful
186         # by testing the shortest array length and explicitly slicing
187         self.assertEqual(ag.instr_index[:28], ([None] * 12 +
188                                           [ag.instr_index[12]] * 16))
189
190     def test_Aggregator__and__or(self):
191         a = lambda x: x.Date == 3
192         b = lambda x: x.Qty == 5
193
194         # Test aggregation.
195         lambda_ag = lambda x: x.Date == 3 and x.Qty == 5
196         ag = logic.Aggregator(a)
197         ag.and_combine(b)
198         self.assertBytecodeEqual(
199             ag.bytecode(),
200             self.toBytecode(lambda_ag.func_code.co_code)
201         )
202         self.assertEqual(list(ag.co_names), list(lambda_ag.func_code.co_names))
203         self.assertEqual(list(ag.co_consts), list(lambda_ag.func_code.co_consts))
204
205         # Combine another. Change the name of the first arg and add kwargs.
206         lambda_ag = lambda x, **kw: ((x.Date == 3) and (x.Qty == 5)) or (x.Size < kw['Size'])
207         ag.or_combine(lambda y, **kw: y.Size < kw['Size'])
208         self.assertBytecodeEqual(
209             ag.bytecode(),
210             self.toBytecode(lambda_ag.func_code.co_code)
211         )
212         self.assertEqual(list(ag.co_names), list(lambda_ag.func_code.co_names))
213         self.assertEqual(list(ag.co_consts), list(lambda_ag.func_code.co_consts))
214
215         f = ag.function()
216         self.assertEqual(logic.Expression(f).code(),
217                          "lambda x, **kw: ((x.Date == 3) and (x.Qty == 5)) "
218                          "or (x.Size < kw['Size'])")
219
220     def test_Aggregator__or__and(self):
221         # This test is quite similar to the above
222         a = lambda x: x.Date == 3
223         b = lambda x: x.Qty == 5
224
225         # Test aggregation.
226         lambda_ag = lambda x: x.Date == 3 or x.Qty == 5
227         ag = logic.Aggregator(a)
228         ag.or_combine(b)
229         self.assertBytecodeEqual(
230             ag.bytecode(),
231             self.toBytecode(lambda_ag.func_code.co_code)
232         )
233         self.assertEqual(list(ag.co_names), list(lambda_ag.func_code.co_names))
234         self.assertEqual(list(ag.co_consts), list(lambda_ag.func_code.co_consts))
235
236         # Combine another. Change the name of the first arg and add kwargs.
237         lambda_ag = lambda x, **kw: ((x.Date == 3) or (x.Qty == 5)) and (x.Size < kw['Size'])
238         ag.and_combine(lambda y, **kw: y.Size < kw['Size'])
239         self.assertBytecodeEqual(
240             ag.bytecode(),
241             self.toBytecode(lambda_ag.func_code.co_code)
242         )
243         self.assertEqual(list(ag.co_names), list(lambda_ag.func_code.co_names))
244         self.assertEqual(list(ag.co_consts), list(lambda_ag.func_code.co_consts))
245
246         f = ag.function()
247         self.assertEqual(logic.Expression(f).code(),
248                          "lambda x, **kw: ((x.Date == 3) or (x.Qty == 5)) "
249                          "and (x.Size < kw['Size'])")
250
251     def test_Aggregator__and__and(self):
252         # This test is quite similar to the above
253         a = lambda x: x.Date == 3
254         b = lambda x: x.Qty == 5
255
256         # Test aggregation.
257         lambda_ag = lambda x: x.Date == 3 and x.Qty == 5
258         ag = logic.Aggregator(a)
259         ag.and_combine(b)
260         self.assertBytecodeEqual(
261             ag.bytecode(),
262             self.toBytecode(lambda_ag.func_code.co_code)
263         )
264         self.assertEqual(list(ag.co_names), list(lambda_ag.func_code.co_names))
265         self.assertEqual(list(ag.co_consts), list(lambda_ag.func_code.co_consts))
266
267         # Combine another. Change the name of the first arg and add kwargs.
268         lambda_ag = lambda x, **kw: ((x.Date == 3) and (x.Qty == 5)) and (x.Size < kw['Size'])
269         ag.and_combine(lambda y, **kw: y.Size < kw['Size'])
270         self.assertBytecodeEqual(
271             ag.bytecode(),
272             self.toBytecode(lambda_ag.func_code.co_code)
273         )
274         self.assertEqual(list(ag.co_names), list(lambda_ag.func_code.co_names))
275         self.assertEqual(list(ag.co_consts), list(lambda_ag.func_code.co_consts))
276
277         f = ag.function()
278         # NOTE: it's a bit odd that the code version switches around the parenthesis
279         #       however - mathmatically - BOTH are equivalent - and we have to decide
280         #       where to put the parenthesis - the current code puts them here.
281         self.assertEqual(logic.Expression(f).code(),
282                          "lambda x, **kw: (x.Date == 3) and ((x.Qty == 5) "
283                          "and (x.Size < kw['Size']))")
284
285     def test_Aggregator__or__or(self):
286         # This test is quite similar to the above
287         a = lambda x: x.Date == 3
288         b = lambda x: x.Qty == 5
289
290         # Test aggregation.
291         lambda_ag = lambda x: x.Date == 3 or x.Qty == 5
292         ag = logic.Aggregator(a)
293         ag.or_combine(b)
294         self.assertBytecodeEqual(
295             ag.bytecode(),
296             self.toBytecode(lambda_ag.func_code.co_code)
297         )
298         self.assertEqual(list(ag.co_names), list(lambda_ag.func_code.co_names))
299         self.assertEqual(list(ag.co_consts), list(lambda_ag.func_code.co_consts))
300
301         # Combine another. Change the name of the first arg and add kwargs.
302         lambda_ag = lambda x, **kw: ((x.Date == 3) or (x.Qty == 5)) or (x.Size < kw['Size'])
303         ag.or_combine(lambda y, **kw: y.Size < kw['Size'])
304         self.assertBytecodeEqual(
305             ag.bytecode(),
306             self.toBytecode(lambda_ag.func_code.co_code)
307         )
308         self.assertEqual(list(ag.co_names), list(lambda_ag.func_code.co_names))
309         self.assertEqual(list(ag.co_consts), list(lambda_ag.func_code.co_consts))
310
311         f = ag.function()
312         # NOTE: it's a bit odd that the code version switches around the parenthesis
313         #       however - mathmatically - BOTH are equivalent - and we have to decide
314         #       where to put the parenthesis - the current code puts them here.
315         self.assertEqual(logic.Expression(f).code(),
316                          "lambda x, **kw: (x.Date == 3) or ((x.Qty == 5) "
317                          "or (x.Size < kw['Size']))")
318
319     def test_filter(self):
320         f = logic.filter(Date=3)
321         g = lambda x: x.Date == 3
322
323         self.assertEqual(
324             codewalk.named_opcodes(map(ord, f.func.func_code.co_code)),
325
326             codewalk.named_opcodes(map(ord, g.func_code.co_code))
327         )
328         self.assertEqual(f.func.func_code.co_names, g.func_code.co_names)
329         self.assertEqual(f.func.func_code.co_consts, g.func_code.co_consts)
330
331         f = logic.filter(Name='Harry', Weight=300)
332         g = lambda x: x.Name == 'Harry' and x.Weight == 300
333         self.assertEqual(
334             codewalk.named_opcodes(map(ord, f.func.func_code.co_code)),
335
336             codewalk.named_opcodes(map(ord, g.func_code.co_code))
337         )
338         self.assertEqual(f.func.func_code.co_names, g.func_code.co_names)
339         self.assertEqual(f.func.func_code.co_consts, g.func_code.co_consts)
340
341         f = logic.filter(Name='Harry', Weight=300, Height=6)
342         g = lambda x: x.Name == 'Harry' and x.Weight == 300 and x.Height == 6
343         self.assertEqual(
344             codewalk.named_opcodes(map(ord, f.func.func_code.co_code)),
345
346             codewalk.named_opcodes(map(ord, g.func_code.co_code))
347         )
348         self.assertEqual(f.func.func_code.co_names, g.func_code.co_names)
349         self.assertEqual(f.func.func_code.co_consts, g.func_code.co_consts)
350
351     def test_comparison(self):
352         f = logic.Expression()
353         g = logic.comparison('Name', 2, 200)
354         f = f & g
355
356         f = logic.comparison('Name', 2, 'Harry')
357         g = logic.Expression(lambda x: x.Name == 'Harry')
358         self.assertEqual(f.func.func_code.co_code, g.func.func_code.co_code)
359         self.assertEqual(f.func.func_code.co_consts, g.func.func_code.co_consts)
360         self.assertEqual(f.func.func_code.co_names, g.func.func_code.co_names)
361
362         f = logic.comparison('Size', 4, 300)
363         g = logic.Expression(lambda x: x.Size > 300)
364         self.assertEqual(f.func.func_code.co_code, g.func.func_code.co_code)
365         self.assertEqual(f.func.func_code.co_consts, g.func.func_code.co_consts)
366         self.assertEqual(f.func.func_code.co_names, g.func.func_code.co_names)
367
368         f = logic.comparison(u'ID', 2, u'30003')
369         g = logic.Expression(lambda x: x.ID == u'30003')
370         self.assertEqual(f.func.func_code.co_code, g.func.func_code.co_code)
371         self.assertEqual(f.func.func_code.co_consts, g.func.func_code.co_consts)
372         self.assertEqual(f.func.func_code.co_names, g.func.func_code.co_names)
373
374
375 if __name__ == "__main__":
376     unittest.main(__name__)
377
Note: See TracBrowser for help on using the browser.