summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/util.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/sql/util.py')
-rw-r--r--lib/sqlalchemy/sql/util.py29
1 files changed, 26 insertions, 3 deletions
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index 2a510906b..d5f2417c2 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -121,6 +121,7 @@ def join_condition(a, b, ignore_nonexistent_tables=False):
else:
return sql.and_(*crit)
+
class Annotated(object):
"""clones a ClauseElement and applies an 'annotations' dictionary.
@@ -133,14 +134,17 @@ class Annotated(object):
hash value may be reused, causing conflicts.
"""
+
def __new__(cls, *args):
if not args:
+ # clone constructor
return object.__new__(cls)
else:
element, values = args
- return object.__new__(
- type.__new__(type, "Annotated%s" % element.__class__.__name__, (Annotated, element.__class__), {})
- )
+ # pull appropriate subclass from this module's
+ # namespace (see below for rationale)
+ cls = eval("Annotated%s" % element.__class__.__name__)
+ return object.__new__(cls)
def __init__(self, element, values):
# force FromClause to generate their internal
@@ -180,6 +184,17 @@ class Annotated(object):
def __cmp__(self, other):
return cmp(hash(self.__element), hash(other))
+# hard-generate Annotated subclasses. this technique
+# is used instead of on-the-fly types (i.e. type.__new__())
+# so that the resulting objects are pickleable.
+from sqlalchemy.sql import expression
+for cls in expression.__dict__.values() + [schema.Column, schema.Table]:
+ if isinstance(cls, type) and issubclass(cls, expression.ClauseElement):
+ exec "class Annotated%s(Annotated, cls):\n" \
+ " __visit_name__ = cls.__visit_name__\n"\
+ " pass" % (cls.__name__, ) in locals()
+
+
def _deep_annotate(element, annotations, exclude=None):
"""Deep copy the given ClauseElement, annotating each element with the given annotations dictionary.
@@ -495,3 +510,11 @@ class ColumnAdapter(ClauseAdapter):
def adapted_row(self, row):
return AliasedRow(row, self.columns)
+ def __getstate__(self):
+ d = self.__dict__.copy()
+ del d['columns']
+ return d
+
+ def __setstate__(self, state):
+ self.__dict__.update(state)
+ self.columns = util.PopulateDict(self._locate_col)