1# sql/visitors.py
2# Copyright (C) 2005-2026 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
10from __future__ import annotations
11
12from collections import deque
13from enum import Enum
14import itertools
15import operator
16import typing
17from typing import Any
18from typing import Callable
19from typing import cast
20from typing import ClassVar
21from typing import Dict
22from typing import Iterable
23from typing import Iterator
24from typing import List
25from typing import Literal
26from typing import Mapping
27from typing import Optional
28from typing import overload
29from typing import Protocol
30from typing import Tuple
31from typing import Type
32from typing import TYPE_CHECKING
33from typing import TypeVar
34from typing import Union
35
36from ._util_cy import anon_map as anon_map
37from ._util_cy import prefix_anon_map as prefix_anon_map # noqa: F401
38from .. import exc
39from .. import util
40from ..util import langhelpers
41from ..util.typing import Self
42
43if TYPE_CHECKING:
44 from .annotation import _AnnotationDict
45 from .elements import ColumnElement
46
47
48__all__ = [
49 "iterate",
50 "traverse_using",
51 "traverse",
52 "cloned_traverse",
53 "replacement_traverse",
54 "Visitable",
55 "ExternalTraversal",
56 "InternalTraversal",
57 "anon_map",
58]
59
60
61class _CompilerDispatchType(Protocol):
62 def __call__(_self, self: Visitable, visitor: Any, **kw: Any) -> Any: ...
63
64
65class Visitable:
66 """Base class for visitable objects.
67
68 :class:`.Visitable` is used to implement the SQL compiler dispatch
69 functions. Other forms of traversal such as for cache key generation
70 are implemented separately using the :class:`.HasTraverseInternals`
71 interface.
72
73 .. versionchanged:: 2.0 The :class:`.Visitable` class was named
74 :class:`.Traversible` in the 1.4 series; the name is changed back
75 to :class:`.Visitable` in 2.0 which is what it was prior to 1.4.
76
77 Both names remain importable in both 1.4 and 2.0 versions.
78
79 """
80
81 __slots__ = ()
82
83 __visit_name__: str
84
85 _original_compiler_dispatch: _CompilerDispatchType
86
87 if typing.TYPE_CHECKING:
88
89 def _compiler_dispatch(self, visitor: Any, **kw: Any) -> str: ...
90
91 def __init_subclass__(cls) -> None:
92 if "__visit_name__" in cls.__dict__:
93 cls._generate_compiler_dispatch()
94 super().__init_subclass__()
95
96 @classmethod
97 def _generate_compiler_dispatch(cls) -> None:
98 visit_name = cls.__visit_name__
99
100 if "_compiler_dispatch" in cls.__dict__:
101 # class has a fixed _compiler_dispatch() method.
102 # copy it to "original" so that we can get it back if
103 # sqlalchemy.ext.compiles overrides it.
104 cls._original_compiler_dispatch = cls._compiler_dispatch
105 return
106
107 if not isinstance(visit_name, str):
108 raise exc.InvalidRequestError(
109 f"__visit_name__ on class {cls.__name__} must be a string "
110 "at the class level"
111 )
112
113 name = "visit_%s" % visit_name
114 getter = operator.attrgetter(name)
115
116 def _compiler_dispatch(
117 self: Visitable, visitor: Any, **kw: Any
118 ) -> str:
119 """Look for an attribute named "visit_<visit_name>" on the
120 visitor, and call it with the same kw params.
121
122 """
123 try:
124 meth = getter(visitor)
125 except AttributeError as err:
126 return visitor.visit_unsupported_compilation(self, err, **kw) # type: ignore[no-any-return] # noqa: E501
127 else:
128 return meth(self, **kw) # type: ignore[no-any-return] # noqa: E501
129
130 cls._compiler_dispatch = ( # type: ignore[method-assign]
131 cls._original_compiler_dispatch
132 ) = _compiler_dispatch
133
134 def __class_getitem__(cls, key: Any) -> Any:
135 # allow generic classes in py3.9+
136 return cls
137
138
139class InternalTraversal(Enum):
140 r"""Defines visitor symbols used for internal traversal.
141
142 The :class:`.InternalTraversal` class is used in two ways. One is that
143 it can serve as the superclass for an object that implements the
144 various visit methods of the class. The other is that the symbols
145 themselves of :class:`.InternalTraversal` are used within
146 the ``_traverse_internals`` collection. Such as, the :class:`.Case`
147 object defines ``_traverse_internals`` as ::
148
149 class Case(ColumnElement[_T]):
150 _traverse_internals = [
151 ("value", InternalTraversal.dp_clauseelement),
152 ("whens", InternalTraversal.dp_clauseelement_tuples),
153 ("else_", InternalTraversal.dp_clauseelement),
154 ]
155
156 Above, the :class:`.Case` class indicates its internal state as the
157 attributes named ``value``, ``whens``, and ``else_``. They each
158 link to an :class:`.InternalTraversal` method which indicates the type
159 of datastructure to which each attribute refers.
160
161 Using the ``_traverse_internals`` structure, objects of type
162 :class:`.InternalTraversible` will have the following methods automatically
163 implemented:
164
165 * :meth:`.HasTraverseInternals.get_children`
166
167 * :meth:`.HasTraverseInternals._copy_internals`
168
169 * :meth:`.HasCacheKey._gen_cache_key`
170
171 Subclasses can also implement these methods directly, particularly for the
172 :meth:`.HasTraverseInternals._copy_internals` method, when special steps
173 are needed.
174
175 .. versionadded:: 1.4
176
177 """
178
179 dp_has_cache_key = "HC"
180 """Visit a :class:`.HasCacheKey` object."""
181
182 dp_has_cache_key_list = "HL"
183 """Visit a list of :class:`.HasCacheKey` objects."""
184
185 dp_clauseelement = "CE"
186 """Visit a :class:`_expression.ClauseElement` object."""
187
188 dp_fromclause_canonical_column_collection = "FC"
189 """Visit a :class:`_expression.FromClause` object in the context of the
190 ``columns`` attribute.
191
192 The column collection is "canonical", meaning it is the originally
193 defined location of the :class:`.ColumnClause` objects. Right now
194 this means that the object being visited is a
195 :class:`_expression.TableClause`
196 or :class:`_schema.Table` object only.
197
198 """
199
200 dp_clauseelement_tuples = "CTS"
201 """Visit a list of tuples which contain :class:`_expression.ClauseElement`
202 objects.
203
204 """
205
206 dp_clauseelement_list = "CL"
207 """Visit a list of :class:`_expression.ClauseElement` objects.
208
209 """
210
211 dp_clauseelement_tuple = "CT"
212 """Visit a tuple of :class:`_expression.ClauseElement` objects.
213
214 """
215
216 dp_executable_options = "EO"
217
218 dp_compile_state_funcs = "WC"
219
220 dp_fromclause_ordered_set = "CO"
221 """Visit an ordered set of :class:`_expression.FromClause` objects. """
222
223 dp_string = "S"
224 """Visit a plain string value.
225
226 Examples include table and column names, bound parameter keys, special
227 keywords such as "UNION", "UNION ALL".
228
229 The string value is considered to be significant for cache key
230 generation.
231
232 """
233
234 dp_string_list = "SL"
235 """Visit a list of strings."""
236
237 dp_anon_name = "AN"
238 """Visit a potentially "anonymized" string value.
239
240 The string value is considered to be significant for cache key
241 generation.
242
243 """
244
245 dp_boolean = "B"
246 """Visit a boolean value.
247
248 The boolean value is considered to be significant for cache key
249 generation.
250
251 """
252
253 dp_operator = "O"
254 """Visit an operator.
255
256 The operator is a function from the :mod:`sqlalchemy.sql.operators`
257 module.
258
259 The operator value is considered to be significant for cache key
260 generation.
261
262 """
263
264 dp_type = "T"
265 """Visit a :class:`.TypeEngine` object
266
267 The type object is considered to be significant for cache key
268 generation.
269
270 """
271
272 dp_plain_dict = "PD"
273 """Visit a dictionary with string keys.
274
275 The keys of the dictionary should be strings, the values should
276 be immutable and hashable. The dictionary is considered to be
277 significant for cache key generation.
278
279 """
280
281 dp_dialect_options = "DO"
282 """Visit a dialect options structure."""
283
284 dp_string_clauseelement_dict = "CD"
285 """Visit a dictionary of string keys to :class:`_expression.ClauseElement`
286 objects.
287
288 """
289
290 dp_string_multi_dict = "MD"
291 """Visit a dictionary of string keys to values which may either be
292 plain immutable/hashable or :class:`.HasCacheKey` objects.
293
294 """
295
296 dp_annotations_key = "AK"
297 """Visit the _annotations_cache_key element.
298
299 This is a dictionary of additional information about a ClauseElement
300 that modifies its role. It should be included when comparing or caching
301 objects, however generating this key is relatively expensive. Visitors
302 should check the "_annotations" dict for non-None first before creating
303 this key.
304
305 """
306
307 dp_plain_obj = "PO"
308 """Visit a plain python object.
309
310 The value should be immutable and hashable, such as an integer.
311 The value is considered to be significant for cache key generation.
312
313 """
314
315 dp_named_ddl_element = "DD"
316 """Visit a simple named DDL element.
317
318 The current object used by this method is the :class:`.Sequence`.
319
320 The object is only considered to be important for cache key generation
321 as far as its name, but not any other aspects of it.
322
323 """
324
325 dp_prefix_sequence = "PS"
326 """Visit the sequence represented by :class:`_expression.HasPrefixes`
327 or :class:`_expression.HasSuffixes`.
328
329 """
330
331 dp_table_hint_list = "TH"
332 """Visit the ``_hints`` collection of a :class:`_expression.Select`
333 object.
334
335 """
336
337 dp_setup_join_tuple = "SJ"
338
339 dp_memoized_select_entities = "ME"
340
341 dp_statement_hint_list = "SH"
342 """Visit the ``_statement_hints`` collection of a
343 :class:`_expression.Select`
344 object.
345
346 """
347
348 dp_unknown_structure = "UK"
349 """Visit an unknown structure.
350
351 """
352
353 dp_dml_ordered_values = "DML_OV"
354 """Visit the values() ordered tuple list of an
355 :class:`_expression.Update` object."""
356
357 dp_dml_values = "DML_V"
358 """Visit the values() dictionary of a :class:`.ValuesBase`
359 (e.g. Insert or Update) object.
360
361 """
362
363 dp_dml_multi_values = "DML_MV"
364 """Visit the values() multi-valued list of dictionaries of an
365 :class:`_expression.Insert` object.
366
367 """
368
369 dp_propagate_attrs = "PA"
370 """Visit the propagate attrs dict. This hardcodes to the particular
371 elements we care about right now."""
372
373 """Symbols that follow are additional symbols that are useful in
374 caching applications.
375
376 Traversals for :class:`_expression.ClauseElement` objects only need to use
377 those symbols present in :class:`.InternalTraversal`. However, for
378 additional caching use cases within the ORM, symbols dealing with the
379 :class:`.HasCacheKey` class are added here.
380
381 """
382
383 dp_ignore = "IG"
384 """Specify an object that should be ignored entirely.
385
386 This currently applies function call argument caching where some
387 arguments should not be considered to be part of a cache key.
388
389 """
390
391 dp_inspectable = "IS"
392 """Visit an inspectable object where the return value is a
393 :class:`.HasCacheKey` object."""
394
395 dp_multi = "M"
396 """Visit an object that may be a :class:`.HasCacheKey` or may be a
397 plain hashable object."""
398
399 dp_multi_list = "MT"
400 """Visit a tuple containing elements that may be :class:`.HasCacheKey` or
401 may be a plain hashable object."""
402
403 dp_has_cache_key_tuples = "HT"
404 """Visit a list of tuples which contain :class:`.HasCacheKey`
405 objects.
406
407 """
408
409 dp_params = "PM"
410 """Visit the _params collection of ExecutableStatement"""
411
412
413_TraverseInternalsType = List[Tuple[str, InternalTraversal]]
414"""a structure that defines how a HasTraverseInternals should be
415traversed.
416
417This structure consists of a list of (attributename, internaltraversal)
418tuples, where the "attributename" refers to the name of an attribute on an
419instance of the HasTraverseInternals object, and "internaltraversal" refers
420to an :class:`.InternalTraversal` enumeration symbol defining what kind
421of data this attribute stores, which indicates to the traverser how it should
422be handled.
423
424"""
425
426
427class HasTraverseInternals:
428 """base for classes that have a "traverse internals" element,
429 which defines all kinds of ways of traversing the elements of an object.
430
431 Compared to :class:`.Visitable`, which relies upon an external visitor to
432 define how the object is traversed (i.e. the :class:`.SQLCompiler`), the
433 :class:`.HasTraverseInternals` interface allows classes to define their own
434 traversal, that is, what attributes are accessed and in what order.
435
436 """
437
438 __slots__ = ()
439
440 _traverse_internals: _TraverseInternalsType
441
442 _is_immutable: bool = False
443
444 @util.preload_module("sqlalchemy.sql.traversals")
445 def get_children(
446 self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any
447 ) -> Iterable[HasTraverseInternals]:
448 r"""Return immediate child :class:`.visitors.HasTraverseInternals`
449 elements of this :class:`.visitors.HasTraverseInternals`.
450
451 This is used for visit traversal.
452
453 \**kw may contain flags that change the collection that is
454 returned, for example to return a subset of items in order to
455 cut down on larger traversals, or to return child items from a
456 different context (such as schema-level collections instead of
457 clause-level).
458
459 """
460
461 traversals = util.preloaded.sql_traversals
462
463 try:
464 traverse_internals = self._traverse_internals
465 except AttributeError:
466 # user-defined classes may not have a _traverse_internals
467 return []
468
469 dispatch = traversals._get_children.run_generated_dispatch
470 return itertools.chain.from_iterable(
471 meth(obj, **kw)
472 for attrname, obj, meth in dispatch(
473 self, traverse_internals, "_generated_get_children_traversal"
474 )
475 if attrname not in omit_attrs and obj is not None
476 )
477
478
479class _InternalTraversalDispatchType(Protocol):
480 def __call__(s, self: object, visitor: HasTraversalDispatch) -> Any: ...
481
482
483class HasTraversalDispatch:
484 r"""Define infrastructure for classes that perform internal traversals
485
486 .. versionadded:: 2.0
487
488 """
489
490 __slots__ = ()
491
492 _dispatch_lookup: ClassVar[Dict[Union[InternalTraversal, str], str]] = {}
493
494 def dispatch(self, visit_symbol: InternalTraversal) -> Callable[..., Any]:
495 """Given a method from :class:`.HasTraversalDispatch`, return the
496 corresponding method on a subclass.
497
498 """
499 name = _dispatch_lookup[visit_symbol]
500 return getattr(self, name, None) # type: ignore[return-value]
501
502 def run_generated_dispatch(
503 self,
504 target: object,
505 internal_dispatch: _TraverseInternalsType,
506 generate_dispatcher_name: str,
507 ) -> Any:
508 dispatcher: _InternalTraversalDispatchType
509 try:
510 dispatcher = target.__class__.__dict__[generate_dispatcher_name]
511 except KeyError:
512 # traversals.py -> _preconfigure_traversals()
513 # may be used to run these ahead of time, but
514 # is not enabled right now.
515 # this block will generate any remaining dispatchers.
516 dispatcher = self.generate_dispatch(
517 target.__class__, internal_dispatch, generate_dispatcher_name
518 )
519 return dispatcher(target, self)
520
521 def generate_dispatch(
522 self,
523 target_cls: Type[object],
524 internal_dispatch: _TraverseInternalsType,
525 generate_dispatcher_name: str,
526 ) -> _InternalTraversalDispatchType:
527 dispatcher = self._generate_dispatcher(
528 internal_dispatch, generate_dispatcher_name
529 )
530 # assert isinstance(target_cls, type)
531 setattr(target_cls, generate_dispatcher_name, dispatcher)
532 return dispatcher
533
534 def _generate_dispatcher(
535 self, internal_dispatch: _TraverseInternalsType, method_name: str
536 ) -> _InternalTraversalDispatchType:
537 names = []
538 for attrname, visit_sym in internal_dispatch:
539 meth = self.dispatch(visit_sym)
540 if meth is not None:
541 visit_name = _dispatch_lookup[visit_sym]
542 names.append((attrname, visit_name))
543
544 code = (
545 (" return [\n")
546 + (
547 ", \n".join(
548 " (%r, self.%s, visitor.%s)"
549 % (attrname, attrname, visit_name)
550 for attrname, visit_name in names
551 )
552 )
553 + ("\n ]\n")
554 )
555 meth_text = ("def %s(self, visitor):\n" % method_name) + code + "\n"
556 return cast(
557 _InternalTraversalDispatchType,
558 langhelpers.exec_code_in_env(meth_text, {}, method_name),
559 )
560
561
562ExtendedInternalTraversal = InternalTraversal
563
564
565def _generate_traversal_dispatch() -> None:
566 lookup = _dispatch_lookup
567
568 for sym in InternalTraversal:
569 key = sym.name
570 if key.startswith("dp_"):
571 visit_key = key.replace("dp_", "visit_")
572 sym_name = sym.value
573 assert sym_name not in lookup, sym_name
574 lookup[sym] = lookup[sym_name] = visit_key
575
576
577_dispatch_lookup = HasTraversalDispatch._dispatch_lookup
578_generate_traversal_dispatch()
579
580
581class ExternallyTraversible(HasTraverseInternals, Visitable):
582 __slots__ = ()
583
584 _annotations: Mapping[Any, Any] = util.EMPTY_DICT
585
586 if typing.TYPE_CHECKING:
587
588 def _annotate(self, values: _AnnotationDict) -> Self: ...
589
590 def get_children(
591 self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any
592 ) -> Iterable[ExternallyTraversible]: ...
593
594 def _clone(self, **kw: Any) -> Self:
595 """clone this element"""
596 raise NotImplementedError()
597
598 def _copy_internals(
599 self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any
600 ) -> None:
601 """Reassign internal elements to be clones of themselves.
602
603 Called during a copy-and-traverse operation on newly
604 shallow-copied elements to create a deep copy.
605
606 The given clone function should be used, which may be applying
607 additional transformations to the element (i.e. replacement
608 traversal, cloned traversal, annotations).
609
610 """
611 raise NotImplementedError()
612
613
614_ET = TypeVar("_ET", bound=ExternallyTraversible)
615
616_CE = TypeVar("_CE", bound="ColumnElement[Any]")
617
618_TraverseCallableType = Callable[[_ET], None]
619
620
621class _CloneCallableType(Protocol):
622 def __call__(self, element: _ET, **kw: Any) -> _ET: ...
623
624
625class _TraverseTransformCallableType(Protocol[_ET]):
626 def __call__(self, element: _ET, **kw: Any) -> Optional[_ET]: ...
627
628
629_ExtT = TypeVar("_ExtT", bound="ExternalTraversal")
630
631
632class ExternalTraversal(util.MemoizedSlots):
633 """Base class for visitor objects which can traverse externally using
634 the :func:`.visitors.traverse` function.
635
636 Direct usage of the :func:`.visitors.traverse` function is usually
637 preferred.
638
639 """
640
641 __slots__ = ("_visitor_dict", "_next")
642
643 __traverse_options__: Dict[str, Any] = {}
644 _next: Optional[ExternalTraversal]
645
646 def traverse_single(self, obj: Visitable, **kw: Any) -> Any:
647 for v in self.visitor_iterator:
648 meth = getattr(v, "visit_%s" % obj.__visit_name__, None)
649 if meth:
650 return meth(obj, **kw)
651
652 def iterate(
653 self, obj: Optional[ExternallyTraversible]
654 ) -> Iterator[ExternallyTraversible]:
655 """Traverse the given expression structure, returning an iterator
656 of all elements.
657
658 """
659 return iterate(obj, self.__traverse_options__)
660
661 @overload
662 def traverse(self, obj: Literal[None]) -> None: ...
663
664 @overload
665 def traverse(
666 self, obj: ExternallyTraversible
667 ) -> ExternallyTraversible: ...
668
669 def traverse(
670 self, obj: Optional[ExternallyTraversible]
671 ) -> Optional[ExternallyTraversible]:
672 """Traverse and visit the given expression structure."""
673
674 return traverse(obj, self.__traverse_options__, self._visitor_dict)
675
676 def _memoized_attr__visitor_dict(
677 self,
678 ) -> Dict[str, _TraverseCallableType[Any]]:
679 visitors = {}
680
681 for name in dir(self):
682 if name.startswith("visit_"):
683 visitors[name[6:]] = getattr(self, name)
684 return visitors
685
686 @property
687 def visitor_iterator(self) -> Iterator[ExternalTraversal]:
688 """Iterate through this visitor and each 'chained' visitor."""
689
690 v: Optional[ExternalTraversal] = self
691 while v:
692 yield v
693 v = getattr(v, "_next", None)
694
695 def chain(self: _ExtT, visitor: ExternalTraversal) -> _ExtT:
696 """'Chain' an additional ExternalTraversal onto this ExternalTraversal
697
698 The chained visitor will receive all visit events after this one.
699
700 """
701 tail = list(self.visitor_iterator)[-1]
702 tail._next = visitor
703 return self
704
705
706class CloningExternalTraversal(ExternalTraversal):
707 """Base class for visitor objects which can traverse using
708 the :func:`.visitors.cloned_traverse` function.
709
710 Direct usage of the :func:`.visitors.cloned_traverse` function is usually
711 preferred.
712
713
714 """
715
716 __slots__ = ()
717
718 def copy_and_process(
719 self, list_: List[ExternallyTraversible]
720 ) -> List[ExternallyTraversible]:
721 """Apply cloned traversal to the given list of elements, and return
722 the new list.
723
724 """
725 return [self.traverse(x) for x in list_]
726
727 @overload
728 def traverse(self, obj: Literal[None]) -> None: ...
729
730 @overload
731 def traverse(
732 self, obj: ExternallyTraversible
733 ) -> ExternallyTraversible: ...
734
735 def traverse(
736 self, obj: Optional[ExternallyTraversible]
737 ) -> Optional[ExternallyTraversible]:
738 """Traverse and visit the given expression structure."""
739
740 return cloned_traverse(
741 obj, self.__traverse_options__, self._visitor_dict
742 )
743
744
745class ReplacingExternalTraversal(CloningExternalTraversal):
746 """Base class for visitor objects which can traverse using
747 the :func:`.visitors.replacement_traverse` function.
748
749 Direct usage of the :func:`.visitors.replacement_traverse` function is
750 usually preferred.
751
752 """
753
754 __slots__ = ()
755
756 def replace(
757 self, elem: ExternallyTraversible
758 ) -> Optional[ExternallyTraversible]:
759 """Receive pre-copied elements during a cloning traversal.
760
761 If the method returns a new element, the element is used
762 instead of creating a simple copy of the element. Traversal
763 will halt on the newly returned element if it is re-encountered.
764 """
765 return None
766
767 @overload
768 def traverse(self, obj: Literal[None]) -> None: ...
769
770 @overload
771 def traverse(
772 self, obj: ExternallyTraversible
773 ) -> ExternallyTraversible: ...
774
775 def traverse(
776 self, obj: Optional[ExternallyTraversible]
777 ) -> Optional[ExternallyTraversible]:
778 """Traverse and visit the given expression structure."""
779
780 def replace(
781 element: ExternallyTraversible,
782 **kw: Any,
783 ) -> Optional[ExternallyTraversible]:
784 for v in self.visitor_iterator:
785 e = cast(ReplacingExternalTraversal, v).replace(element)
786 if e is not None:
787 return e
788
789 return None
790
791 return replacement_traverse(obj, self.__traverse_options__, replace)
792
793
794# backwards compatibility
795Traversible = Visitable
796
797ClauseVisitor = ExternalTraversal
798CloningVisitor = CloningExternalTraversal
799ReplacingCloningVisitor = ReplacingExternalTraversal
800
801
802def iterate(
803 obj: Optional[ExternallyTraversible],
804 opts: Mapping[str, Any] = util.EMPTY_DICT,
805) -> Iterator[ExternallyTraversible]:
806 r"""Traverse the given expression structure, returning an iterator.
807
808 Traversal is configured to be breadth-first.
809
810 The central API feature used by the :func:`.visitors.iterate`
811 function is the
812 :meth:`_expression.ClauseElement.get_children` method of
813 :class:`_expression.ClauseElement` objects. This method should return all
814 the :class:`_expression.ClauseElement` objects which are associated with a
815 particular :class:`_expression.ClauseElement` object. For example, a
816 :class:`.Case` structure will refer to a series of
817 :class:`_expression.ColumnElement` objects within its "whens" and "else\_"
818 member variables.
819
820 :param obj: :class:`_expression.ClauseElement` structure to be traversed
821
822 :param opts: dictionary of iteration options. This dictionary is usually
823 empty in modern usage.
824
825 """
826 if obj is None:
827 return
828
829 yield obj
830 children = obj.get_children(**opts)
831
832 if not children:
833 return
834
835 stack = deque([children])
836 while stack:
837 t_iterator = stack.popleft()
838 for t in t_iterator:
839 yield t
840 stack.append(t.get_children(**opts))
841
842
843@overload
844def traverse_using(
845 iterator: Iterable[ExternallyTraversible],
846 obj: Literal[None],
847 visitors: Mapping[str, _TraverseCallableType[Any]],
848) -> None: ...
849
850
851@overload
852def traverse_using(
853 iterator: Iterable[ExternallyTraversible],
854 obj: ExternallyTraversible,
855 visitors: Mapping[str, _TraverseCallableType[Any]],
856) -> ExternallyTraversible: ...
857
858
859def traverse_using(
860 iterator: Iterable[ExternallyTraversible],
861 obj: Optional[ExternallyTraversible],
862 visitors: Mapping[str, _TraverseCallableType[Any]],
863) -> Optional[ExternallyTraversible]:
864 """Visit the given expression structure using the given iterator of
865 objects.
866
867 :func:`.visitors.traverse_using` is usually called internally as the result
868 of the :func:`.visitors.traverse` function.
869
870 :param iterator: an iterable or sequence which will yield
871 :class:`_expression.ClauseElement`
872 structures; the iterator is assumed to be the
873 product of the :func:`.visitors.iterate` function.
874
875 :param obj: the :class:`_expression.ClauseElement`
876 that was used as the target of the
877 :func:`.iterate` function.
878
879 :param visitors: dictionary of visit functions. See :func:`.traverse`
880 for details on this dictionary.
881
882 .. seealso::
883
884 :func:`.traverse`
885
886
887 """
888 for target in iterator:
889 meth = visitors.get(target.__visit_name__, None)
890 if meth:
891 meth(target)
892 return obj
893
894
895@overload
896def traverse(
897 obj: Literal[None],
898 opts: Mapping[str, Any],
899 visitors: Mapping[str, _TraverseCallableType[Any]],
900) -> None: ...
901
902
903@overload
904def traverse(
905 obj: ExternallyTraversible,
906 opts: Mapping[str, Any],
907 visitors: Mapping[str, _TraverseCallableType[Any]],
908) -> ExternallyTraversible: ...
909
910
911def traverse(
912 obj: Optional[ExternallyTraversible],
913 opts: Mapping[str, Any],
914 visitors: Mapping[str, _TraverseCallableType[Any]],
915) -> Optional[ExternallyTraversible]:
916 """Traverse and visit the given expression structure using the default
917 iterator.
918
919 e.g.::
920
921 from sqlalchemy.sql import visitors
922
923 stmt = select(some_table).where(some_table.c.foo == "bar")
924
925
926 def visit_bindparam(bind_param):
927 print("found bound value: %s" % bind_param.value)
928
929
930 visitors.traverse(stmt, {}, {"bindparam": visit_bindparam})
931
932 The iteration of objects uses the :func:`.visitors.iterate` function,
933 which does a breadth-first traversal using a stack.
934
935 :param obj: :class:`_expression.ClauseElement` structure to be traversed
936
937 :param opts: dictionary of iteration options. This dictionary is usually
938 empty in modern usage.
939
940 :param visitors: dictionary of visit functions. The dictionary should
941 have strings as keys, each of which would correspond to the
942 ``__visit_name__`` of a particular kind of SQL expression object, and
943 callable functions as values, each of which represents a visitor function
944 for that kind of object.
945
946 """
947 return traverse_using(iterate(obj, opts), obj, visitors)
948
949
950@overload
951def cloned_traverse(
952 obj: Literal[None],
953 opts: Mapping[str, Any],
954 visitors: Mapping[str, _TraverseCallableType[Any]],
955) -> None: ...
956
957
958# a bit of controversy here, as the clone of the lead element
959# *could* in theory replace with an entirely different kind of element.
960# however this is really not how cloned_traverse is ever used internally
961# at least.
962@overload
963def cloned_traverse(
964 obj: _ET,
965 opts: Mapping[str, Any],
966 visitors: Mapping[str, _TraverseCallableType[Any]],
967) -> _ET: ...
968
969
970def cloned_traverse(
971 obj: Optional[ExternallyTraversible],
972 opts: Mapping[str, Any],
973 visitors: Mapping[str, _TraverseCallableType[Any]],
974) -> Optional[ExternallyTraversible]:
975 """Clone the given expression structure, allowing modifications by
976 visitors for mutable objects.
977
978 Traversal usage is the same as that of :func:`.visitors.traverse`.
979 The visitor functions present in the ``visitors`` dictionary may also
980 modify the internals of the given structure as the traversal proceeds.
981
982 The :func:`.cloned_traverse` function does **not** provide objects that are
983 part of the :class:`.Immutable` interface to the visit methods (this
984 primarily includes :class:`.ColumnClause`, :class:`.Column`,
985 :class:`.TableClause` and :class:`.Table` objects). As this traversal is
986 only intended to allow in-place mutation of objects, :class:`.Immutable`
987 objects are skipped. The :meth:`.Immutable._clone` method is still called
988 on each object to allow for objects to replace themselves with a different
989 object based on a clone of their sub-internals (e.g. a
990 :class:`.ColumnClause` that clones its subquery to return a new
991 :class:`.ColumnClause`).
992
993 .. versionchanged:: 2.0 The :func:`.cloned_traverse` function omits
994 objects that are part of the :class:`.Immutable` interface.
995
996 The central API feature used by the :func:`.visitors.cloned_traverse`
997 and :func:`.visitors.replacement_traverse` functions, in addition to the
998 :meth:`_expression.ClauseElement.get_children`
999 function that is used to achieve
1000 the iteration, is the :meth:`_expression.ClauseElement._copy_internals`
1001 method.
1002 For a :class:`_expression.ClauseElement`
1003 structure to support cloning and replacement
1004 traversals correctly, it needs to be able to pass a cloning function into
1005 its internal members in order to make copies of them.
1006
1007 .. seealso::
1008
1009 :func:`.visitors.traverse`
1010
1011 :func:`.visitors.replacement_traverse`
1012
1013 """
1014
1015 cloned: Dict[int, ExternallyTraversible] = {}
1016 stop_on = set(opts.get("stop_on", []))
1017
1018 def deferred_copy_internals(
1019 obj: ExternallyTraversible,
1020 ) -> ExternallyTraversible:
1021 return cloned_traverse(obj, opts, visitors)
1022
1023 def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible:
1024 if elem in stop_on:
1025 return elem
1026 else:
1027 if id(elem) not in cloned:
1028 if "replace" in kw:
1029 newelem = cast(
1030 Optional[ExternallyTraversible], kw["replace"](elem)
1031 )
1032 if newelem is not None:
1033 cloned[id(elem)] = newelem
1034 return newelem
1035
1036 # the _clone method for immutable normally returns "self".
1037 # however, the method is still allowed to return a
1038 # different object altogether; ColumnClause._clone() will
1039 # based on options clone the subquery to which it is associated
1040 # and return the new corresponding column.
1041 cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw)
1042 newelem._copy_internals(clone=clone, **kw)
1043
1044 # however, visit methods which are tasked with in-place
1045 # mutation of the object should not get access to the immutable
1046 # object.
1047 if not elem._is_immutable:
1048 meth = visitors.get(newelem.__visit_name__, None)
1049 if meth:
1050 meth(newelem)
1051 return cloned[id(elem)]
1052
1053 if obj is not None:
1054 obj = clone(
1055 obj, deferred_copy_internals=deferred_copy_internals, **opts
1056 )
1057 clone = None # type: ignore[assignment] # remove gc cycles
1058 return obj
1059
1060
1061@overload
1062def replacement_traverse(
1063 obj: Literal[None],
1064 opts: Mapping[str, Any],
1065 replace: _TraverseTransformCallableType[Any],
1066) -> None: ...
1067
1068
1069@overload
1070def replacement_traverse(
1071 obj: _CE,
1072 opts: Mapping[str, Any],
1073 replace: _TraverseTransformCallableType[Any],
1074) -> _CE: ...
1075
1076
1077@overload
1078def replacement_traverse(
1079 obj: ExternallyTraversible,
1080 opts: Mapping[str, Any],
1081 replace: _TraverseTransformCallableType[Any],
1082) -> ExternallyTraversible: ...
1083
1084
1085def replacement_traverse(
1086 obj: Optional[ExternallyTraversible],
1087 opts: Mapping[str, Any],
1088 replace: _TraverseTransformCallableType[Any],
1089) -> Optional[ExternallyTraversible]:
1090 """Clone the given expression structure, allowing element
1091 replacement by a given replacement function.
1092
1093 This function is very similar to the :func:`.visitors.cloned_traverse`
1094 function, except instead of being passed a dictionary of visitors, all
1095 elements are unconditionally passed into the given replace function.
1096 The replace function then has the option to return an entirely new object
1097 which will replace the one given. If it returns ``None``, then the object
1098 is kept in place.
1099
1100 The difference in usage between :func:`.visitors.cloned_traverse` and
1101 :func:`.visitors.replacement_traverse` is that in the former case, an
1102 already-cloned object is passed to the visitor function, and the visitor
1103 function can then manipulate the internal state of the object.
1104 In the case of the latter, the visitor function should only return an
1105 entirely different object, or do nothing.
1106
1107 The use case for :func:`.visitors.replacement_traverse` is that of
1108 replacing a FROM clause inside of a SQL structure with a different one,
1109 as is a common use case within the ORM.
1110
1111 """
1112
1113 cloned = {}
1114 stop_on = {id(x) for x in opts.get("stop_on", [])}
1115
1116 def deferred_copy_internals(
1117 obj: ExternallyTraversible,
1118 ) -> ExternallyTraversible:
1119 return replacement_traverse(obj, opts, replace)
1120
1121 def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible:
1122 if (
1123 id(elem) in stop_on
1124 or "no_replacement_traverse" in elem._annotations
1125 ):
1126 return elem
1127 else:
1128 newelem = replace(elem)
1129 if newelem is not None:
1130 stop_on.add(id(newelem))
1131 return newelem # type: ignore[no-any-return]
1132 else:
1133 # base "already seen" on id(), not hash, so that we don't
1134 # replace an Annotated element with its non-annotated one, and
1135 # vice versa
1136 id_elem = id(elem)
1137 if id_elem not in cloned:
1138 if "replace" in kw:
1139 newelem = kw["replace"](elem)
1140 if newelem is not None:
1141 cloned[id_elem] = newelem
1142 return newelem # type: ignore[no-any-return]
1143
1144 cloned[id_elem] = newelem = elem._clone(**kw)
1145 newelem._copy_internals(clone=clone, **kw)
1146 return cloned[id_elem] # type: ignore[no-any-return]
1147
1148 if obj is not None:
1149 obj = clone(
1150 obj, deferred_copy_internals=deferred_copy_internals, **opts
1151 )
1152 clone = None # type: ignore[assignment] # remove gc cycles
1153 return obj