Changeset 131
- Timestamp:
- 08/12/07 01:55:15
- Files:
-
- branches/ast/geniusql/adapters.py (modified) (1 diff)
- branches/ast/geniusql/astwalk.py (modified) (23 diffs)
- branches/ast/geniusql/deparse.py (modified) (8 diffs)
- branches/ast/geniusql/logicfuncs.py (modified) (2 diffs)
- branches/ast/geniusql/select.py (modified) (2 diffs)
- branches/ast/geniusql/test/test_logic.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/ast/geniusql/adapters.py
r130 r131 530 530 if value is None: 531 531 return 'NULL' 532 if not isinstance(value, typerefs.fixedpoint.FixedPoint): 533 value = typerefs.fixedpoint.FixedPoint(value) 532 534 return "'" + str(value) + "'" 533 535 branches/ast/geniusql/astwalk.py
r130 r131 1 """AST visitors, including rewriters and de compilers.1 """AST visitors, including rewriters and deparsers. 2 2 3 3 This work, including the source code, documentation … … 134 134 optimization, because the order of eval is (x * 4) * 5. Rewritten 135 135 as lambda x: 4 * 5 * x, the "4 * 5" can be replaced with "20". 136 137 irreducible: a list of constants (globals, freevars, or attributes) 138 which should not be reduced. For example, datetime.date.today 139 would usually be called and the result stored in the AST (since 140 it takes no arguments, and therefore has no free variables). 141 If you want the today function to be stored directly in the AST 142 (so it can be called later), include it in this "irreducible" list. 143 This does not control the conversion of globals and free variables 144 into constants--that happens regardless. It only controls the 145 reduction of complex expressions into simpler ones. 146 147 env: a dict of objects which will be used to make Consts out of 148 globals and builtins. This is auto-populated with items in 149 __builtin__ and any globals which were present at the time 150 the function was created, so you usually don't have to add 151 anything. However, it can be a handy way for frameworks to 152 provide globals without forcing every caller to import them. 136 153 """ 137 154 138 def __init__(self, func, env=None, reduce=True ):155 def __init__(self, func, env=None, reduce=True, irreducible=None): 139 156 codewalk.Visitor.__init__(self, func) 140 157 141 158 self.reduce = reduce 142 159 143 # self.env will be used to make Consts out of globals and builtins.144 160 if env is None: 145 161 self.env = {} … … 158 174 if fc.co_flags & codewalk.CO_VARARGS: 159 175 self.ast.star_args = self.ast.args.pop() 176 177 if irreducible is None: 178 irreducible = [] 179 self.irreducible = irreducible 160 180 161 181 def walk(self): 182 """Walk self and set self.ast.root.""" 162 183 self.stack = [] 163 184 self.targets = {} … … 169 190 if self.verbose: 170 191 self.debug("stack:", self.stack) 192 193 def _may_reduce(self, *terms): 194 """Return True if all terms are ast.Const and not marked irreducible.""" 195 for term in terms: 196 if not isinstance(term, ast.Const): 197 return False 198 if term.value in self.irreducible: 199 return False 200 if getattr(term.value, "irreducible", False): 201 return False 202 return True 171 203 172 204 def visit_instruction(self, op, lo=None, hi=None): … … 195 227 while terms: 196 228 term, oper = terms.pop() 197 if self.reduce and (isinstance(term, ast.Const) and 198 isinstance(clause, ast.Const)): 229 if self.reduce and self._may_reduce(term, clause): 199 230 op = ast_to_op[oper] 200 231 clause = ast.Const(op(term.value, clause.value)) … … 245 276 func = self.stack[-1] 246 277 247 if self.reduce and isinstance(func, ast.Const):278 if self.reduce and self._may_reduce(func): 248 279 if func.value is getattr and not isinstance(args[0], ast.Const): 249 280 self.stack[-1] = ast.Getattr(args[0], args[1].value) … … 252 283 # If all args/kwargs are also Const, 253 284 # reduce to a single Const. 254 argvals = [] 255 for a in args: 256 if isinstance(a, ast.Const): 257 argvals.append(a.value) 258 else: 259 argvals = None 260 break 261 if argvals is not None: 262 kwargvals = {} 263 for k in kwargs: 264 if isinstance(k.expr, ast.Const): 265 kwargvals[k.name] = k.expr.value 266 else: 267 kwargvals = None 268 break 269 if kwargvals is not None: 285 argvals = [a.value for a in args if self._may_reduce(a)] 286 if len(argvals) == len(args): 287 kwargvals = dict([(k.name, k.expr.value) for k, v in kwargs 288 if self._may_reduce(k.expr)]) 289 if len(kwargvals) == len(kwargs): 270 290 retval = func.value(*tuple(argvals), **kwargvals) 271 291 self.stack[-1] = ast.Const(retval) … … 279 299 term1, term2 = self.stack[-2:] 280 300 op = cmp_op[lo + (hi << 8)] 281 if self.reduce and (isinstance(term1, ast.Const) and 282 isinstance(term2, ast.Const)): 301 if self.reduce and self._may_reduce(term1, term2): 283 302 oper = codewalk.comparisons[op] 284 303 self.stack[-2:] = [ast.Const(oper(term1.value, term2.value))] … … 314 333 term = self.co_names[lo + (hi << 8)] 315 334 obj = self.stack[-1] 316 if self.reduce and isinstance(obj, ast.Const):335 if self.reduce and self._may_reduce(obj): 317 336 self.stack[-1] = ast.Const(getattr(obj.value, term)) 318 337 else: … … 331 350 value = self._func.func_closure[lo + (hi << 8)] 332 351 self.stack.append(ast.Const(codewalk.deref_cell(value))) 333 else: 334 name = self.co_freevars[lo + (hi << 8)] 335 self.stack.append(ast.Name(name)) 352 return 353 354 name = self.co_freevars[lo + (hi << 8)] 355 self.stack.append(ast.Name(name)) 336 356 337 357 def visit_LOAD_FAST(self, lo, hi): … … 347 367 raise KeyError("'%s' is not present in supplied globals." % name) 348 368 self.stack.append(ast.Const(self.env[name])) 349 else: 350 self.stack.append(ast.Name(name)) 369 return 370 371 self.stack.append(ast.Name(name)) 351 372 352 373 def visit_POP_TOP(self): … … 363 384 def visit_SLICE_PLUS_0(self): 364 385 obj = self.stack[-1] 365 if self.reduce and isinstance(obj, ast.Const):386 if self.reduce and self._may_reduce(obj): 366 387 self.stack[-1] = ast.Const(obj.value[:]) 367 388 else: … … 370 391 def visit_SLICE_PLUS_1(self): 371 392 obj, arg = self.stack[-2:] 372 if self.reduce and (isinstance(obj, ast.Const) and 373 isinstance(arg, ast.Const)): 393 if self.reduce and self._may_reduce(obj, arg): 374 394 self.stack[-2:] = [ast.Const(obj.value[arg.value:])] 375 395 else: … … 378 398 def visit_SLICE_PLUS_2(self): 379 399 obj, arg = self.stack[-2:] 380 if self.reduce and (isinstance(obj, ast.Const) and 381 isinstance(arg, ast.Const)): 400 if self.reduce and self._may_reduce(obj, arg): 382 401 self.stack[-2:] = ast.Const(obj.value[:arg.value]) 383 402 else: … … 386 405 def visit_SLICE_PLUS_3(self): 387 406 obj, arg1, arg2 = self.stack[-3:] 388 if self.reduce and (isinstance(obj, ast.Const) and 389 isinstance(arg1, ast.Const) and 390 isinstance(arg2, ast.Const)): 407 if self.reduce and self._may_reduce(obj, arg1, arg2): 391 408 self.stack[-3:] = ast.Const(obj.value[arg1.value:arg2.value]) 392 409 else: … … 398 415 self.stack[-3:] = [] 399 416 x.items.append((k, v)) 400 ##401 ## def visit_UNARY_CONVERT(self):402 ## term = self.stack.pop()403 ## self.stack.append("`(" + term + ")`")404 417 405 418 def visit_UNARY_INVERT(self): 406 419 term = self.stack[-1] 407 if self.reduce and isinstance(term, ast.Const):420 if self.reduce and self._may_reduce(term): 408 421 self.stack[-1] = ast.Const(~term.value) 409 422 else: … … 412 425 def visit_UNARY_NEGATIVE(self): 413 426 term = self.stack[-1] 414 if self.reduce and isinstance(term, ast.Const):427 if self.reduce and self._may_reduce(term): 415 428 self.stack[-1] = ast.Const(-term.value) 416 429 else: … … 419 432 def visit_UNARY_NOT(self): 420 433 term = self.stack[-1] 421 if self.reduce and isinstance(term, ast.Const):434 if self.reduce and self._may_reduce(term): 422 435 self.stack[-1] = ast.Const(not term.value) 423 436 else: … … 426 439 def visit_UNARY_POSITIVE(self): 427 440 term = self.stack[-1] 428 if self.reduce and isinstance(term, ast.Const):441 if self.reduce and self._may_reduce(term): 429 442 self.stack[-1] = ast.Const(+term.value) 430 443 else: … … 433 446 def visit_BINARY_SUBSCR(self): 434 447 op1, op2 = self.stack[-2:] 435 if self.reduce and (isinstance(op1, ast.Const) and 436 isinstance(op2, ast.Const)): 448 if self.reduce and self._may_reduce(op1, op2): 437 449 self.stack[-2:] = [ast.Const(op1.value[op2.value])] 438 450 else: 439 self.stack[-2:] = [ast.Subscript(op1, 'OP_APPLY', op2)]451 self.stack[-2:] = [ast.Subscript(op1, 'OP_APPLY', [op2])] 440 452 441 453 def binary_op(self, op): 442 454 op1, op2 = self.stack[-2:] 443 if self.reduce and (isinstance(op1, ast.Const) and 444 isinstance(op2, ast.Const)): 455 if self.reduce and self._may_reduce(op1, op2): 445 456 self.stack[-2:] = [ast.Const(ast_to_op[op](op1.value, op2.value))] 446 457 else: … … 450 461 def bit_op(self, op): 451 462 op1, op2 = self.stack[-2:] 452 if self.reduce and (isinstance(op1, ast.Const) and 453 isinstance(op2, ast.Const)): 463 if self.reduce and self._may_reduce(op1, op2): 454 464 self.stack[-2:] = [ast.Const(ast_to_op[op](op1.value, op2.value))] 455 465 else: … … 497 507 498 508 def visit_Dict(self, *terms): 499 return "{%s}" % ", ".join([self.walk(term) for term in terms]) 509 return "{%s}" % ", ".join(["%s: %s" % (self.walk(terms[i]), 510 self.walk(terms[i + 1])) 511 for i in xrange(0, len(terms), 2)]) 500 512 501 513 def visit_Tuple(self, *terms): … … 578 590 579 591 def visit_Subscript(self, expr, flags, subs): 580 expr = self.walk(expr) 581 return "%s[%r]" % (expr, subs) 592 return "%s[%s]" % (self.walk(expr), self.walk(subs)) 582 593 583 594 def visit_Invert(self, expr): branches/ast/geniusql/deparse.py
r130 r131 1 1 import datetime 2 import sys 3 import traceback 2 4 from types import FunctionType, NoneType 3 5 from geniusql import logic, astwalk … … 124 126 # SQL binary operators; a map from values in astwalk.binary_operators 125 127 # to their SQL equivalents. The default map is isomorphic. 126 sql_bin_op = dict([( v, v) for v in astwalk.codewalk.binary_repr.itervalues()])128 sql_bin_op = dict([(k, k) for k in astwalk.repr_to_op]) 127 129 128 130 none_expr = SQLExpression("NULL", "expr0", None, NoneType) … … 173 175 # Grab the completed SQL from a cache, if available 174 176 try: 175 sql, imp = ast_to_sql_cache[( rootrepr, tablenames)]177 sql, imp = ast_to_sql_cache[(self.typeset, rootrepr, tablenames)] 176 178 except KeyError: 177 179 pass … … 197 199 198 200 # Cache the result 199 ast_to_sql_cache[( rootrepr, tablenames)] = (result.sql, self.imperfect)201 ast_to_sql_cache[(self.typeset, rootrepr, tablenames)] = (result.sql, self.imperfect) 200 202 201 203 return result.sql … … 236 238 # Use TRUE for the term, so all records are returned. 237 239 term = self.expr_true 238 ## # Blurg. SQL Server is *so* picky. 239 ## if term == self.comp_true: 240 ## term = self.expr_true 241 ## elif term == self.comp_false: 242 ## term = self.expr_false 240 else: 241 # Blurg. SQL Server is *so* picky. 242 if term == self.comp_true: 243 term = self.expr_true 244 elif term == self.comp_false: 245 term = self.expr_false 243 246 newterms.append("(%s)" % term.sql) 244 247 clause = self.get_expr(" AND ".join(newterms), bool) … … 258 261 # Use TRUE for the term, so all records are returned. 259 262 term = self.expr_true 260 ## # Blurg. SQL Server is *so* picky. 261 ## if term == self.comp_true: 262 ## term = self.expr_true 263 ## elif term == self.comp_false: 264 ## term = self.expr_false 263 else: 264 # Blurg. SQL Server is *so* picky. 265 if term == self.comp_true: 266 term = self.expr_true 267 elif term == self.comp_false: 268 term = self.expr_false 265 269 newterms.append("(%s)" % term.sql) 266 270 clause = self.get_expr(" OR ".join(newterms), bool) … … 389 393 try: 390 394 sql = op1.adapter.compare_op(op1, op, self.sql_cmp_op[op], op2) 391 except TypeError: 395 except TypeError, exc: 396 if self.verbose: 397 self.debug("".join(traceback.format_exception(*sys.exc_info()))) 392 398 rop = reverseop[op] 393 399 try: 394 400 sql = op1.adapter.compare_op(op2, rop, self.sql_cmp_op[rop], op1) 395 except TypeError: 401 except TypeError, exc: 402 if self.verbose: 403 self.debug("".join(traceback.format_exception(*sys.exc_info()))) 396 404 raise CannotRepresent( 397 405 "No comparison function %r between %r and %r." % … … 415 423 (subs, expr)) 416 424 417 name = subs[0] 418 ## # name, since formed in visit_Const, may have extraneous quotes. 419 ## name = name.sql.strip("'\"") 425 name = subs[0].value 420 426 421 427 value = self.expr.kwargs[name] branches/ast/geniusql/logicfuncs.py
r130 r131 64 64 """Late-bound datetime.datetime.now(). Taint this when early binding.""" 65 65 return _datetime.datetime.now() 66 now. bind_late = True66 now.irreducible = True 67 67 68 68 def utcnow(): 69 69 """Late-bound datetime.datetime.utcnow(). Taint this when early binding.""" 70 70 return _datetime.datetime.utcnow() 71 utcnow. bind_late = True71 utcnow.irreducible = True 72 72 73 73 def today(): 74 74 """Late-bound datetime.date.today(). Taint this when early binding.""" 75 75 return _datetime.date.today() 76 today. bind_late = True76 today.irreducible = True 77 77 78 78 def iscurrentweek(value): … … 82 82 else: 83 83 return False 84 iscurrentweek. bind_late = True84 iscurrentweek.irreducible = True 85 85 86 86 def count(values): branches/ast/geniusql/select.py
r130 r131 245 245 # Get the key for the table. 246 246 colkey = '%s_%s' % (table.schema.key_for(table), colkey) 247 colname = '%s_%s' % ( alias, col.name)247 colname = '%s_%s' % (table.name, col.name) 248 248 colqname = self.db.quote(colname) 249 249 selname = '%s.%s AS %s' % (alias, col.qname, colqname) … … 308 308 append("FROM") 309 309 append(self.fromclause) 310 if self.whereclause:311 append("WHERE")312 append(self.whereclause)310 if self.whereclause: 311 append("WHERE") 312 append(self.whereclause) 313 313 if self._groupby and len(self._groupby) < len(self.input_list): 314 314 append("GROUP BY") branches/ast/geniusql/test/test_logic.py
r130 r131 17 17 e = logic.Expression(lambda x: icontains(x.Status, 'c')) 18 18 self.assertEqual(repr(e), lx + "icontains(x.Status, 'c'))") 19 co_code = e.func.func_code.co_code20 co_code = logic.codewalk.named_opcodes(map(ord, co_code))21 ## if sys.version_info >= (2, 5):22 ## # Python 2.5 stopped including args in co_names,23 ## # so the indices into co_names changed.24 ## self.assertEqual(co_code,25 ## ['LOAD_CONST', 2, 0,26 ## 'LOAD_FAST', 0, 0,27 ## 'LOAD_ATTR', 2, 0,28 ## 'LOAD_CONST', 0, 0,29 ## 'CALL_FUNCTION', 2, 0,30 ## 'RETURN_VALUE'])31 ## else:32 ## self.assertEqual(co_code,33 ## ['LOAD_CONST', 2, 0,34 ## 'LOAD_FAST', 0, 0,35 ## 'LOAD_ATTR', 2, 0,36 ## 'LOAD_CONST', 1, 0,37 ## 'CALL_FUNCTION', 2, 0,38 ## 'RETURN_VALUE'])39 19 40 20 # 4/28/04: This one failed in endue.html.nav, … … 240 220 f = logic.comparison('Name', 2, 'Harry') 241 221 g = logic.Expression(lambda x: x.Name == 'Harry') 242 self.assertEqual(f.func.func_code , g.func.func_code)222 self.assertEqual(f.func.func_code.co_code, g.func.func_code.co_code) 243 223 244 224 f = logic.comparison('Size', 4, 300) 245 225 g = logic.Expression(lambda x: x.Size > 300) 246 self.assertEqual(f.func.func_code , g.func.func_code)226 self.assertEqual(f.func.func_code.co_code, g.func.func_code.co_code) 247 227 248 228 f = logic.comparison(u'ID', 2, u'30003') 249 229 g = logic.Expression(lambda x: x.ID == u'30003') 250 self.assertEqual(f.func.func_code , g.func.func_code)230 self.assertEqual(f.func.func_code.co_code, g.func.func_code.co_code) 251 231 252 232
