1# sql/visitors.py
2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8"""Visitor/traversal interface and library functions.
9
10SQLAlchemy schema and expression constructs rely on a Python-centric
11version of the classic "visitor" pattern as the primary way in which
12they apply functionality. The most common use of this pattern
13is statement compilation, where individual expression classes match
14up to rendering methods that produce a string result. Beyond this,
15the visitor system is also used to inspect expressions for various
16information and patterns, as well as for the purposes of applying
17transformations to expressions.
18
19Examples of how the visit system is used can be seen in the source code
20of for example the ``sqlalchemy.sql.util`` and the ``sqlalchemy.sql.compiler``
21modules. Some background on clause adaption is also at
22https://techspot.zzzeek.org/2008/01/23/expression-transformations/ .
23
24"""
25
26from collections import deque
27import itertools
28import operator
29
30from .. import exc
31from .. import util
32from ..util import langhelpers
33from ..util import symbol
34
35__all__ = [
36 "iterate",
37 "traverse_using",
38 "traverse",
39 "cloned_traverse",
40 "replacement_traverse",
41 "Traversible",
42 "TraversibleType",
43 "ExternalTraversal",
44 "InternalTraversal",
45]
46
47
48def _generate_compiler_dispatch(cls):
49 """Generate a _compiler_dispatch() external traversal on classes with a
50 __visit_name__ attribute.
51
52 """
53 visit_name = cls.__visit_name__
54
55 if "_compiler_dispatch" in cls.__dict__:
56 # class has a fixed _compiler_dispatch() method.
57 # copy it to "original" so that we can get it back if
58 # sqlalchemy.ext.compiles overrides it.
59 cls._original_compiler_dispatch = cls._compiler_dispatch
60 return
61
62 if not isinstance(visit_name, util.compat.string_types):
63 raise exc.InvalidRequestError(
64 "__visit_name__ on class %s must be a string at the class level"
65 % cls.__name__
66 )
67
68 name = "visit_%s" % visit_name
69 getter = operator.attrgetter(name)
70
71 def _compiler_dispatch(self, visitor, **kw):
72 """Look for an attribute named "visit_<visit_name>" on the
73 visitor, and call it with the same kw params.
74
75 """
76 try:
77 meth = getter(visitor)
78 except AttributeError as err:
79 return visitor.visit_unsupported_compilation(self, err, **kw)
80
81 else:
82 return meth(self, **kw)
83
84 cls._compiler_dispatch = (
85 cls._original_compiler_dispatch
86 ) = _compiler_dispatch
87
88
89class TraversibleType(type):
90 """Metaclass which assigns dispatch attributes to various kinds of
91 "visitable" classes.
92
93 Attributes include:
94
95 * The ``_compiler_dispatch`` method, corresponding to ``__visit_name__``.
96 This is called "external traversal" because the caller of each visit()
97 method is responsible for sub-traversing the inner elements of each
98 object. This is appropriate for string compilers and other traversals
99 that need to call upon the inner elements in a specific pattern.
100
101 * internal traversal collections ``_children_traversal``,
102 ``_cache_key_traversal``, ``_copy_internals_traversal``, generated from
103 an optional ``_traverse_internals`` collection of symbols which comes
104 from the :class:`.InternalTraversal` list of symbols. This is called
105 "internal traversal" MARKMARK
106
107 """
108
109 def __init__(cls, clsname, bases, clsdict):
110 if clsname != "Traversible":
111 if "__visit_name__" in clsdict:
112 _generate_compiler_dispatch(cls)
113
114 super(TraversibleType, cls).__init__(clsname, bases, clsdict)
115
116
117class Traversible(util.with_metaclass(TraversibleType)):
118 """Base class for visitable objects, applies the
119 :class:`.visitors.TraversibleType` metaclass.
120
121 """
122
123 def __class_getitem__(cls, key):
124 # allow generic classes in py3.9+
125 return cls
126
127 @util.preload_module("sqlalchemy.sql.traversals")
128 def get_children(self, omit_attrs=(), **kw):
129 r"""Return immediate child :class:`.visitors.Traversible`
130 elements of this :class:`.visitors.Traversible`.
131
132 This is used for visit traversal.
133
134 \**kw may contain flags that change the collection that is
135 returned, for example to return a subset of items in order to
136 cut down on larger traversals, or to return child items from a
137 different context (such as schema-level collections instead of
138 clause-level).
139
140 """
141
142 traversals = util.preloaded.sql_traversals
143
144 try:
145 traverse_internals = self._traverse_internals
146 except AttributeError:
147 # user-defined classes may not have a _traverse_internals
148 return []
149
150 dispatch = traversals._get_children.run_generated_dispatch
151 return itertools.chain.from_iterable(
152 meth(obj, **kw)
153 for attrname, obj, meth in dispatch(
154 self, traverse_internals, "_generated_get_children_traversal"
155 )
156 if attrname not in omit_attrs and obj is not None
157 )
158
159
160class _InternalTraversalType(type):
161 def __init__(cls, clsname, bases, clsdict):
162 if cls.__name__ in ("InternalTraversal", "ExtendedInternalTraversal"):
163 lookup = {}
164 for key, sym in clsdict.items():
165 if key.startswith("dp_"):
166 visit_key = key.replace("dp_", "visit_")
167 sym_name = sym.name
168 assert sym_name not in lookup, sym_name
169 lookup[sym] = lookup[sym_name] = visit_key
170 if hasattr(cls, "_dispatch_lookup"):
171 lookup.update(cls._dispatch_lookup)
172 cls._dispatch_lookup = lookup
173
174 super(_InternalTraversalType, cls).__init__(clsname, bases, clsdict)
175
176
177def _generate_dispatcher(visitor, internal_dispatch, method_name):
178 names = []
179 for attrname, visit_sym in internal_dispatch:
180 meth = visitor.dispatch(visit_sym)
181 if meth:
182 visit_name = ExtendedInternalTraversal._dispatch_lookup[visit_sym]
183 names.append((attrname, visit_name))
184
185 code = (
186 (" return [\n")
187 + (
188 ", \n".join(
189 " (%r, self.%s, visitor.%s)"
190 % (attrname, attrname, visit_name)
191 for attrname, visit_name in names
192 )
193 )
194 + ("\n ]\n")
195 )
196 meth_text = ("def %s(self, visitor):\n" % method_name) + code + "\n"
197 # print(meth_text)
198 return langhelpers._exec_code_in_env(meth_text, {}, method_name)
199
200
201class InternalTraversal(util.with_metaclass(_InternalTraversalType, object)):
202 r"""Defines visitor symbols used for internal traversal.
203
204 The :class:`.InternalTraversal` class is used in two ways. One is that
205 it can serve as the superclass for an object that implements the
206 various visit methods of the class. The other is that the symbols
207 themselves of :class:`.InternalTraversal` are used within
208 the ``_traverse_internals`` collection. Such as, the :class:`.Case`
209 object defines ``_traverse_internals`` as ::
210
211 _traverse_internals = [
212 ("value", InternalTraversal.dp_clauseelement),
213 ("whens", InternalTraversal.dp_clauseelement_tuples),
214 ("else_", InternalTraversal.dp_clauseelement),
215 ]
216
217 Above, the :class:`.Case` class indicates its internal state as the
218 attributes named ``value``, ``whens``, and ``else_``. They each
219 link to an :class:`.InternalTraversal` method which indicates the type
220 of datastructure referred towards.
221
222 Using the ``_traverse_internals`` structure, objects of type
223 :class:`.InternalTraversible` will have the following methods automatically
224 implemented:
225
226 * :meth:`.Traversible.get_children`
227
228 * :meth:`.Traversible._copy_internals`
229
230 * :meth:`.Traversible._gen_cache_key`
231
232 Subclasses can also implement these methods directly, particularly for the
233 :meth:`.Traversible._copy_internals` method, when special steps
234 are needed.
235
236 .. versionadded:: 1.4
237
238 """
239
240 def dispatch(self, visit_symbol):
241 """Given a method from :class:`.InternalTraversal`, return the
242 corresponding method on a subclass.
243
244 """
245 name = self._dispatch_lookup[visit_symbol]
246 return getattr(self, name, None)
247
248 def run_generated_dispatch(
249 self, target, internal_dispatch, generate_dispatcher_name
250 ):
251 try:
252 dispatcher = target.__class__.__dict__[generate_dispatcher_name]
253 except KeyError:
254 # most of the dispatchers are generated up front
255 # in sqlalchemy/sql/__init__.py ->
256 # traversals.py-> _preconfigure_traversals().
257 # this block will generate any remaining dispatchers.
258 dispatcher = self.generate_dispatch(
259 target.__class__, internal_dispatch, generate_dispatcher_name
260 )
261 return dispatcher(target, self)
262
263 def generate_dispatch(
264 self, target_cls, internal_dispatch, generate_dispatcher_name
265 ):
266 dispatcher = _generate_dispatcher(
267 self, internal_dispatch, generate_dispatcher_name
268 )
269 # assert isinstance(target_cls, type)
270 setattr(target_cls, generate_dispatcher_name, dispatcher)
271 return dispatcher
272
273 dp_has_cache_key = symbol("HC")
274 """Visit a :class:`.HasCacheKey` object."""
275
276 dp_has_cache_key_list = symbol("HL")
277 """Visit a list of :class:`.HasCacheKey` objects."""
278
279 dp_clauseelement = symbol("CE")
280 """Visit a :class:`_expression.ClauseElement` object."""
281
282 dp_fromclause_canonical_column_collection = symbol("FC")
283 """Visit a :class:`_expression.FromClause` object in the context of the
284 ``columns`` attribute.
285
286 The column collection is "canonical", meaning it is the originally
287 defined location of the :class:`.ColumnClause` objects. Right now
288 this means that the object being visited is a
289 :class:`_expression.TableClause`
290 or :class:`_schema.Table` object only.
291
292 """
293
294 dp_clauseelement_tuples = symbol("CTS")
295 """Visit a list of tuples which contain :class:`_expression.ClauseElement`
296 objects.
297
298 """
299
300 dp_clauseelement_list = symbol("CL")
301 """Visit a list of :class:`_expression.ClauseElement` objects.
302
303 """
304
305 dp_clauseelement_tuple = symbol("CT")
306 """Visit a tuple of :class:`_expression.ClauseElement` objects.
307
308 """
309
310 dp_executable_options = symbol("EO")
311
312 dp_with_context_options = symbol("WC")
313
314 dp_fromclause_ordered_set = symbol("CO")
315 """Visit an ordered set of :class:`_expression.FromClause` objects. """
316
317 dp_string = symbol("S")
318 """Visit a plain string value.
319
320 Examples include table and column names, bound parameter keys, special
321 keywords such as "UNION", "UNION ALL".
322
323 The string value is considered to be significant for cache key
324 generation.
325
326 """
327
328 dp_string_list = symbol("SL")
329 """Visit a list of strings."""
330
331 dp_anon_name = symbol("AN")
332 """Visit a potentially "anonymized" string value.
333
334 The string value is considered to be significant for cache key
335 generation.
336
337 """
338
339 dp_boolean = symbol("B")
340 """Visit a boolean value.
341
342 The boolean value is considered to be significant for cache key
343 generation.
344
345 """
346
347 dp_operator = symbol("O")
348 """Visit an operator.
349
350 The operator is a function from the :mod:`sqlalchemy.sql.operators`
351 module.
352
353 The operator value is considered to be significant for cache key
354 generation.
355
356 """
357
358 dp_type = symbol("T")
359 """Visit a :class:`.TypeEngine` object
360
361 The type object is considered to be significant for cache key
362 generation.
363
364 """
365
366 dp_plain_dict = symbol("PD")
367 """Visit a dictionary with string keys.
368
369 The keys of the dictionary should be strings, the values should
370 be immutable and hashable. The dictionary is considered to be
371 significant for cache key generation.
372
373 """
374
375 dp_dialect_options = symbol("DO")
376 """Visit a dialect options structure."""
377
378 dp_string_clauseelement_dict = symbol("CD")
379 """Visit a dictionary of string keys to :class:`_expression.ClauseElement`
380 objects.
381
382 """
383
384 dp_string_multi_dict = symbol("MD")
385 """Visit a dictionary of string keys to values which may either be
386 plain immutable/hashable or :class:`.HasCacheKey` objects.
387
388 """
389
390 dp_annotations_key = symbol("AK")
391 """Visit the _annotations_cache_key element.
392
393 This is a dictionary of additional information about a ClauseElement
394 that modifies its role. It should be included when comparing or caching
395 objects, however generating this key is relatively expensive. Visitors
396 should check the "_annotations" dict for non-None first before creating
397 this key.
398
399 """
400
401 dp_plain_obj = symbol("PO")
402 """Visit a plain python object.
403
404 The value should be immutable and hashable, such as an integer.
405 The value is considered to be significant for cache key generation.
406
407 """
408
409 dp_named_ddl_element = symbol("DD")
410 """Visit a simple named DDL element.
411
412 The current object used by this method is the :class:`.Sequence`.
413
414 The object is only considered to be important for cache key generation
415 as far as its name, but not any other aspects of it.
416
417 """
418
419 dp_prefix_sequence = symbol("PS")
420 """Visit the sequence represented by :class:`_expression.HasPrefixes`
421 or :class:`_expression.HasSuffixes`.
422
423 """
424
425 dp_table_hint_list = symbol("TH")
426 """Visit the ``_hints`` collection of a :class:`_expression.Select`
427 object.
428
429 """
430
431 dp_setup_join_tuple = symbol("SJ")
432
433 dp_memoized_select_entities = symbol("ME")
434
435 dp_statement_hint_list = symbol("SH")
436 """Visit the ``_statement_hints`` collection of a
437 :class:`_expression.Select`
438 object.
439
440 """
441
442 dp_unknown_structure = symbol("UK")
443 """Visit an unknown structure.
444
445 """
446
447 dp_dml_ordered_values = symbol("DML_OV")
448 """Visit the values() ordered tuple list of an
449 :class:`_expression.Update` object."""
450
451 dp_dml_values = symbol("DML_V")
452 """Visit the values() dictionary of a :class:`.ValuesBase`
453 (e.g. Insert or Update) object.
454
455 """
456
457 dp_dml_multi_values = symbol("DML_MV")
458 """Visit the values() multi-valued list of dictionaries of an
459 :class:`_expression.Insert` object.
460
461 """
462
463 dp_propagate_attrs = symbol("PA")
464 """Visit the propagate attrs dict. This hardcodes to the particular
465 elements we care about right now."""
466
467
468class ExtendedInternalTraversal(InternalTraversal):
469 """Defines additional symbols that are useful in caching applications.
470
471 Traversals for :class:`_expression.ClauseElement` objects only need to use
472 those symbols present in :class:`.InternalTraversal`. However, for
473 additional caching use cases within the ORM, symbols dealing with the
474 :class:`.HasCacheKey` class are added here.
475
476 """
477
478 dp_ignore = symbol("IG")
479 """Specify an object that should be ignored entirely.
480
481 This currently applies function call argument caching where some
482 arguments should not be considered to be part of a cache key.
483
484 """
485
486 dp_inspectable = symbol("IS")
487 """Visit an inspectable object where the return value is a
488 :class:`.HasCacheKey` object."""
489
490 dp_multi = symbol("M")
491 """Visit an object that may be a :class:`.HasCacheKey` or may be a
492 plain hashable object."""
493
494 dp_multi_list = symbol("MT")
495 """Visit a tuple containing elements that may be :class:`.HasCacheKey` or
496 may be a plain hashable object."""
497
498 dp_has_cache_key_tuples = symbol("HT")
499 """Visit a list of tuples which contain :class:`.HasCacheKey`
500 objects.
501
502 """
503
504 dp_inspectable_list = symbol("IL")
505 """Visit a list of inspectable objects which upon inspection are
506 HasCacheKey objects."""
507
508
509class ExternalTraversal(object):
510 """Base class for visitor objects which can traverse externally using
511 the :func:`.visitors.traverse` function.
512
513 Direct usage of the :func:`.visitors.traverse` function is usually
514 preferred.
515
516 """
517
518 __traverse_options__ = {}
519
520 def traverse_single(self, obj, **kw):
521 for v in self.visitor_iterator:
522 meth = getattr(v, "visit_%s" % obj.__visit_name__, None)
523 if meth:
524 return meth(obj, **kw)
525
526 def iterate(self, obj):
527 """Traverse the given expression structure, returning an iterator
528 of all elements.
529
530 """
531 return iterate(obj, self.__traverse_options__)
532
533 def traverse(self, obj):
534 """Traverse and visit the given expression structure."""
535
536 return traverse(obj, self.__traverse_options__, self._visitor_dict)
537
538 @util.memoized_property
539 def _visitor_dict(self):
540 visitors = {}
541
542 for name in dir(self):
543 if name.startswith("visit_"):
544 visitors[name[6:]] = getattr(self, name)
545 return visitors
546
547 @property
548 def visitor_iterator(self):
549 """Iterate through this visitor and each 'chained' visitor."""
550
551 v = self
552 while v:
553 yield v
554 v = getattr(v, "_next", None)
555
556 def chain(self, visitor):
557 """'Chain' an additional ClauseVisitor onto this ClauseVisitor.
558
559 The chained visitor will receive all visit events after this one.
560
561 """
562 tail = list(self.visitor_iterator)[-1]
563 tail._next = visitor
564 return self
565
566
567class CloningExternalTraversal(ExternalTraversal):
568 """Base class for visitor objects which can traverse using
569 the :func:`.visitors.cloned_traverse` function.
570
571 Direct usage of the :func:`.visitors.cloned_traverse` function is usually
572 preferred.
573
574
575 """
576
577 def copy_and_process(self, list_):
578 """Apply cloned traversal to the given list of elements, and return
579 the new list.
580
581 """
582 return [self.traverse(x) for x in list_]
583
584 def traverse(self, obj):
585 """Traverse and visit the given expression structure."""
586
587 return cloned_traverse(
588 obj, self.__traverse_options__, self._visitor_dict
589 )
590
591
592class ReplacingExternalTraversal(CloningExternalTraversal):
593 """Base class for visitor objects which can traverse using
594 the :func:`.visitors.replacement_traverse` function.
595
596 Direct usage of the :func:`.visitors.replacement_traverse` function is
597 usually preferred.
598
599 """
600
601 def replace(self, elem):
602 """Receive pre-copied elements during a cloning traversal.
603
604 If the method returns a new element, the element is used
605 instead of creating a simple copy of the element. Traversal
606 will halt on the newly returned element if it is re-encountered.
607 """
608 return None
609
610 def traverse(self, obj):
611 """Traverse and visit the given expression structure."""
612
613 def replace(elem):
614 for v in self.visitor_iterator:
615 e = v.replace(elem)
616 if e is not None:
617 return e
618
619 return replacement_traverse(obj, self.__traverse_options__, replace)
620
621
622# backwards compatibility
623Visitable = Traversible
624VisitableType = TraversibleType
625ClauseVisitor = ExternalTraversal
626CloningVisitor = CloningExternalTraversal
627ReplacingCloningVisitor = ReplacingExternalTraversal
628
629
630def iterate(obj, opts=util.immutabledict()):
631 r"""Traverse the given expression structure, returning an iterator.
632
633 Traversal is configured to be breadth-first.
634
635 The central API feature used by the :func:`.visitors.iterate`
636 function is the
637 :meth:`_expression.ClauseElement.get_children` method of
638 :class:`_expression.ClauseElement` objects. This method should return all
639 the :class:`_expression.ClauseElement` objects which are associated with a
640 particular :class:`_expression.ClauseElement` object. For example, a
641 :class:`.Case` structure will refer to a series of
642 :class:`_expression.ColumnElement` objects within its "whens" and "else\_"
643 member variables.
644
645 :param obj: :class:`_expression.ClauseElement` structure to be traversed
646
647 :param opts: dictionary of iteration options. This dictionary is usually
648 empty in modern usage.
649
650 """
651 yield obj
652 children = obj.get_children(**opts)
653
654 if not children:
655 return
656
657 stack = deque([children])
658 while stack:
659 t_iterator = stack.popleft()
660 for t in t_iterator:
661 yield t
662 stack.append(t.get_children(**opts))
663
664
665def traverse_using(iterator, obj, visitors):
666 """Visit the given expression structure using the given iterator of
667 objects.
668
669 :func:`.visitors.traverse_using` is usually called internally as the result
670 of the :func:`.visitors.traverse` function.
671
672 :param iterator: an iterable or sequence which will yield
673 :class:`_expression.ClauseElement`
674 structures; the iterator is assumed to be the
675 product of the :func:`.visitors.iterate` function.
676
677 :param obj: the :class:`_expression.ClauseElement`
678 that was used as the target of the
679 :func:`.iterate` function.
680
681 :param visitors: dictionary of visit functions. See :func:`.traverse`
682 for details on this dictionary.
683
684 .. seealso::
685
686 :func:`.traverse`
687
688
689 """
690 for target in iterator:
691 meth = visitors.get(target.__visit_name__, None)
692 if meth:
693 meth(target)
694 return obj
695
696
697def traverse(obj, opts, visitors):
698 """Traverse and visit the given expression structure using the default
699 iterator.
700
701 e.g.::
702
703 from sqlalchemy.sql import visitors
704
705 stmt = select(some_table).where(some_table.c.foo == 'bar')
706
707 def visit_bindparam(bind_param):
708 print("found bound value: %s" % bind_param.value)
709
710 visitors.traverse(stmt, {}, {"bindparam": visit_bindparam})
711
712 The iteration of objects uses the :func:`.visitors.iterate` function,
713 which does a breadth-first traversal using a stack.
714
715 :param obj: :class:`_expression.ClauseElement` structure to be traversed
716
717 :param opts: dictionary of iteration options. This dictionary is usually
718 empty in modern usage.
719
720 :param visitors: dictionary of visit functions. The dictionary should
721 have strings as keys, each of which would correspond to the
722 ``__visit_name__`` of a particular kind of SQL expression object, and
723 callable functions as values, each of which represents a visitor function
724 for that kind of object.
725
726 """
727 return traverse_using(iterate(obj, opts), obj, visitors)
728
729
730def cloned_traverse(obj, opts, visitors):
731 """Clone the given expression structure, allowing modifications by
732 visitors.
733
734 Traversal usage is the same as that of :func:`.visitors.traverse`.
735 The visitor functions present in the ``visitors`` dictionary may also
736 modify the internals of the given structure as the traversal proceeds.
737
738 The central API feature used by the :func:`.visitors.cloned_traverse`
739 and :func:`.visitors.replacement_traverse` functions, in addition to the
740 :meth:`_expression.ClauseElement.get_children`
741 function that is used to achieve
742 the iteration, is the :meth:`_expression.ClauseElement._copy_internals`
743 method.
744 For a :class:`_expression.ClauseElement`
745 structure to support cloning and replacement
746 traversals correctly, it needs to be able to pass a cloning function into
747 its internal members in order to make copies of them.
748
749 .. seealso::
750
751 :func:`.visitors.traverse`
752
753 :func:`.visitors.replacement_traverse`
754
755 """
756
757 cloned = {}
758 stop_on = set(opts.get("stop_on", []))
759
760 def deferred_copy_internals(obj):
761 return cloned_traverse(obj, opts, visitors)
762
763 def clone(elem, **kw):
764 if elem in stop_on:
765 return elem
766 else:
767 if id(elem) not in cloned:
768
769 if "replace" in kw:
770 newelem = kw["replace"](elem)
771 if newelem is not None:
772 cloned[id(elem)] = newelem
773 return newelem
774
775 cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw)
776 newelem._copy_internals(clone=clone, **kw)
777 meth = visitors.get(newelem.__visit_name__, None)
778 if meth:
779 meth(newelem)
780 return cloned[id(elem)]
781
782 if obj is not None:
783 obj = clone(
784 obj, deferred_copy_internals=deferred_copy_internals, **opts
785 )
786 clone = None # remove gc cycles
787 return obj
788
789
790def replacement_traverse(obj, opts, replace):
791 """Clone the given expression structure, allowing element
792 replacement by a given replacement function.
793
794 This function is very similar to the :func:`.visitors.cloned_traverse`
795 function, except instead of being passed a dictionary of visitors, all
796 elements are unconditionally passed into the given replace function.
797 The replace function then has the option to return an entirely new object
798 which will replace the one given. If it returns ``None``, then the object
799 is kept in place.
800
801 The difference in usage between :func:`.visitors.cloned_traverse` and
802 :func:`.visitors.replacement_traverse` is that in the former case, an
803 already-cloned object is passed to the visitor function, and the visitor
804 function can then manipulate the internal state of the object.
805 In the case of the latter, the visitor function should only return an
806 entirely different object, or do nothing.
807
808 The use case for :func:`.visitors.replacement_traverse` is that of
809 replacing a FROM clause inside of a SQL structure with a different one,
810 as is a common use case within the ORM.
811
812 """
813
814 cloned = {}
815 stop_on = {id(x) for x in opts.get("stop_on", [])}
816
817 def deferred_copy_internals(obj):
818 return replacement_traverse(obj, opts, replace)
819
820 def clone(elem, **kw):
821 if (
822 id(elem) in stop_on
823 or "no_replacement_traverse" in elem._annotations
824 ):
825 return elem
826 else:
827 newelem = replace(elem)
828 if newelem is not None:
829 stop_on.add(id(newelem))
830 return newelem
831 else:
832 # base "already seen" on id(), not hash, so that we don't
833 # replace an Annotated element with its non-annotated one, and
834 # vice versa
835 id_elem = id(elem)
836 if id_elem not in cloned:
837 if "replace" in kw:
838 newelem = kw["replace"](elem)
839 if newelem is not None:
840 cloned[id_elem] = newelem
841 return newelem
842
843 cloned[id_elem] = newelem = elem._clone(**kw)
844 newelem._copy_internals(clone=clone, **kw)
845 return cloned[id_elem]
846
847 if obj is not None:
848 obj = clone(
849 obj, deferred_copy_internals=deferred_copy_internals, **opts
850 )
851 clone = None # remove gc cycles
852 return obj