📄 expr.py
字号:
## Copyright (c) 2006, 2007 Canonical## Written by Gustavo Niemeyer <gustavo@niemeyer.net>## This file is part of Storm Object Relational Mapper.## Storm is free software; you can redistribute it and/or modify# it under the terms of the GNU Lesser General Public License as# published by the Free Software Foundation; either version 2.1 of# the License, or (at your option) any later version.## Storm is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU Lesser General Public License for more details.## You should have received a copy of the GNU Lesser General Public License# along with this program. If not, see <http://www.gnu.org/licenses/>.#from datetime import datetime, date, time, timedeltafrom copy import copyimport sysfrom storm.exceptions import CompileError, NoTableError, ExprErrorfrom storm.variables import ( Variable, CharsVariable, UnicodeVariable, LazyValue, DateTimeVariable, DateVariable, TimeVariable, TimeDeltaVariable, BoolVariable, IntVariable, FloatVariable)from storm import Undef# --------------------------------------------------------------------# Basic compiler infrastructureclass Compile(object): """Compiler based on the concept of generic functions.""" def __init__(self, parent=None): self._local_dispatch_table = {} self._local_precedence = {} self._dispatch_table = {} self._precedence = {} self._hash = None self._parents_hash = None self._parents = [] if parent: self._parents.extend(parent._parents) self._parents.append(parent) def _check_parents(self): parents_hash = hash(tuple(parent._hash for parent in self._parents)) if parents_hash != self._parents_hash: self._parents_hash = parents_hash for parent in self._parents: self._dispatch_table.update(parent._local_dispatch_table) self._precedence.update(parent._local_precedence) self._dispatch_table.update(self._local_dispatch_table) self._precedence.update(self._local_precedence) def _update(self): self._dispatch_table.update(self._local_dispatch_table) self._precedence.update(self._local_precedence) self._hash = hash(tuple(sorted(self._local_dispatch_table.items() + self._local_precedence.items()))) def fork(self): return self.__class__(self) def when(self, *types): def decorator(method): for type in types: self._local_dispatch_table[type] = method self._update() return method return decorator def get_precedence(self, type): self._check_parents() return self._precedence.get(type, MAX_PRECEDENCE) def set_precedence(self, precedence, *types): for type in types: self._local_precedence[type] = precedence self._update() def _compile_single(self, state, expr, outer_precedence): cls = expr.__class__ for class_ in cls.__mro__: handler = self._dispatch_table.get(class_) if handler is not None: inner_precedence = state.precedence = \ self._precedence.get(cls, MAX_PRECEDENCE) statement = handler(self._compile, state, expr) if inner_precedence < outer_precedence: statement = "(%s)" % statement return statement else: raise CompileError("Don't know how to compile type %r of %r" % (expr.__class__, expr)) def _compile(self, state, expr, join=", ", raw=False): # This docstring is in a pretty crappy place; where could it # go that would be more discoverable? """ @type state: L{State}. @param expr: The expression to compile. @param join: The string token to use to put between subexpressions. Defaults to ", ". @param raw: If true, any string or unicode expression or subexpression will not be further compiled. """ outer_precedence = state.precedence expr_type = type(expr) if (expr_type is SQLRaw or expr_type is SQLToken or raw and (expr_type is str or expr_type is unicode)): return expr if expr_type in (tuple, list): compiled = [] for subexpr in expr: subexpr_type = type(subexpr) if (subexpr_type is SQLRaw or subexpr_type is SQLToken or raw and (subexpr_type is str or subexpr_type is unicode)): statement = subexpr elif subexpr_type in (tuple, list): state.precedence = outer_precedence statement = self._compile(state, subexpr, join, raw) else: statement = self._compile_single(state, subexpr, outer_precedence) compiled.append(statement) statement = join.join(compiled) else: statement = self._compile_single(state, expr, outer_precedence) state.precedence = outer_precedence return statement def __call__(self, expr): self._check_parents() state = State() return self._compile(state, expr), state.parametersclass CompilePython(Compile): def get_expr(self, expr): return self._compile(State(), expr) def __call__(self, expr): exec ("def match(get_column): return bool(%s)" % self._compile(State(), expr)) return matchclass State(object): """All the data necessary during compilation of an expression. @ivar aliases: Dict of L{Column} instances to L{Alias} instances, specifying how columns should be compiled as aliases in very specific situations. This is typically used to work around strange deficiencies in various databases. @ivar auto_tables: The list of all implicitly-used tables. e.g., in store.find(Foo, Foo.attr==Bar.id), the tables of Bar and Foo are implicitly used because columns in them are referenced. This is used when building tables. @ivar join_tables: If not None, when Join expressions are compiled, tables seen will be added to this set. This acts as a blacklist against auto_tables when compiling Joins, because the generated statements should not refer to the table twice. @ivar context: an instance of L{Context}, specifying the context of the expression currently being compiled. @ivar precedence: Current precedence, automatically set and restored by the compiler. If an inner precedence is lower than an outer precedence, parenthesis around the inner expression are automatically emitted. """ def __init__(self): self._stack = [] self.precedence = 0 self.parameters = [] self.auto_tables = [] self.join_tables = None self.context = None self.aliases = None def push(self, attr, new_value=Undef): """Set an attribute in a way that can later be reverted with L{pop}. """ old_value = getattr(self, attr, None) self._stack.append((attr, old_value)) if new_value is Undef: new_value = copy(old_value) setattr(self, attr, new_value) return old_value def pop(self): """Revert the topmost L{push}. """ setattr(self, *self._stack.pop(-1))compile = Compile()compile_python = CompilePython()# --------------------------------------------------------------------# Expression contextsclass Context(object): """ An object used to specify the nature of expected SQL expressions being compiled in a given context. """ def __init__(self, name): self._name = name def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self._name)TABLE = Context("TABLE")EXPR = Context("EXPR")COLUMN = Context("COLUMN")COLUMN_PREFIX = Context("COLUMN_PREFIX")COLUMN_NAME = Context("COLUMN_NAME")SELECT = Context("SELECT")# --------------------------------------------------------------------# Builtin type support@compile.when(str)def compile_str(compile, state, expr): state.parameters.append(CharsVariable(expr)) return "?"@compile.when(unicode)def compile_unicode(compile, state, expr): state.parameters.append(UnicodeVariable(expr)) return "?"@compile.when(int, long)def compile_int(compile, state, expr): state.parameters.append(IntVariable(expr)) return "?"@compile.when(float)def compile_float(compile, state, expr): state.parameters.append(FloatVariable(expr)) return "?"@compile.when(bool)def compile_bool(compile, state, expr): state.parameters.append(BoolVariable(expr)) return "?"@compile.when(datetime)def compile_datetime(compile, state, expr): state.parameters.append(DateTimeVariable(expr)) return "?"@compile.when(date)def compile_date(compile, state, expr): state.parameters.append(DateVariable(expr)) return "?"@compile.when(time)def compile_time(compile, state, expr): state.parameters.append(TimeVariable(expr)) return "?"@compile.when(timedelta)def compile_timedelta(compile, state, expr): state.parameters.append(TimeDeltaVariable(expr)) return "?"@compile.when(type(None))def compile_none(compile, state, expr): return "NULL"@compile_python.when(str, unicode, bool, int, long, float, datetime, date, time, timedelta, type(None))def compile_python_builtin(compile, state, expr): return repr(expr)@compile.when(Variable)def compile_variable(compile, state, variable): state.parameters.append(variable) return "?"@compile_python.when(Variable)def compile_python_variable(compile, state, variable): return repr(variable.get())class SQLToken(str): """Marker for strings the should be considered as a single SQL token. In the future, these strings will be quoted, when needed. """@compile.when(SQLToken)def compile_str(compile, state, expr): return expr# --------------------------------------------------------------------# Base classes for expressionsMAX_PRECEDENCE = 1000class Expr(LazyValue): pass@compile_python.when(Expr)def compile_python_unsupported(compile, state, expr): raise CompileError("Can't compile python expressions with %r" % type(expr))class Comparable(object): def __eq__(self, other): if other is not None and not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Eq(self, other) def __ne__(self, other): if other is not None and not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Ne(self, other) def __gt__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Gt(self, other) def __ge__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Ge(self, other) def __lt__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Lt(self, other) def __le__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Le(self, other) def __rshift__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return RShift(self, other) def __lshift__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return LShift(self, other) def __and__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return And(self, other) def __or__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Or(self, other) def __add__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Add(self, other) def __sub__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Sub(self, other) def __mul__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Mul(self, other) def __div__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Div(self, other) def __mod__(self, other): if not isinstance(other, (Expr, Variable)): other = getattr(self, "variable_factory", Variable)(value=other) return Mod(self, other) def is_in(self, others): if not isinstance(others, Expr): if not others: return None others = list(others) variable_factory = getattr(self, "variable_factory", Variable)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -