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