Team Ai
Datasetpublic

codekingpro/portable-devtools

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