diff options
Diffstat (limited to 'lib/sqlalchemy/sql')
| -rw-r--r-- | lib/sqlalchemy/sql/expression.py | 17 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/util.py | 29 |
2 files changed, 42 insertions, 4 deletions
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 85f229ba0..5206dc5fa 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -1002,6 +1002,11 @@ class ClauseElement(Visitable): yield f f = getattr(f, '_is_clone_of', None) + def __getstate__(self): + d = self.__dict__.copy() + d.pop('_is_clone_of', None) + return d + def _get_from_objects(self, **modifiers): """Return objects represented in this ``ClauseElement`` that should be added to the ``FROM`` list of a query, when this @@ -1959,7 +1964,17 @@ class _BindParamClause(ColumnElement): """ return isinstance(other, _BindParamClause) and other.type.__class__ == self.type.__class__ - + + def __getstate__(self): + """execute a deferred value for serialization purposes.""" + + d = self.__dict__.copy() + v = self.value + if callable(v): + v = v() + d['value'] = v + return d + def __repr__(self): return "_BindParamClause(%s, %s, type_=%s)" % (repr(self.key), repr(self.value), repr(self.type)) 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) |
