1# sql/visitors.py
2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8"""Visitor/traversal interface and library functions.
9
10
11"""
12
13from __future__ import annotations
14
15from collections import deque
16from enum import Enum
17import itertools
18import operator
19import typing
20from typing import Any
21from typing import Callable
22from typing import cast
23from typing import ClassVar
24from typing import Dict
25from typing import Iterable
26from typing import Iterator
27from typing import List
28from typing import Mapping
29from typing import Optional
30from typing import overload
31from typing import Protocol
32from typing import Tuple
33from typing import Type
34from typing import TYPE_CHECKING
35from typing import TypeVar
36from typing import Union
37
38from ._util_cy import anon_map as anon_map
39from ._util_cy import prefix_anon_map as prefix_anon_map # noqa: F401
40from .. import exc
41from .. import util
42from ..util import langhelpers
43from ..util.typing import Literal
44from ..util.typing import Self
45
46if TYPE_CHECKING:
47 from .annotation import _AnnotationDict
48 from .elements import ColumnElement
49
50
51__all__ = [
52 "iterate",
53 "traverse_using",
54 "traverse",
55 "cloned_traverse",
56 "replacement_traverse",
57 "Visitable",
58 "ExternalTraversal",
59 "InternalTraversal",
60 "anon_map",
61]
62
63
64class _CompilerDispatchType(Protocol):
65 def __call__(_self, self: Visitable, visitor: Any, **kw: Any) -> Any: ...
66
67
68class Visitable:
69 """Base class for visitable objects.
70
71 :class:`.Visitable` is used to implement the SQL compiler dispatch
72 functions. Other forms of traversal such as for cache key generation
73 are implemented separately using the :class:`.HasTraverseInternals`
74 interface.
75
76 .. versionchanged:: 2.0 The :class:`.Visitable` class was named
77 :class:`.Traversible` in the 1.4 series; the name is changed back
78 to :class:`.Visitable` in 2.0 which is what it was prior to 1.4.
79
80 Both names remain importable in both 1.4 and 2.0 versions.
81
82 """
83
84 __slots__ = ()
85
86 __visit_name__: str
87
88 _original_compiler_dispatch: _CompilerDispatchType
89
90 if typing.TYPE_CHECKING:
91
92 def _compiler_dispatch(self, visitor: Any, **kw: Any) -> str: ...
93
94 def __init_subclass__(cls) -> None:
95 if "__visit_name__" in cls.__dict__:
96 cls._generate_compiler_dispatch()
97 super().__init_subclass__()
98
99 @classmethod
100 def _generate_compiler_dispatch(cls) -> None:
101 visit_name = cls.__visit_name__
102
103 if "_compiler_dispatch" in cls.__dict__:
104 # class has a fixed _compiler_dispatch() method.
105 # copy it to "original" so that we can get it back if
106 # sqlalchemy.ext.compiles overrides it.
107 cls._original_compiler_dispatch = cls._compiler_dispatch
108 return
109
110 if not isinstance(visit_name, str):
111 raise exc.InvalidRequestError(
112 f"__visit_name__ on class {cls.__name__} must be a string "
113 "at the class level"
114 )
115
116 name = "visit_%s" % visit_name
117 getter = operator.attrgetter(name)
118
119 def _compiler_dispatch(
120 self: Visitable, visitor: Any, **kw: Any
121 ) -> str:
122 """Look for an attribute named "visit_<visit_name>" on the
123 visitor, and call it with the same kw params.
124
125 """
126 try:
127 meth = getter(visitor)
128 except AttributeError as err:
129 return visitor.visit_unsupported_compilation(self, err, **kw) # type: ignore # noqa: E501
130 else:
131 return meth(self, **kw) # type: ignore # noqa: E501
132
133 cls._compiler_dispatch = ( # type: ignore
134 cls._original_compiler_dispatch
135 ) = _compiler_dispatch
136
137 def __class_getitem__(cls, key: Any) -> Any:
138 # allow generic classes in py3.9+
139 return cls
140
141
142class InternalTraversal(Enum):
143 r"""Defines visitor symbols used for internal traversal.
144
145 The :class:`.InternalTraversal` class is used in two ways. One is that
146 it can serve as the superclass for an object that implements the
147 various visit methods of the class. The other is that the symbols
148 themselves of :class:`.InternalTraversal` are used within
149 the ``_traverse_internals`` collection. Such as, the :class:`.Case`
150 object defines ``_traverse_internals`` as ::
151
152 class Case(ColumnElement[_T]):
153 _traverse_internals = [
154 ("value", InternalTraversal.dp_clauseelement),
155 ("whens", InternalTraversal.dp_clauseelement_tuples),
156 ("else_", InternalTraversal.dp_clauseelement),
157 ]
158
159 Above, the :class:`.Case` class indicates its internal state as the
160 attributes named ``value``, ``whens``, and ``else_``. They each
161 link to an :class:`.InternalTraversal` method which indicates the type
162 of datastructure to which each attribute refers.
163
164 Using the ``_traverse_internals`` structure, objects of type
165 :class:`.InternalTraversible` will have the following methods automatically
166 implemented:
167
168 * :meth:`.HasTraverseInternals.get_children`
169
170 * :meth:`.HasTraverseInternals._copy_internals`
171
172 * :meth:`.HasCacheKey._gen_cache_key`
173
174 Subclasses can also implement these methods directly, particularly for the
175 :meth:`.HasTraverseInternals._copy_internals` method, when special steps
176 are needed.
177
178 .. versionadded:: 1.4
179
180 """
181
182 dp_has_cache_key = "HC"
183 """Visit a :class:`.HasCacheKey` object."""
184
185 dp_has_cache_key_list = "HL"
186 """Visit a list of :class:`.HasCacheKey` objects."""
187
188 dp_clauseelement = "CE"
189 """Visit a :class:`_expression.ClauseElement` object."""
190
191 dp_fromclause_canonical_column_collection = "FC"
192 """Visit a :class:`_expression.FromClause` object in the context of the
193 ``columns`` attribute.
194
195 The column collection is "canonical", meaning it is the originally
196 defined location of the :class:`.ColumnClause` objects. Right now
197 this means that the object being visited is a
198 :class:`_expression.TableClause`
199 or :class:`_schema.Table` object only.
200
201 """
202
203 dp_clauseelement_tuples = "CTS"
204 """Visit a list of tuples which contain :class:`_expression.ClauseElement`
205 objects.
206
207 """
208
209 dp_clauseelement_list = "CL"
210 """Visit a list of :class:`_expression.ClauseElement` objects.
211
212 """
213
214 dp_clauseelement_tuple = "CT"
215 """Visit a tuple of :class:`_expression.ClauseElement` objects.
216
217 """
218
219 dp_executable_options = "EO"
220
221 dp_with_context_options = "WC"
222
223 dp_fromclause_ordered_set = "CO"
224 """Visit an ordered set of :class:`_expression.FromClause` objects. """
225
226 dp_string = "S"
227 """Visit a plain string value.
228
229 Examples include table and column names, bound parameter keys, special
230 keywords such as "UNION", "UNION ALL".
231
232 The string value is considered to be significant for cache key
233 generation.
234
235 """
236
237 dp_string_list = "SL"
238 """Visit a list of strings."""
239
240 dp_anon_name = "AN"
241 """Visit a potentially "anonymized" string value.
242
243 The string value is considered to be significant for cache key
244 generation.
245
246 """
247
248 dp_boolean = "B"
249 """Visit a boolean value.
250
251 The boolean value is considered to be significant for cache key
252 generation.
253
254 """
255
256 dp_operator = "O"
257 """Visit an operator.
258
259 The operator is a function from the :mod:`sqlalchemy.sql.operators`
260 module.
261
262 The operator value is considered to be significant for cache key
263 generation.
264
265 """
266
267 dp_type = "T"
268 """Visit a :class:`.TypeEngine` object
269
270 The type object is considered to be significant for cache key
271 generation.
272
273 """
274
275 dp_plain_dict = "PD"
276 """Visit a dictionary with string keys.
277
278 The keys of the dictionary should be strings, the values should
279 be immutable and hashable. The dictionary is considered to be
280 significant for cache key generation.
281
282 """
283
284 dp_dialect_options = "DO"
285 """Visit a dialect options structure."""
286
287 dp_string_clauseelement_dict = "CD"
288 """Visit a dictionary of string keys to :class:`_expression.ClauseElement`
289 objects.
290
291 """
292
293 dp_string_multi_dict = "MD"
294 """Visit a dictionary of string keys to values which may either be
295 plain immutable/hashable or :class:`.HasCacheKey` objects.
296
297 """
298
299 dp_annotations_key = "AK"
300 """Visit the _annotations_cache_key element.
301
302 This is a dictionary of additional information about a ClauseElement
303 that modifies its role. It should be included when comparing or caching
304 objects, however generating this key is relatively expensive. Visitors
305 should check the "_annotations" dict for non-None first before creating
306 this key.
307
308 """
309
310 dp_plain_obj = "PO"
311 """Visit a plain python object.
312
313 The value should be immutable and hashable, such as an integer.
314 The value is considered to be significant for cache key generation.
315
316 """
317
318 dp_named_ddl_element = "DD"
319 """Visit a simple named DDL element.
320
321 The current object used by this method is the :class:`.Sequence`.
322
323 The object is only considered to be important for cache key generation
324 as far as its name, but not any other aspects of it.
325
326 """
327
328 dp_prefix_sequence = "PS"
329 """Visit the sequence represented by :class:`_expression.HasPrefixes`
330 or :class:`_expression.HasSuffixes`.
331
332 """
333
334 dp_table_hint_list = "TH"
335 """Visit the ``_hints`` collection of a :class:`_expression.Select`
336 object.
337
338 """
339
340 dp_setup_join_tuple = "SJ"
341
342 dp_memoized_select_entities = "ME"
343
344 dp_statement_hint_list = "SH"
345 """Visit the ``_statement_hints`` collection of a
346 :class:`_expression.Select`
347 object.
348
349 """
350
351 dp_unknown_structure = "UK"
352 """Visit an unknown structure.
353
354 """
355
356 dp_dml_ordered_values = "DML_OV"
357 """Visit the values() ordered tuple list of an
358 :class:`_expression.Update` object."""
359
360 dp_dml_values = "DML_V"
361 """Visit the values() dictionary of a :class:`.ValuesBase`
362 (e.g. Insert or Update) object.
363
364 """
365
366 dp_dml_multi_values = "DML_MV"
367 """Visit the values() multi-valued list of dictionaries of an
368 :class:`_expression.Insert` object.
369
370 """
371
372 dp_propagate_attrs = "PA"
373 """Visit the propagate attrs dict. This hardcodes to the particular
374 elements we care about right now."""
375
376 """Symbols that follow are additional symbols that are useful in
377 caching applications.
378
379 Traversals for :class:`_expression.ClauseElement` objects only need to use
380 those symbols present in :class:`.InternalTraversal`. However, for
381 additional caching use cases within the ORM, symbols dealing with the
382 :class:`.HasCacheKey` class are added here.
383
384 """
385
386 dp_ignore = "IG"
387 """Specify an object that should be ignored entirely.
388
389 This currently applies function call argument caching where some
390 arguments should not be considered to be part of a cache key.
391
392 """
393
394 dp_inspectable = "IS"
395 """Visit an inspectable object where the return value is a
396 :class:`.HasCacheKey` object."""
397
398 dp_multi = "M"
399 """Visit an object that may be a :class:`.HasCacheKey` or may be a
400 plain hashable object."""
401
402 dp_multi_list = "MT"
403 """Visit a tuple containing elements that may be :class:`.HasCacheKey` or
404 may be a plain hashable object."""
405
406 dp_has_cache_key_tuples = "HT"
407 """Visit a list of tuples which contain :class:`.HasCacheKey`
408 objects.
409
410 """
411
412 dp_inspectable_list = "IL"
413 """Visit a list of inspectable objects which upon inspection are
414 HasCacheKey objects."""
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 def visit_bindparam(bind_param):
930 print("found bound value: %s" % bind_param.value)
931
932 visitors.traverse(stmt, {}, {"bindparam": visit_bindparam})
933
934 The iteration of objects uses the :func:`.visitors.iterate` function,
935 which does a breadth-first traversal using a stack.
936
937 :param obj: :class:`_expression.ClauseElement` structure to be traversed
938
939 :param opts: dictionary of iteration options. This dictionary is usually
940 empty in modern usage.
941
942 :param visitors: dictionary of visit functions. The dictionary should
943 have strings as keys, each of which would correspond to the
944 ``__visit_name__`` of a particular kind of SQL expression object, and
945 callable functions as values, each of which represents a visitor function
946 for that kind of object.
947
948 """
949 return traverse_using(iterate(obj, opts), obj, visitors)
950
951
952@overload
953def cloned_traverse(
954 obj: Literal[None],
955 opts: Mapping[str, Any],
956 visitors: Mapping[str, _TraverseCallableType[Any]],
957) -> None: ...
958
959
960# a bit of controversy here, as the clone of the lead element
961# *could* in theory replace with an entirely different kind of element.
962# however this is really not how cloned_traverse is ever used internally
963# at least.
964@overload
965def cloned_traverse(
966 obj: _ET,
967 opts: Mapping[str, Any],
968 visitors: Mapping[str, _TraverseCallableType[Any]],
969) -> _ET: ...
970
971
972def cloned_traverse(
973 obj: Optional[ExternallyTraversible],
974 opts: Mapping[str, Any],
975 visitors: Mapping[str, _TraverseCallableType[Any]],
976) -> Optional[ExternallyTraversible]:
977 """Clone the given expression structure, allowing modifications by
978 visitors for mutable objects.
979
980 Traversal usage is the same as that of :func:`.visitors.traverse`.
981 The visitor functions present in the ``visitors`` dictionary may also
982 modify the internals of the given structure as the traversal proceeds.
983
984 The :func:`.cloned_traverse` function does **not** provide objects that are
985 part of the :class:`.Immutable` interface to the visit methods (this
986 primarily includes :class:`.ColumnClause`, :class:`.Column`,
987 :class:`.TableClause` and :class:`.Table` objects). As this traversal is
988 only intended to allow in-place mutation of objects, :class:`.Immutable`
989 objects are skipped. The :meth:`.Immutable._clone` method is still called
990 on each object to allow for objects to replace themselves with a different
991 object based on a clone of their sub-internals (e.g. a
992 :class:`.ColumnClause` that clones its subquery to return a new
993 :class:`.ColumnClause`).
994
995 .. versionchanged:: 2.0 The :func:`.cloned_traverse` function omits
996 objects that are part of the :class:`.Immutable` interface.
997
998 The central API feature used by the :func:`.visitors.cloned_traverse`
999 and :func:`.visitors.replacement_traverse` functions, in addition to the
1000 :meth:`_expression.ClauseElement.get_children`
1001 function that is used to achieve
1002 the iteration, is the :meth:`_expression.ClauseElement._copy_internals`
1003 method.
1004 For a :class:`_expression.ClauseElement`
1005 structure to support cloning and replacement
1006 traversals correctly, it needs to be able to pass a cloning function into
1007 its internal members in order to make copies of them.
1008
1009 .. seealso::
1010
1011 :func:`.visitors.traverse`
1012
1013 :func:`.visitors.replacement_traverse`
1014
1015 """
1016
1017 cloned: Dict[int, ExternallyTraversible] = {}
1018 stop_on = set(opts.get("stop_on", []))
1019
1020 def deferred_copy_internals(
1021 obj: ExternallyTraversible,
1022 ) -> ExternallyTraversible:
1023 return cloned_traverse(obj, opts, visitors)
1024
1025 def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible:
1026 if elem in stop_on:
1027 return elem
1028 else:
1029 if id(elem) not in cloned:
1030 if "replace" in kw:
1031 newelem = cast(
1032 Optional[ExternallyTraversible], kw["replace"](elem)
1033 )
1034 if newelem is not None:
1035 cloned[id(elem)] = newelem
1036 return newelem
1037
1038 # the _clone method for immutable normally returns "self".
1039 # however, the method is still allowed to return a
1040 # different object altogether; ColumnClause._clone() will
1041 # based on options clone the subquery to which it is associated
1042 # and return the new corresponding column.
1043 cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw)
1044 newelem._copy_internals(clone=clone, **kw)
1045
1046 # however, visit methods which are tasked with in-place
1047 # mutation of the object should not get access to the immutable
1048 # object.
1049 if not elem._is_immutable:
1050 meth = visitors.get(newelem.__visit_name__, None)
1051 if meth:
1052 meth(newelem)
1053 return cloned[id(elem)]
1054
1055 if obj is not None:
1056 obj = clone(
1057 obj, deferred_copy_internals=deferred_copy_internals, **opts
1058 )
1059 clone = None # type: ignore[assignment] # remove gc cycles
1060 return obj
1061
1062
1063@overload
1064def replacement_traverse(
1065 obj: Literal[None],
1066 opts: Mapping[str, Any],
1067 replace: _TraverseTransformCallableType[Any],
1068) -> None: ...
1069
1070
1071@overload
1072def replacement_traverse(
1073 obj: _CE,
1074 opts: Mapping[str, Any],
1075 replace: _TraverseTransformCallableType[Any],
1076) -> _CE: ...
1077
1078
1079@overload
1080def replacement_traverse(
1081 obj: ExternallyTraversible,
1082 opts: Mapping[str, Any],
1083 replace: _TraverseTransformCallableType[Any],
1084) -> ExternallyTraversible: ...
1085
1086
1087def replacement_traverse(
1088 obj: Optional[ExternallyTraversible],
1089 opts: Mapping[str, Any],
1090 replace: _TraverseTransformCallableType[Any],
1091) -> Optional[ExternallyTraversible]:
1092 """Clone the given expression structure, allowing element
1093 replacement by a given replacement function.
1094
1095 This function is very similar to the :func:`.visitors.cloned_traverse`
1096 function, except instead of being passed a dictionary of visitors, all
1097 elements are unconditionally passed into the given replace function.
1098 The replace function then has the option to return an entirely new object
1099 which will replace the one given. If it returns ``None``, then the object
1100 is kept in place.
1101
1102 The difference in usage between :func:`.visitors.cloned_traverse` and
1103 :func:`.visitors.replacement_traverse` is that in the former case, an
1104 already-cloned object is passed to the visitor function, and the visitor
1105 function can then manipulate the internal state of the object.
1106 In the case of the latter, the visitor function should only return an
1107 entirely different object, or do nothing.
1108
1109 The use case for :func:`.visitors.replacement_traverse` is that of
1110 replacing a FROM clause inside of a SQL structure with a different one,
1111 as is a common use case within the ORM.
1112
1113 """
1114
1115 cloned = {}
1116 stop_on = {id(x) for x in opts.get("stop_on", [])}
1117
1118 def deferred_copy_internals(
1119 obj: ExternallyTraversible,
1120 ) -> ExternallyTraversible:
1121 return replacement_traverse(obj, opts, replace)
1122
1123 def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible:
1124 if (
1125 id(elem) in stop_on
1126 or "no_replacement_traverse" in elem._annotations
1127 ):
1128 return elem
1129 else:
1130 newelem = replace(elem)
1131 if newelem is not None:
1132 stop_on.add(id(newelem))
1133 return newelem # type: ignore
1134 else:
1135 # base "already seen" on id(), not hash, so that we don't
1136 # replace an Annotated element with its non-annotated one, and
1137 # vice versa
1138 id_elem = id(elem)
1139 if id_elem not in cloned:
1140 if "replace" in kw:
1141 newelem = kw["replace"](elem)
1142 if newelem is not None:
1143 cloned[id_elem] = newelem
1144 return newelem # type: ignore
1145
1146 cloned[id_elem] = newelem = elem._clone(**kw)
1147 newelem._copy_internals(clone=clone, **kw)
1148 return cloned[id_elem] # type: ignore
1149
1150 if obj is not None:
1151 obj = clone(
1152 obj, deferred_copy_internals=deferred_copy_internals, **opts
1153 )
1154 clone = None # type: ignore[assignment] # remove gc cycles
1155 return obj