1# sql/visitors.py
2# Copyright (C) 2005-2025 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 # noqa: E501
127 else:
128 return meth(self, **kw) # type: ignore # noqa: E501
129
130 cls._compiler_dispatch = ( # type: ignore
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_inspectable_list = "IL"
410 """Visit a list of inspectable objects which upon inspection are
411 HasCacheKey objects."""
412
413 dp_params = "PM"
414 """Visit the _params collection of ExecutableStatement"""
415
416
417_TraverseInternalsType = List[Tuple[str, InternalTraversal]]
418"""a structure that defines how a HasTraverseInternals should be
419traversed.
420
421This structure consists of a list of (attributename, internaltraversal)
422tuples, where the "attributename" refers to the name of an attribute on an
423instance of the HasTraverseInternals object, and "internaltraversal" refers
424to an :class:`.InternalTraversal` enumeration symbol defining what kind
425of data this attribute stores, which indicates to the traverser how it should
426be handled.
427
428"""
429
430
431class HasTraverseInternals:
432 """base for classes that have a "traverse internals" element,
433 which defines all kinds of ways of traversing the elements of an object.
434
435 Compared to :class:`.Visitable`, which relies upon an external visitor to
436 define how the object is travered (i.e. the :class:`.SQLCompiler`), the
437 :class:`.HasTraverseInternals` interface allows classes to define their own
438 traversal, that is, what attributes are accessed and in what order.
439
440 """
441
442 __slots__ = ()
443
444 _traverse_internals: _TraverseInternalsType
445
446 _is_immutable: bool = False
447
448 @util.preload_module("sqlalchemy.sql.traversals")
449 def get_children(
450 self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any
451 ) -> Iterable[HasTraverseInternals]:
452 r"""Return immediate child :class:`.visitors.HasTraverseInternals`
453 elements of this :class:`.visitors.HasTraverseInternals`.
454
455 This is used for visit traversal.
456
457 \**kw may contain flags that change the collection that is
458 returned, for example to return a subset of items in order to
459 cut down on larger traversals, or to return child items from a
460 different context (such as schema-level collections instead of
461 clause-level).
462
463 """
464
465 traversals = util.preloaded.sql_traversals
466
467 try:
468 traverse_internals = self._traverse_internals
469 except AttributeError:
470 # user-defined classes may not have a _traverse_internals
471 return []
472
473 dispatch = traversals._get_children.run_generated_dispatch
474 return itertools.chain.from_iterable(
475 meth(obj, **kw)
476 for attrname, obj, meth in dispatch(
477 self, traverse_internals, "_generated_get_children_traversal"
478 )
479 if attrname not in omit_attrs and obj is not None
480 )
481
482
483class _InternalTraversalDispatchType(Protocol):
484 def __call__(s, self: object, visitor: HasTraversalDispatch) -> Any: ...
485
486
487class HasTraversalDispatch:
488 r"""Define infrastructure for classes that perform internal traversals
489
490 .. versionadded:: 2.0
491
492 """
493
494 __slots__ = ()
495
496 _dispatch_lookup: ClassVar[Dict[Union[InternalTraversal, str], str]] = {}
497
498 def dispatch(self, visit_symbol: InternalTraversal) -> Callable[..., Any]:
499 """Given a method from :class:`.HasTraversalDispatch`, return the
500 corresponding method on a subclass.
501
502 """
503 name = _dispatch_lookup[visit_symbol]
504 return getattr(self, name, None) # type: ignore
505
506 def run_generated_dispatch(
507 self,
508 target: object,
509 internal_dispatch: _TraverseInternalsType,
510 generate_dispatcher_name: str,
511 ) -> Any:
512 dispatcher: _InternalTraversalDispatchType
513 try:
514 dispatcher = target.__class__.__dict__[generate_dispatcher_name]
515 except KeyError:
516 # traversals.py -> _preconfigure_traversals()
517 # may be used to run these ahead of time, but
518 # is not enabled right now.
519 # this block will generate any remaining dispatchers.
520 dispatcher = self.generate_dispatch(
521 target.__class__, internal_dispatch, generate_dispatcher_name
522 )
523 return dispatcher(target, self)
524
525 def generate_dispatch(
526 self,
527 target_cls: Type[object],
528 internal_dispatch: _TraverseInternalsType,
529 generate_dispatcher_name: str,
530 ) -> _InternalTraversalDispatchType:
531 dispatcher = self._generate_dispatcher(
532 internal_dispatch, generate_dispatcher_name
533 )
534 # assert isinstance(target_cls, type)
535 setattr(target_cls, generate_dispatcher_name, dispatcher)
536 return dispatcher
537
538 def _generate_dispatcher(
539 self, internal_dispatch: _TraverseInternalsType, method_name: str
540 ) -> _InternalTraversalDispatchType:
541 names = []
542 for attrname, visit_sym in internal_dispatch:
543 meth = self.dispatch(visit_sym)
544 if meth is not None:
545 visit_name = _dispatch_lookup[visit_sym]
546 names.append((attrname, visit_name))
547
548 code = (
549 (" return [\n")
550 + (
551 ", \n".join(
552 " (%r, self.%s, visitor.%s)"
553 % (attrname, attrname, visit_name)
554 for attrname, visit_name in names
555 )
556 )
557 + ("\n ]\n")
558 )
559 meth_text = ("def %s(self, visitor):\n" % method_name) + code + "\n"
560 return cast(
561 _InternalTraversalDispatchType,
562 langhelpers._exec_code_in_env(meth_text, {}, method_name),
563 )
564
565
566ExtendedInternalTraversal = InternalTraversal
567
568
569def _generate_traversal_dispatch() -> None:
570 lookup = _dispatch_lookup
571
572 for sym in InternalTraversal:
573 key = sym.name
574 if key.startswith("dp_"):
575 visit_key = key.replace("dp_", "visit_")
576 sym_name = sym.value
577 assert sym_name not in lookup, sym_name
578 lookup[sym] = lookup[sym_name] = visit_key
579
580
581_dispatch_lookup = HasTraversalDispatch._dispatch_lookup
582_generate_traversal_dispatch()
583
584
585class ExternallyTraversible(HasTraverseInternals, Visitable):
586 __slots__ = ()
587
588 _annotations: Mapping[Any, Any] = util.EMPTY_DICT
589
590 if typing.TYPE_CHECKING:
591
592 def _annotate(self, values: _AnnotationDict) -> Self: ...
593
594 def get_children(
595 self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any
596 ) -> Iterable[ExternallyTraversible]: ...
597
598 def _clone(self, **kw: Any) -> Self:
599 """clone this element"""
600 raise NotImplementedError()
601
602 def _copy_internals(
603 self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any
604 ) -> None:
605 """Reassign internal elements to be clones of themselves.
606
607 Called during a copy-and-traverse operation on newly
608 shallow-copied elements to create a deep copy.
609
610 The given clone function should be used, which may be applying
611 additional transformations to the element (i.e. replacement
612 traversal, cloned traversal, annotations).
613
614 """
615 raise NotImplementedError()
616
617
618_ET = TypeVar("_ET", bound=ExternallyTraversible)
619
620_CE = TypeVar("_CE", bound="ColumnElement[Any]")
621
622_TraverseCallableType = Callable[[_ET], None]
623
624
625class _CloneCallableType(Protocol):
626 def __call__(self, element: _ET, **kw: Any) -> _ET: ...
627
628
629class _TraverseTransformCallableType(Protocol[_ET]):
630 def __call__(self, element: _ET, **kw: Any) -> Optional[_ET]: ...
631
632
633_ExtT = TypeVar("_ExtT", bound="ExternalTraversal")
634
635
636class ExternalTraversal(util.MemoizedSlots):
637 """Base class for visitor objects which can traverse externally using
638 the :func:`.visitors.traverse` function.
639
640 Direct usage of the :func:`.visitors.traverse` function is usually
641 preferred.
642
643 """
644
645 __slots__ = ("_visitor_dict", "_next")
646
647 __traverse_options__: Dict[str, Any] = {}
648 _next: Optional[ExternalTraversal]
649
650 def traverse_single(self, obj: Visitable, **kw: Any) -> Any:
651 for v in self.visitor_iterator:
652 meth = getattr(v, "visit_%s" % obj.__visit_name__, None)
653 if meth:
654 return meth(obj, **kw)
655
656 def iterate(
657 self, obj: Optional[ExternallyTraversible]
658 ) -> Iterator[ExternallyTraversible]:
659 """Traverse the given expression structure, returning an iterator
660 of all elements.
661
662 """
663 return iterate(obj, self.__traverse_options__)
664
665 @overload
666 def traverse(self, obj: Literal[None]) -> None: ...
667
668 @overload
669 def traverse(
670 self, obj: ExternallyTraversible
671 ) -> ExternallyTraversible: ...
672
673 def traverse(
674 self, obj: Optional[ExternallyTraversible]
675 ) -> Optional[ExternallyTraversible]:
676 """Traverse and visit the given expression structure."""
677
678 return traverse(obj, self.__traverse_options__, self._visitor_dict)
679
680 def _memoized_attr__visitor_dict(
681 self,
682 ) -> Dict[str, _TraverseCallableType[Any]]:
683 visitors = {}
684
685 for name in dir(self):
686 if name.startswith("visit_"):
687 visitors[name[6:]] = getattr(self, name)
688 return visitors
689
690 @property
691 def visitor_iterator(self) -> Iterator[ExternalTraversal]:
692 """Iterate through this visitor and each 'chained' visitor."""
693
694 v: Optional[ExternalTraversal] = self
695 while v:
696 yield v
697 v = getattr(v, "_next", None)
698
699 def chain(self: _ExtT, visitor: ExternalTraversal) -> _ExtT:
700 """'Chain' an additional ExternalTraversal onto this ExternalTraversal
701
702 The chained visitor will receive all visit events after this one.
703
704 """
705 tail = list(self.visitor_iterator)[-1]
706 tail._next = visitor
707 return self
708
709
710class CloningExternalTraversal(ExternalTraversal):
711 """Base class for visitor objects which can traverse using
712 the :func:`.visitors.cloned_traverse` function.
713
714 Direct usage of the :func:`.visitors.cloned_traverse` function is usually
715 preferred.
716
717
718 """
719
720 __slots__ = ()
721
722 def copy_and_process(
723 self, list_: List[ExternallyTraversible]
724 ) -> List[ExternallyTraversible]:
725 """Apply cloned traversal to the given list of elements, and return
726 the new list.
727
728 """
729 return [self.traverse(x) for x in list_]
730
731 @overload
732 def traverse(self, obj: Literal[None]) -> None: ...
733
734 @overload
735 def traverse(
736 self, obj: ExternallyTraversible
737 ) -> ExternallyTraversible: ...
738
739 def traverse(
740 self, obj: Optional[ExternallyTraversible]
741 ) -> Optional[ExternallyTraversible]:
742 """Traverse and visit the given expression structure."""
743
744 return cloned_traverse(
745 obj, self.__traverse_options__, self._visitor_dict
746 )
747
748
749class ReplacingExternalTraversal(CloningExternalTraversal):
750 """Base class for visitor objects which can traverse using
751 the :func:`.visitors.replacement_traverse` function.
752
753 Direct usage of the :func:`.visitors.replacement_traverse` function is
754 usually preferred.
755
756 """
757
758 __slots__ = ()
759
760 def replace(
761 self, elem: ExternallyTraversible
762 ) -> Optional[ExternallyTraversible]:
763 """Receive pre-copied elements during a cloning traversal.
764
765 If the method returns a new element, the element is used
766 instead of creating a simple copy of the element. Traversal
767 will halt on the newly returned element if it is re-encountered.
768 """
769 return None
770
771 @overload
772 def traverse(self, obj: Literal[None]) -> None: ...
773
774 @overload
775 def traverse(
776 self, obj: ExternallyTraversible
777 ) -> ExternallyTraversible: ...
778
779 def traverse(
780 self, obj: Optional[ExternallyTraversible]
781 ) -> Optional[ExternallyTraversible]:
782 """Traverse and visit the given expression structure."""
783
784 def replace(
785 element: ExternallyTraversible,
786 **kw: Any,
787 ) -> Optional[ExternallyTraversible]:
788 for v in self.visitor_iterator:
789 e = cast(ReplacingExternalTraversal, v).replace(element)
790 if e is not None:
791 return e
792
793 return None
794
795 return replacement_traverse(obj, self.__traverse_options__, replace)
796
797
798# backwards compatibility
799Traversible = Visitable
800
801ClauseVisitor = ExternalTraversal
802CloningVisitor = CloningExternalTraversal
803ReplacingCloningVisitor = ReplacingExternalTraversal
804
805
806def iterate(
807 obj: Optional[ExternallyTraversible],
808 opts: Mapping[str, Any] = util.EMPTY_DICT,
809) -> Iterator[ExternallyTraversible]:
810 r"""Traverse the given expression structure, returning an iterator.
811
812 Traversal is configured to be breadth-first.
813
814 The central API feature used by the :func:`.visitors.iterate`
815 function is the
816 :meth:`_expression.ClauseElement.get_children` method of
817 :class:`_expression.ClauseElement` objects. This method should return all
818 the :class:`_expression.ClauseElement` objects which are associated with a
819 particular :class:`_expression.ClauseElement` object. For example, a
820 :class:`.Case` structure will refer to a series of
821 :class:`_expression.ColumnElement` objects within its "whens" and "else\_"
822 member variables.
823
824 :param obj: :class:`_expression.ClauseElement` structure to be traversed
825
826 :param opts: dictionary of iteration options. This dictionary is usually
827 empty in modern usage.
828
829 """
830 if obj is None:
831 return
832
833 yield obj
834 children = obj.get_children(**opts)
835
836 if not children:
837 return
838
839 stack = deque([children])
840 while stack:
841 t_iterator = stack.popleft()
842 for t in t_iterator:
843 yield t
844 stack.append(t.get_children(**opts))
845
846
847@overload
848def traverse_using(
849 iterator: Iterable[ExternallyTraversible],
850 obj: Literal[None],
851 visitors: Mapping[str, _TraverseCallableType[Any]],
852) -> None: ...
853
854
855@overload
856def traverse_using(
857 iterator: Iterable[ExternallyTraversible],
858 obj: ExternallyTraversible,
859 visitors: Mapping[str, _TraverseCallableType[Any]],
860) -> ExternallyTraversible: ...
861
862
863def traverse_using(
864 iterator: Iterable[ExternallyTraversible],
865 obj: Optional[ExternallyTraversible],
866 visitors: Mapping[str, _TraverseCallableType[Any]],
867) -> Optional[ExternallyTraversible]:
868 """Visit the given expression structure using the given iterator of
869 objects.
870
871 :func:`.visitors.traverse_using` is usually called internally as the result
872 of the :func:`.visitors.traverse` function.
873
874 :param iterator: an iterable or sequence which will yield
875 :class:`_expression.ClauseElement`
876 structures; the iterator is assumed to be the
877 product of the :func:`.visitors.iterate` function.
878
879 :param obj: the :class:`_expression.ClauseElement`
880 that was used as the target of the
881 :func:`.iterate` function.
882
883 :param visitors: dictionary of visit functions. See :func:`.traverse`
884 for details on this dictionary.
885
886 .. seealso::
887
888 :func:`.traverse`
889
890
891 """
892 for target in iterator:
893 meth = visitors.get(target.__visit_name__, None)
894 if meth:
895 meth(target)
896 return obj
897
898
899@overload
900def traverse(
901 obj: Literal[None],
902 opts: Mapping[str, Any],
903 visitors: Mapping[str, _TraverseCallableType[Any]],
904) -> None: ...
905
906
907@overload
908def traverse(
909 obj: ExternallyTraversible,
910 opts: Mapping[str, Any],
911 visitors: Mapping[str, _TraverseCallableType[Any]],
912) -> ExternallyTraversible: ...
913
914
915def traverse(
916 obj: Optional[ExternallyTraversible],
917 opts: Mapping[str, Any],
918 visitors: Mapping[str, _TraverseCallableType[Any]],
919) -> Optional[ExternallyTraversible]:
920 """Traverse and visit the given expression structure using the default
921 iterator.
922
923 e.g.::
924
925 from sqlalchemy.sql import visitors
926
927 stmt = select(some_table).where(some_table.c.foo == "bar")
928
929
930 def visit_bindparam(bind_param):
931 print("found bound value: %s" % bind_param.value)
932
933
934 visitors.traverse(stmt, {}, {"bindparam": visit_bindparam})
935
936 The iteration of objects uses the :func:`.visitors.iterate` function,
937 which does a breadth-first traversal using a stack.
938
939 :param obj: :class:`_expression.ClauseElement` structure to be traversed
940
941 :param opts: dictionary of iteration options. This dictionary is usually
942 empty in modern usage.
943
944 :param visitors: dictionary of visit functions. The dictionary should
945 have strings as keys, each of which would correspond to the
946 ``__visit_name__`` of a particular kind of SQL expression object, and
947 callable functions as values, each of which represents a visitor function
948 for that kind of object.
949
950 """
951 return traverse_using(iterate(obj, opts), obj, visitors)
952
953
954@overload
955def cloned_traverse(
956 obj: Literal[None],
957 opts: Mapping[str, Any],
958 visitors: Mapping[str, _TraverseCallableType[Any]],
959) -> None: ...
960
961
962# a bit of controversy here, as the clone of the lead element
963# *could* in theory replace with an entirely different kind of element.
964# however this is really not how cloned_traverse is ever used internally
965# at least.
966@overload
967def cloned_traverse(
968 obj: _ET,
969 opts: Mapping[str, Any],
970 visitors: Mapping[str, _TraverseCallableType[Any]],
971) -> _ET: ...
972
973
974def cloned_traverse(
975 obj: Optional[ExternallyTraversible],
976 opts: Mapping[str, Any],
977 visitors: Mapping[str, _TraverseCallableType[Any]],
978) -> Optional[ExternallyTraversible]:
979 """Clone the given expression structure, allowing modifications by
980 visitors for mutable objects.
981
982 Traversal usage is the same as that of :func:`.visitors.traverse`.
983 The visitor functions present in the ``visitors`` dictionary may also
984 modify the internals of the given structure as the traversal proceeds.
985
986 The :func:`.cloned_traverse` function does **not** provide objects that are
987 part of the :class:`.Immutable` interface to the visit methods (this
988 primarily includes :class:`.ColumnClause`, :class:`.Column`,
989 :class:`.TableClause` and :class:`.Table` objects). As this traversal is
990 only intended to allow in-place mutation of objects, :class:`.Immutable`
991 objects are skipped. The :meth:`.Immutable._clone` method is still called
992 on each object to allow for objects to replace themselves with a different
993 object based on a clone of their sub-internals (e.g. a
994 :class:`.ColumnClause` that clones its subquery to return a new
995 :class:`.ColumnClause`).
996
997 .. versionchanged:: 2.0 The :func:`.cloned_traverse` function omits
998 objects that are part of the :class:`.Immutable` interface.
999
1000 The central API feature used by the :func:`.visitors.cloned_traverse`
1001 and :func:`.visitors.replacement_traverse` functions, in addition to the
1002 :meth:`_expression.ClauseElement.get_children`
1003 function that is used to achieve
1004 the iteration, is the :meth:`_expression.ClauseElement._copy_internals`
1005 method.
1006 For a :class:`_expression.ClauseElement`
1007 structure to support cloning and replacement
1008 traversals correctly, it needs to be able to pass a cloning function into
1009 its internal members in order to make copies of them.
1010
1011 .. seealso::
1012
1013 :func:`.visitors.traverse`
1014
1015 :func:`.visitors.replacement_traverse`
1016
1017 """
1018
1019 cloned: Dict[int, ExternallyTraversible] = {}
1020 stop_on = set(opts.get("stop_on", []))
1021
1022 def deferred_copy_internals(
1023 obj: ExternallyTraversible,
1024 ) -> ExternallyTraversible:
1025 return cloned_traverse(obj, opts, visitors)
1026
1027 def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible:
1028 if elem in stop_on:
1029 return elem
1030 else:
1031 if id(elem) not in cloned:
1032 if "replace" in kw:
1033 newelem = cast(
1034 Optional[ExternallyTraversible], kw["replace"](elem)
1035 )
1036 if newelem is not None:
1037 cloned[id(elem)] = newelem
1038 return newelem
1039
1040 # the _clone method for immutable normally returns "self".
1041 # however, the method is still allowed to return a
1042 # different object altogether; ColumnClause._clone() will
1043 # based on options clone the subquery to which it is associated
1044 # and return the new corresponding column.
1045 cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw)
1046 newelem._copy_internals(clone=clone, **kw)
1047
1048 # however, visit methods which are tasked with in-place
1049 # mutation of the object should not get access to the immutable
1050 # object.
1051 if not elem._is_immutable:
1052 meth = visitors.get(newelem.__visit_name__, None)
1053 if meth:
1054 meth(newelem)
1055 return cloned[id(elem)]
1056
1057 if obj is not None:
1058 obj = clone(
1059 obj, deferred_copy_internals=deferred_copy_internals, **opts
1060 )
1061 clone = None # type: ignore[assignment] # remove gc cycles
1062 return obj
1063
1064
1065@overload
1066def replacement_traverse(
1067 obj: Literal[None],
1068 opts: Mapping[str, Any],
1069 replace: _TraverseTransformCallableType[Any],
1070) -> None: ...
1071
1072
1073@overload
1074def replacement_traverse(
1075 obj: _CE,
1076 opts: Mapping[str, Any],
1077 replace: _TraverseTransformCallableType[Any],
1078) -> _CE: ...
1079
1080
1081@overload
1082def replacement_traverse(
1083 obj: ExternallyTraversible,
1084 opts: Mapping[str, Any],
1085 replace: _TraverseTransformCallableType[Any],
1086) -> ExternallyTraversible: ...
1087
1088
1089def replacement_traverse(
1090 obj: Optional[ExternallyTraversible],
1091 opts: Mapping[str, Any],
1092 replace: _TraverseTransformCallableType[Any],
1093) -> Optional[ExternallyTraversible]:
1094 """Clone the given expression structure, allowing element
1095 replacement by a given replacement function.
1096
1097 This function is very similar to the :func:`.visitors.cloned_traverse`
1098 function, except instead of being passed a dictionary of visitors, all
1099 elements are unconditionally passed into the given replace function.
1100 The replace function then has the option to return an entirely new object
1101 which will replace the one given. If it returns ``None``, then the object
1102 is kept in place.
1103
1104 The difference in usage between :func:`.visitors.cloned_traverse` and
1105 :func:`.visitors.replacement_traverse` is that in the former case, an
1106 already-cloned object is passed to the visitor function, and the visitor
1107 function can then manipulate the internal state of the object.
1108 In the case of the latter, the visitor function should only return an
1109 entirely different object, or do nothing.
1110
1111 The use case for :func:`.visitors.replacement_traverse` is that of
1112 replacing a FROM clause inside of a SQL structure with a different one,
1113 as is a common use case within the ORM.
1114
1115 """
1116
1117 cloned = {}
1118 stop_on = {id(x) for x in opts.get("stop_on", [])}
1119
1120 def deferred_copy_internals(
1121 obj: ExternallyTraversible,
1122 ) -> ExternallyTraversible:
1123 return replacement_traverse(obj, opts, replace)
1124
1125 def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible:
1126 if (
1127 id(elem) in stop_on
1128 or "no_replacement_traverse" in elem._annotations
1129 ):
1130 return elem
1131 else:
1132 newelem = replace(elem)
1133 if newelem is not None:
1134 stop_on.add(id(newelem))
1135 return newelem # type: ignore
1136 else:
1137 # base "already seen" on id(), not hash, so that we don't
1138 # replace an Annotated element with its non-annotated one, and
1139 # vice versa
1140 id_elem = id(elem)
1141 if id_elem not in cloned:
1142 if "replace" in kw:
1143 newelem = kw["replace"](elem)
1144 if newelem is not None:
1145 cloned[id_elem] = newelem
1146 return newelem # type: ignore
1147
1148 cloned[id_elem] = newelem = elem._clone(**kw)
1149 newelem._copy_internals(clone=clone, **kw)
1150 return cloned[id_elem] # type: ignore
1151
1152 if obj is not None:
1153 obj = clone(
1154 obj, deferred_copy_internals=deferred_copy_internals, **opts
1155 )
1156 clone = None # type: ignore[assignment] # remove gc cycles
1157 return obj