diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-07-24 17:51:01 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-07-24 17:51:01 -0400 |
| commit | 8a483dbf38168ff43ca0652229b1d46afb23235d (patch) | |
| tree | 0707d1e1f3912bda8262e32bd1e4e008a7c67b88 /lib/sqlalchemy/sql/visitors.py | |
| parent | 9867086943d60e347695930dd7f442f9e95e4577 (diff) | |
| download | sqlalchemy-8a483dbf38168ff43ca0652229b1d46afb23235d.tar.gz | |
- rewrite cloned_traverse() and replacement_traverse() to use a straight
recursive descent with clone() + _copy_internals(). This is essentially
what it was doing anyway with lots of unnecessary steps.
Fix Alias() to honor the given clone() function which may have been the
reason the traversal hadn't been fixed sooner. Alias._copy_internals()
will specifically skip an alias of a Table
as a more specific form of what it was doing before. This may need to
be further improved such that ClauseAdapter or replacement_traverse()
send it some specific hints what not to dig into; **kw has been added
to all _copy_internals() to support this. replacement/clone traversal
is at least clear now.
- apply new no_replacement_traverse annotation to join created by
_create_joins(), fixes [ticket:2195]
- can replace orm.query "_halt_adapt" with "no_replacement_traverse"
Diffstat (limited to 'lib/sqlalchemy/sql/visitors.py')
| -rw-r--r-- | lib/sqlalchemy/sql/visitors.py | 80 |
1 files changed, 38 insertions, 42 deletions
diff --git a/lib/sqlalchemy/sql/visitors.py b/lib/sqlalchemy/sql/visitors.py index 0c6be97d7..b94f07f58 100644 --- a/lib/sqlalchemy/sql/visitors.py +++ b/lib/sqlalchemy/sql/visitors.py @@ -19,7 +19,7 @@ use a non-visitor traversal system. For many examples of how the visit system is used, see the sqlalchemy.sql.util and the sqlalchemy.sql.compiler modules. For an introduction to clause adaption, see -http://techspot.zzzeek.org/?p=19 . +http://techspot.zzzeek.org/2008/01/23/expression-transformations/ """ @@ -212,55 +212,51 @@ def traverse_depthfirst(obj, opts, visitors): return traverse_using(iterate_depthfirst(obj, opts), obj, visitors) def cloned_traverse(obj, opts, visitors): - """clone the given expression structure, allowing modifications by visitors.""" + """clone the given expression structure, allowing + modifications by visitors.""" cloned = util.column_dict() + stop_on = util.column_set(opts.get('stop_on', [])) - def clone(element): - if element not in cloned: - cloned[element] = element._clone() - return cloned[element] - - obj = clone(obj) - stack = [obj] - - while stack: - t = stack.pop() - if t in cloned: - continue - t._copy_internals(clone=clone) - - meth = visitors.get(t.__visit_name__, None) - if meth: - meth(t) - - for c in t.get_children(**opts): - stack.append(c) + def clone(elem): + if elem in stop_on: + return elem + else: + if elem not in cloned: + cloned[elem] = newelem = elem._clone() + newelem._copy_internals(clone=clone) + meth = visitors.get(newelem.__visit_name__, None) + if meth: + meth(newelem) + return cloned[elem] + + if obj is not None: + obj = clone(obj) return obj + def replacement_traverse(obj, opts, replace): - """clone the given expression structure, allowing element replacement by a given replacement function.""" + """clone the given expression structure, allowing element + replacement by a given replacement function.""" cloned = util.column_dict() stop_on = util.column_set(opts.get('stop_on', [])) - def clone(element): - newelem = replace(element) - if newelem is not None: - stop_on.add(newelem) - return newelem - - if element not in cloned: - cloned[element] = element._clone() - return cloned[element] - - obj = clone(obj) - stack = [obj] - while stack: - t = stack.pop() - if t in stop_on: - continue - t._copy_internals(clone=clone) - for c in t.get_children(**opts): - stack.append(c) + def clone(elem, **kw): + if elem in stop_on or \ + 'no_replacement_traverse' in elem._annotations: + return elem + else: + newelem = replace(elem) + if newelem is not None: + stop_on.add(newelem) + return newelem + else: + if elem not in cloned: + cloned[elem] = newelem = elem._clone() + newelem._copy_internals(clone=clone, **kw) + return cloned[elem] + + if obj is not None: + obj = clone(obj, **opts) return obj |
