Team Ai
Datasetpublic

codekingpro/portable-devtools

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