summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/util.py
diff options
context:
space:
mode:
authorJason Kirtland <jek@discorporate.us>2008-02-14 20:02:10 +0000
committerJason Kirtland <jek@discorporate.us>2008-02-14 20:02:10 +0000
commit71e745e96b8c5be990b3dc949cb99310dd055609 (patch)
tree00c748e65e7e85e0231a1c7c504dec6cfcab8e87 /lib/sqlalchemy/sql/util.py
parent8dd5eb402ef65194af4c54a6fd33a181b7d5eaf0 (diff)
downloadsqlalchemy-71e745e96b8c5be990b3dc949cb99310dd055609.tar.gz
- Fixed a couple pyflakes, cleaned up imports & whitespace
Diffstat (limited to 'lib/sqlalchemy/sql/util.py')
-rw-r--r--lib/sqlalchemy/sql/util.py78
1 files changed, 38 insertions, 40 deletions
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index 2cd0a26fd..70a1dcc96 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -1,5 +1,5 @@
-from sqlalchemy import util, schema, topological
-from sqlalchemy.sql import expression, visitors, operators
+from sqlalchemy import exceptions, schema, topological, util
+from sqlalchemy.sql import expression, operators, visitors
from itertools import chain
"""Utility functions that build upon SQL and Schema constructs."""
@@ -30,16 +30,16 @@ def find_tables(clause, check_columns=False, include_aliases=False):
def visit_alias(alias):
tables.append(alias)
kwargs['visit_alias'] = visit_alias
-
+
if check_columns:
def visit_column(column):
tables.append(column.table)
kwargs['visit_column'] = visit_column
-
+
def visit_table(table):
tables.append(table)
kwargs['visit_table'] = visit_table
-
+
visitors.traverse(clause, traverse_options= {'column_collections':False}, **kwargs)
return tables
@@ -49,26 +49,26 @@ def find_columns(clause):
cols.add(col)
visitors.traverse(clause, visit_column=visit_column)
return cols
-
-
+
+
def reduce_columns(columns, *clauses):
"""given a list of columns, return a 'reduced' set based on natural equivalents.
the set is reduced to the smallest list of columns which have no natural
equivalent present in the list. A "natural equivalent" means that two columns
will ultimately represent the same value because they are related by a foreign key.
-
+
\*clauses is an optional list of join clauses which will be traversed
to further identify columns that are "equivalent".
-
+
This function is primarily used to determine the most minimal "primary key"
from a selectable, by reducing the set of primary key columns present
in the the selectable to just those that are not repeated.
-
+
"""
-
+
columns = util.OrderedSet(columns)
-
+
omit = util.Set()
for col in columns:
for fk in col.foreign_keys:
@@ -78,7 +78,7 @@ def reduce_columns(columns, *clauses):
if fk.column.shares_lineage(c):
omit.add(col)
break
-
+
if clauses:
def visit_binary(binary):
if binary.operator == operators.eq:
@@ -90,7 +90,7 @@ def reduce_columns(columns, *clauses):
break
for clause in clauses:
visitors.traverse(clause, visit_binary=visit_binary)
-
+
return expression.ColumnSet(columns.difference(omit))
def row_adapter(from_, to, equivalent_columns=None):
@@ -133,7 +133,7 @@ def row_adapter(from_, to, equivalent_columns=None):
return map.keys()
AliasedRow.map = map
return AliasedRow
-
+
class ColumnsInClause(visitors.ClauseVisitor):
"""Given a selectable, visit clauses and determine if any columns
from the clause are in the selectable.
@@ -149,16 +149,16 @@ class ColumnsInClause(visitors.ClauseVisitor):
class AbstractClauseProcessor(object):
"""Traverse and copy a ClauseElement, replacing selected elements based on rules.
-
+
This class implements its own visit-and-copy strategy but maintains the
same public interface as visitors.ClauseVisitor.
"""
-
+
__traverse_options__ = {'column_collections':False}
-
+
def __init__(self, stop_on=None):
self.stop_on = stop_on
-
+
def convert_element(self, elem):
"""Define the *conversion* method for this ``AbstractClauseProcessor``."""
@@ -166,14 +166,14 @@ class AbstractClauseProcessor(object):
def chain(self, visitor):
# chaining AbstractClauseProcessor and other ClauseVisitor
- # objects separately. All the ACP objects are chained on
+ # objects separately. All the ACP objects are chained on
# their convert_element() method whereas regular visitors
# chain on their visit_XXX methods.
if isinstance(visitor, AbstractClauseProcessor):
attr = '_next_acp'
else:
attr = '_next'
-
+
tail = self
while getattr(tail, attr, None) is not None:
tail = getattr(tail, attr)
@@ -182,7 +182,7 @@ class AbstractClauseProcessor(object):
def copy_and_process(self, list_):
"""Copy the given list to a new list, with each element traversed individually."""
-
+
list_ = list(list_)
stop_on = util.Set(self.stop_on or [])
cloned = {}
@@ -198,44 +198,44 @@ class AbstractClauseProcessor(object):
stop_on.add(newelem)
return newelem
v = getattr(v, '_next_acp', None)
-
+
if elem not in cloned:
# the full traversal will only make a clone of a particular element
# once.
cloned[elem] = elem._clone()
return cloned[elem]
-
+
def traverse(self, elem, clone=True):
if not clone:
raise exceptions.ArgumentError("AbstractClauseProcessor 'clone' argument must be True")
-
+
return self._traverse(elem, util.Set(self.stop_on or []), {}, _clone_toplevel=True)
-
+
def _traverse(self, elem, stop_on, cloned, _clone_toplevel=False):
if elem in stop_on:
return elem
-
+
if _clone_toplevel:
elem = self._convert_element(elem, stop_on, cloned)
if elem in stop_on:
return elem
-
+
def clone(element):
return self._convert_element(element, stop_on, cloned)
elem._copy_internals(clone=clone)
-
+
v = getattr(self, '_next', None)
while v is not None:
meth = getattr(v, "visit_%s" % elem.__visit_name__, None)
if meth:
meth(elem)
v = getattr(v, '_next', None)
-
+
for e in elem.get_children(**self.__traverse_options__):
if e not in stop_on:
self._traverse(e, stop_on, cloned)
return elem
-
+
class ClauseAdapter(AbstractClauseProcessor):
"""Given a clause (like as in a WHERE criterion), locate columns
which are embedded within a given selectable, and changes those
@@ -273,23 +273,23 @@ class ClauseAdapter(AbstractClauseProcessor):
def copy_and_chain(self, adapter):
"""create a copy of this adapter and chain to the given adapter.
-
+
currently this adapter must be unchained to start, raises
- an exception if it's already chained.
-
+ an exception if it's already chained.
+
Does not modify the given adapter.
"""
-
+
if adapter is None:
return self
-
+
if hasattr(self, '_next_acp') or hasattr(self, '_next'):
raise NotImplementedError("Can't chain_to on an already chained ClauseAdapter (yet)")
-
+
ca = ClauseAdapter(self.selectable, self.include, self.exclude, self.equivalents)
ca._next_acp = adapter
return ca
-
+
def convert_element(self, col):
if isinstance(col, expression.FromClause):
if self.selectable.is_derived_from(col):
@@ -309,5 +309,3 @@ class ClauseAdapter(AbstractClauseProcessor):
if newcol:
return newcol
return newcol
-
-