def __init__(self, regexp, Result_Type=TFL.Record, **converters): self.__super.__init__(Result_Type=Result_Type) self.regexp = rex = TFL.Regexp(regexp) self._converters = conv = {} for match in self.field_pat.search_iter(rex._pattern.pattern): name = match.group("name") conv [name] = \ ( converters.get (name) or converters.get ("default_converter", str) )
class _Base_ (TFL.Meta.Object) : """Base class for SAW Q_Result classes""" _distinct = False _order_by_cached = None _sa_query_ob = None def __init__ (self, session = None) : self.session = session self.bvar_man = Q.BVAR_Man () self._joined = set () self._order_bys = [] self.polymorphic = False # end def __init__ @TFL.Meta.Once_Property def QX (self) : from _MOM._DBW._SAW import QX return QX # end def QX @property def sa_query (self) : result = self._sa_query_ob if result is None : result = self._sa_query obs = self._order_by if obs : result = result.order_by (* obs) if self._distinct : ### For `SELECT DISTINCT`, PostgreSQL requires ORDER BY ### expressions to appear in select list ### ### `_SAW_ORIGINAL` is the expression **without** `DESC`, ### if any for ob in obs : cx = getattr (ob, "_SAW_ORIGINAL", ob) result.append_column (cx) self._sa_query_ob = result return result # end def sa_query @property def sa_query_count (self) : result = SA.sql.select \ ( [SA.func.count ("*").label ("count")] , from_obj = self._sa_query.alias ("__count__") ) return result # end def sa_query_count @property def session (self) : return self.__dict__ ["session"] # end def session @session.setter def session (self, value) : old_value = self.__dict__.get ("session") if old_value is None or old_value is value : self.__dict__ ["session"] = value else : raise AttributeError \ ("Cannot change session from %s to %s" % (old_value, value)) # end def session @property def _order_by (self) : result = self._order_by_cached if result is None : result = self._order_by_cached = [] for obs in reversed (self._order_bys) : result.extend (obs) return result # end def _order_by def all (self) : return list (self) # end def all def attr (self, getter, allow_duplicates = False) : return self._Attr_ (self, getter, allow_duplicates = allow_duplicates) # end def attr def attrs (self, * getters, ** kw) : if not getters : raise TypeError \ ( "%s.attrs() requires at least one argument" % self.__class__.__name__ ) return self._Attrs_ (self, getters, ** kw) # end def attrs def bind (self, _session = None, ** bindings) : result = self._clone () result.bvar_man.bind (** bindings) if _session is not None : result.session = _session return result # end def bind def count (self) : q_result = self.session and self.session.q_cache.get (self) if q_result is not None : return len (q_result) else : qr = self._execute (self.session, self.sa_query_count) result = int (qr.scalar ()) return result # end def count def distinct (self) : result = self._clone () result._sa_query = result._sa_query.distinct () result._distinct = True return result # end def distinct def filter (self, * criteria, ** kw) : if kw : crits = list (criteria) crits.extend (getattr (Q, k) == v for k, v in pyk.iteritems (kw)) criteria = tuple (crits) assert criteria result = self._clone () sa_query = result._sa_query where_exp, join = result._get_filters (criteria, sa_query) if join is not None : sa_query = sa_query.select_from (join) result._sa_query = sa_query.where (where_exp) return result # end def filter def first (self) : try : ### cache ??? `self.limit` creates a new Q_Result return TFL.first (self.limit (1)) except IndexError : return None # end def first def formatted (self) : return formatted (self) # end def formatted def formatted_xqpi (self) : """Return formatted query with query parameters expanded inline.""" return formatted_xqpi (self) # end def formatted_xqpi def group_by (self, * columns) : result = self._clone () result._sa_query = result._sa_query.group_by \ (* self._get_group_by (columns)) return result # end def group_by def limit (self, limit) : result = self._clone () result._sa_query = result._sa_query.limit (limit) return result # end def limit def offset (self, offset) : result = self._clone () result._sa_query = result._sa_query.offset (offset) return result # end def offset def one (self) : result = tuple (self.limit (2)) count = len (result) if count != 1 : raise IndexError \ ( "Query result contains %s entries" % (self.count () if count else 0, ) ) return result [0] # end def one def order_by (self, * criteria) : result = self._clone () sa_query = result._sa_query obs, join = result._get_order_by (criteria, sa_query) if join is not None : result._sa_query = sa_query.select_from (join) ### `obs` is a list; use `append` here to be able to later reverse ### `_order_bys` while keeping the sequence of items in `obs` ### this allows later `order_by` calls to dominate earlier ones result._order_bys.append (obs) return result # end def order_by def row_iter (self, session, ** kw) : result = self._execute (session, self.sa_query, ** kw) try : yield from result finally : result.close () # end def row_iter def slice (self, start, stop = None) : result = self.offset (start) if stop is not None : result_sa_query = result._sa_query.limit (stop - start) return result # end def slice def _clone (self, ** kw) : cls = self.__class__ result = cls.__new__ (cls) result.__dict__.update \ ( self.__dict__ , bvar_man = self.bvar_man.clone () , _joined = set (self._joined) , _order_bys = list (self._order_bys) , _order_by_cached = None , _sa_query_ob = None , ** kw ) return result # end def _clone def _execute (self, session, q, ** kw) : qkw = dict (self.bvar_man.bindings, ** kw) return session.connection.execute (q, ** qkw) # end def _execute def _extend_join (self, join, joined, jxs) : for a_join in jxs : if a_join.key not in joined : joined.add (a_join.key) join = a_join (join) return join # end def _extend_join def _get_attr_exprs (self, axes, sa_query) : axes = list \ ( (getattr (Q, ax) if isinstance (ax, pyk.string_types) else ax) for ax in axes ) return self._get_xs (axes, sa_query, "XS_ATTR", TFL.Method.extend) # end def _get_attr_exprs def _get_filters (self, criteria, sa_query) : wxs, join = self._get_xs \ (criteria, sa_query, "XS_FILTER", TFL.Method.append) wxs = wxs [0] if len (wxs) == 1 else SA.expression.and_ (* wxs) return wxs, join # end def _get_filters def _get_group_by (self, columns) : ETW = getattr (self, "ETW", None) if ETW is None : yield from columns else : ETW_Q = ETW.QC for c in columns : if isinstance (c, pyk.string_types) : c = getattr (Q, c) yield c (ETW_Q) # end def _get_group_by def _get_order_by (self, criteria, sa_query) : return self._get_xs \ ( self.QX.fixed_order_by (criteria), sa_query , "XS_ORDER_BY", TFL.Method.extend ) # end def _get_order_by def _get_xs (self, criteria, sa_query, xs_name, adder) : QX = self.QX joined = self._joined join = orig_join = sa_query.froms [-1] xs = [] for c in criteria : if isinstance (c, SA.Operators) : xs.append (c) elif c is not None : qx = QX.Mapper (self) (c) x = getattr (qx, xs_name) join = self._extend_join (join, joined, qx.JOINS) adder (xs, x) return xs, None if join is orig_join else join # end def _get_xs def __bool__ (self) : result = self.session.q_cache.get (self) ### XXX cache return bool (result or self.first () is not None) # end def __bool__ def __getslice__ (self, start, stop) : return self.slice (start, stop) # end def __getslice__ def __iter__ (self) : session = self.session result = session.q_cache.get (self) if result is None : result = [] for row in self.row_iter (session) : element = self._from_row (row) result.append (element) yield element session.q_cache [self] = result else : yield from result # end def __iter__ _fix_by = TFL.Re_Replacer ("\s+((?:GROUP|ORDER) BY)", "\n \\1") _fix_join = TFL.Re_Replacer ("((?:[A-Z]+ )*JOIN)", "\n \\1") _fix_where = TFL.Re_Replacer ("((?:\s+)*(?:AND|OR)\s+)", "\n \\1") _select_pat = TFL.Regexp ("(SELECT(?: DISTINCT)?) .*,") _select_sep = TFL.Regexp ("(?<!\)), ") def __repr__ (self) : def q_parts (self) : _fix_by = self._fix_by _fix_join = self._fix_join _fix_where = self._fix_where _select_pat = self._select_pat _select_split = self._select_sep.split sep_1 = "\n " sep_n = "," + sep_1 for p in str (self.sa_query).split ("\n") : p = p.strip () if _select_pat.match (p) : h = _select_pat.group (1) t = p [_select_pat.end (1):].strip () ts = _select_split (t) p = sep_1.join ((h, sep_n.join (sorted (ts)))) elif "JOIN" in p : px = _fix_join (p) ps = list (x.rstrip () for x in px.split ("\n")) p = "\n".join (ps) elif "WHERE" in p : px = _fix_where (p) ps = list (x.rstrip () for x in px.split ("\n")) p = "\n".join (ps) p = _fix_by (p) yield p sa_query = "\n ".join (q_parts (self)) return "SQL: %s" % (sa_query, )
def __init__(cls, name, bases, dct): cls.__m_super.__init__(name, bases, dct) cls.epk_split_pat = TFL.Regexp(cls.epk_split_characters)