You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							853 lines
						
					
					
						
							27 KiB
						
					
					
				
			
		
		
	
	
							853 lines
						
					
					
						
							27 KiB
						
					
					
				# sql/visitors.py
 | 
						|
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
 | 
						|
# <see AUTHORS file>
 | 
						|
#
 | 
						|
# This module is part of SQLAlchemy and is released under
 | 
						|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
 | 
						|
 | 
						|
"""Visitor/traversal interface and library functions.
 | 
						|
 | 
						|
SQLAlchemy schema and expression constructs rely on a Python-centric
 | 
						|
version of the classic "visitor" pattern as the primary way in which
 | 
						|
they apply functionality.  The most common use of this pattern
 | 
						|
is statement compilation, where individual expression classes match
 | 
						|
up to rendering methods that produce a string result.   Beyond this,
 | 
						|
the visitor system is also used to inspect expressions for various
 | 
						|
information and patterns, as well as for the purposes of applying
 | 
						|
transformations to expressions.
 | 
						|
 | 
						|
Examples of how the visit system is used can be seen in the source code
 | 
						|
of for example the ``sqlalchemy.sql.util`` and the ``sqlalchemy.sql.compiler``
 | 
						|
modules.  Some background on clause adaption is also at
 | 
						|
https://techspot.zzzeek.org/2008/01/23/expression-transformations/ .
 | 
						|
 | 
						|
"""
 | 
						|
 | 
						|
from collections import deque
 | 
						|
import itertools
 | 
						|
import operator
 | 
						|
 | 
						|
from .. import exc
 | 
						|
from .. import util
 | 
						|
from ..util import langhelpers
 | 
						|
from ..util import symbol
 | 
						|
 | 
						|
__all__ = [
 | 
						|
    "iterate",
 | 
						|
    "traverse_using",
 | 
						|
    "traverse",
 | 
						|
    "cloned_traverse",
 | 
						|
    "replacement_traverse",
 | 
						|
    "Traversible",
 | 
						|
    "TraversibleType",
 | 
						|
    "ExternalTraversal",
 | 
						|
    "InternalTraversal",
 | 
						|
]
 | 
						|
 | 
						|
 | 
						|
def _generate_compiler_dispatch(cls):
 | 
						|
    """Generate a _compiler_dispatch() external traversal on classes with a
 | 
						|
    __visit_name__ attribute.
 | 
						|
 | 
						|
    """
 | 
						|
    visit_name = cls.__visit_name__
 | 
						|
 | 
						|
    if "_compiler_dispatch" in cls.__dict__:
 | 
						|
        # class has a fixed _compiler_dispatch() method.
 | 
						|
        # copy it to "original" so that we can get it back if
 | 
						|
        # sqlalchemy.ext.compiles overrides it.
 | 
						|
        cls._original_compiler_dispatch = cls._compiler_dispatch
 | 
						|
        return
 | 
						|
 | 
						|
    if not isinstance(visit_name, util.compat.string_types):
 | 
						|
        raise exc.InvalidRequestError(
 | 
						|
            "__visit_name__ on class %s must be a string at the class level"
 | 
						|
            % cls.__name__
 | 
						|
        )
 | 
						|
 | 
						|
    name = "visit_%s" % visit_name
 | 
						|
    getter = operator.attrgetter(name)
 | 
						|
 | 
						|
    def _compiler_dispatch(self, visitor, **kw):
 | 
						|
        """Look for an attribute named "visit_<visit_name>" on the
 | 
						|
        visitor, and call it with the same kw params.
 | 
						|
 | 
						|
        """
 | 
						|
        try:
 | 
						|
            meth = getter(visitor)
 | 
						|
        except AttributeError as err:
 | 
						|
            return visitor.visit_unsupported_compilation(self, err, **kw)
 | 
						|
 | 
						|
        else:
 | 
						|
            return meth(self, **kw)
 | 
						|
 | 
						|
    cls._compiler_dispatch = (
 | 
						|
        cls._original_compiler_dispatch
 | 
						|
    ) = _compiler_dispatch
 | 
						|
 | 
						|
 | 
						|
class TraversibleType(type):
 | 
						|
    """Metaclass which assigns dispatch attributes to various kinds of
 | 
						|
    "visitable" classes.
 | 
						|
 | 
						|
    Attributes include:
 | 
						|
 | 
						|
    * The ``_compiler_dispatch`` method, corresponding to ``__visit_name__``.
 | 
						|
      This is called "external traversal" because the caller of each visit()
 | 
						|
      method is responsible for sub-traversing the inner elements of each
 | 
						|
      object. This is appropriate for string compilers and other traversals
 | 
						|
      that need to call upon the inner elements in a specific pattern.
 | 
						|
 | 
						|
    * internal traversal collections ``_children_traversal``,
 | 
						|
      ``_cache_key_traversal``, ``_copy_internals_traversal``, generated from
 | 
						|
      an optional ``_traverse_internals`` collection of symbols which comes
 | 
						|
      from the :class:`.InternalTraversal` list of symbols.  This is called
 | 
						|
      "internal traversal" MARKMARK
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(cls, clsname, bases, clsdict):
 | 
						|
        if clsname != "Traversible":
 | 
						|
            if "__visit_name__" in clsdict:
 | 
						|
                _generate_compiler_dispatch(cls)
 | 
						|
 | 
						|
        super(TraversibleType, cls).__init__(clsname, bases, clsdict)
 | 
						|
 | 
						|
 | 
						|
class Traversible(util.with_metaclass(TraversibleType)):
 | 
						|
    """Base class for visitable objects, applies the
 | 
						|
    :class:`.visitors.TraversibleType` metaclass.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def __class_getitem__(cls, key):
 | 
						|
        # allow generic classes in py3.9+
 | 
						|
        return cls
 | 
						|
 | 
						|
    @util.preload_module("sqlalchemy.sql.traversals")
 | 
						|
    def get_children(self, omit_attrs=(), **kw):
 | 
						|
        r"""Return immediate child :class:`.visitors.Traversible`
 | 
						|
        elements of this :class:`.visitors.Traversible`.
 | 
						|
 | 
						|
        This is used for visit traversal.
 | 
						|
 | 
						|
        \**kw may contain flags that change the collection that is
 | 
						|
        returned, for example to return a subset of items in order to
 | 
						|
        cut down on larger traversals, or to return child items from a
 | 
						|
        different context (such as schema-level collections instead of
 | 
						|
        clause-level).
 | 
						|
 | 
						|
        """
 | 
						|
 | 
						|
        traversals = util.preloaded.sql_traversals
 | 
						|
 | 
						|
        try:
 | 
						|
            traverse_internals = self._traverse_internals
 | 
						|
        except AttributeError:
 | 
						|
            # user-defined classes may not have a _traverse_internals
 | 
						|
            return []
 | 
						|
 | 
						|
        dispatch = traversals._get_children.run_generated_dispatch
 | 
						|
        return itertools.chain.from_iterable(
 | 
						|
            meth(obj, **kw)
 | 
						|
            for attrname, obj, meth in dispatch(
 | 
						|
                self, traverse_internals, "_generated_get_children_traversal"
 | 
						|
            )
 | 
						|
            if attrname not in omit_attrs and obj is not None
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class _InternalTraversalType(type):
 | 
						|
    def __init__(cls, clsname, bases, clsdict):
 | 
						|
        if cls.__name__ in ("InternalTraversal", "ExtendedInternalTraversal"):
 | 
						|
            lookup = {}
 | 
						|
            for key, sym in clsdict.items():
 | 
						|
                if key.startswith("dp_"):
 | 
						|
                    visit_key = key.replace("dp_", "visit_")
 | 
						|
                    sym_name = sym.name
 | 
						|
                    assert sym_name not in lookup, sym_name
 | 
						|
                    lookup[sym] = lookup[sym_name] = visit_key
 | 
						|
            if hasattr(cls, "_dispatch_lookup"):
 | 
						|
                lookup.update(cls._dispatch_lookup)
 | 
						|
            cls._dispatch_lookup = lookup
 | 
						|
 | 
						|
        super(_InternalTraversalType, cls).__init__(clsname, bases, clsdict)
 | 
						|
 | 
						|
 | 
						|
def _generate_dispatcher(visitor, internal_dispatch, method_name):
 | 
						|
    names = []
 | 
						|
    for attrname, visit_sym in internal_dispatch:
 | 
						|
        meth = visitor.dispatch(visit_sym)
 | 
						|
        if meth:
 | 
						|
            visit_name = ExtendedInternalTraversal._dispatch_lookup[visit_sym]
 | 
						|
            names.append((attrname, visit_name))
 | 
						|
 | 
						|
    code = (
 | 
						|
        ("    return [\n")
 | 
						|
        + (
 | 
						|
            ", \n".join(
 | 
						|
                "        (%r, self.%s, visitor.%s)"
 | 
						|
                % (attrname, attrname, visit_name)
 | 
						|
                for attrname, visit_name in names
 | 
						|
            )
 | 
						|
        )
 | 
						|
        + ("\n    ]\n")
 | 
						|
    )
 | 
						|
    meth_text = ("def %s(self, visitor):\n" % method_name) + code + "\n"
 | 
						|
    # print(meth_text)
 | 
						|
    return langhelpers._exec_code_in_env(meth_text, {}, method_name)
 | 
						|
 | 
						|
 | 
						|
class InternalTraversal(util.with_metaclass(_InternalTraversalType, object)):
 | 
						|
    r"""Defines visitor symbols used for internal traversal.
 | 
						|
 | 
						|
    The :class:`.InternalTraversal` class is used in two ways.  One is that
 | 
						|
    it can serve as the superclass for an object that implements the
 | 
						|
    various visit methods of the class.   The other is that the symbols
 | 
						|
    themselves of :class:`.InternalTraversal` are used within
 | 
						|
    the ``_traverse_internals`` collection.   Such as, the :class:`.Case`
 | 
						|
    object defines ``_traverse_internals`` as ::
 | 
						|
 | 
						|
        _traverse_internals = [
 | 
						|
            ("value", InternalTraversal.dp_clauseelement),
 | 
						|
            ("whens", InternalTraversal.dp_clauseelement_tuples),
 | 
						|
            ("else_", InternalTraversal.dp_clauseelement),
 | 
						|
        ]
 | 
						|
 | 
						|
    Above, the :class:`.Case` class indicates its internal state as the
 | 
						|
    attributes named ``value``, ``whens``, and ``else_``.    They each
 | 
						|
    link to an :class:`.InternalTraversal` method which indicates the type
 | 
						|
    of datastructure referred towards.
 | 
						|
 | 
						|
    Using the ``_traverse_internals`` structure, objects of type
 | 
						|
    :class:`.InternalTraversible` will have the following methods automatically
 | 
						|
    implemented:
 | 
						|
 | 
						|
    * :meth:`.Traversible.get_children`
 | 
						|
 | 
						|
    * :meth:`.Traversible._copy_internals`
 | 
						|
 | 
						|
    * :meth:`.Traversible._gen_cache_key`
 | 
						|
 | 
						|
    Subclasses can also implement these methods directly, particularly for the
 | 
						|
    :meth:`.Traversible._copy_internals` method, when special steps
 | 
						|
    are needed.
 | 
						|
 | 
						|
    .. versionadded:: 1.4
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def dispatch(self, visit_symbol):
 | 
						|
        """Given a method from :class:`.InternalTraversal`, return the
 | 
						|
        corresponding method on a subclass.
 | 
						|
 | 
						|
        """
 | 
						|
        name = self._dispatch_lookup[visit_symbol]
 | 
						|
        return getattr(self, name, None)
 | 
						|
 | 
						|
    def run_generated_dispatch(
 | 
						|
        self, target, internal_dispatch, generate_dispatcher_name
 | 
						|
    ):
 | 
						|
        try:
 | 
						|
            dispatcher = target.__class__.__dict__[generate_dispatcher_name]
 | 
						|
        except KeyError:
 | 
						|
            # most of the dispatchers are generated up front
 | 
						|
            # in sqlalchemy/sql/__init__.py ->
 | 
						|
            # traversals.py-> _preconfigure_traversals().
 | 
						|
            # this block will generate any remaining dispatchers.
 | 
						|
            dispatcher = self.generate_dispatch(
 | 
						|
                target.__class__, internal_dispatch, generate_dispatcher_name
 | 
						|
            )
 | 
						|
        return dispatcher(target, self)
 | 
						|
 | 
						|
    def generate_dispatch(
 | 
						|
        self, target_cls, internal_dispatch, generate_dispatcher_name
 | 
						|
    ):
 | 
						|
        dispatcher = _generate_dispatcher(
 | 
						|
            self, internal_dispatch, generate_dispatcher_name
 | 
						|
        )
 | 
						|
        # assert isinstance(target_cls, type)
 | 
						|
        setattr(target_cls, generate_dispatcher_name, dispatcher)
 | 
						|
        return dispatcher
 | 
						|
 | 
						|
    dp_has_cache_key = symbol("HC")
 | 
						|
    """Visit a :class:`.HasCacheKey` object."""
 | 
						|
 | 
						|
    dp_has_cache_key_list = symbol("HL")
 | 
						|
    """Visit a list of :class:`.HasCacheKey` objects."""
 | 
						|
 | 
						|
    dp_clauseelement = symbol("CE")
 | 
						|
    """Visit a :class:`_expression.ClauseElement` object."""
 | 
						|
 | 
						|
    dp_fromclause_canonical_column_collection = symbol("FC")
 | 
						|
    """Visit a :class:`_expression.FromClause` object in the context of the
 | 
						|
    ``columns`` attribute.
 | 
						|
 | 
						|
    The column collection is "canonical", meaning it is the originally
 | 
						|
    defined location of the :class:`.ColumnClause` objects.   Right now
 | 
						|
    this means that the object being visited is a
 | 
						|
    :class:`_expression.TableClause`
 | 
						|
    or :class:`_schema.Table` object only.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_clauseelement_tuples = symbol("CTS")
 | 
						|
    """Visit a list of tuples which contain :class:`_expression.ClauseElement`
 | 
						|
    objects.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_clauseelement_list = symbol("CL")
 | 
						|
    """Visit a list of :class:`_expression.ClauseElement` objects.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_clauseelement_tuple = symbol("CT")
 | 
						|
    """Visit a tuple of :class:`_expression.ClauseElement` objects.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_executable_options = symbol("EO")
 | 
						|
 | 
						|
    dp_with_context_options = symbol("WC")
 | 
						|
 | 
						|
    dp_fromclause_ordered_set = symbol("CO")
 | 
						|
    """Visit an ordered set of :class:`_expression.FromClause` objects. """
 | 
						|
 | 
						|
    dp_string = symbol("S")
 | 
						|
    """Visit a plain string value.
 | 
						|
 | 
						|
    Examples include table and column names, bound parameter keys, special
 | 
						|
    keywords such as "UNION", "UNION ALL".
 | 
						|
 | 
						|
    The string value is considered to be significant for cache key
 | 
						|
    generation.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_string_list = symbol("SL")
 | 
						|
    """Visit a list of strings."""
 | 
						|
 | 
						|
    dp_anon_name = symbol("AN")
 | 
						|
    """Visit a potentially "anonymized" string value.
 | 
						|
 | 
						|
    The string value is considered to be significant for cache key
 | 
						|
    generation.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_boolean = symbol("B")
 | 
						|
    """Visit a boolean value.
 | 
						|
 | 
						|
    The boolean value is considered to be significant for cache key
 | 
						|
    generation.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_operator = symbol("O")
 | 
						|
    """Visit an operator.
 | 
						|
 | 
						|
    The operator is a function from the :mod:`sqlalchemy.sql.operators`
 | 
						|
    module.
 | 
						|
 | 
						|
    The operator value is considered to be significant for cache key
 | 
						|
    generation.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_type = symbol("T")
 | 
						|
    """Visit a :class:`.TypeEngine` object
 | 
						|
 | 
						|
    The type object is considered to be significant for cache key
 | 
						|
    generation.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_plain_dict = symbol("PD")
 | 
						|
    """Visit a dictionary with string keys.
 | 
						|
 | 
						|
    The keys of the dictionary should be strings, the values should
 | 
						|
    be immutable and hashable.   The dictionary is considered to be
 | 
						|
    significant for cache key generation.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_dialect_options = symbol("DO")
 | 
						|
    """Visit a dialect options structure."""
 | 
						|
 | 
						|
    dp_string_clauseelement_dict = symbol("CD")
 | 
						|
    """Visit a dictionary of string keys to :class:`_expression.ClauseElement`
 | 
						|
    objects.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_string_multi_dict = symbol("MD")
 | 
						|
    """Visit a dictionary of string keys to values which may either be
 | 
						|
    plain immutable/hashable or :class:`.HasCacheKey` objects.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_annotations_key = symbol("AK")
 | 
						|
    """Visit the _annotations_cache_key element.
 | 
						|
 | 
						|
    This is a dictionary of additional information about a ClauseElement
 | 
						|
    that modifies its role.  It should be included when comparing or caching
 | 
						|
    objects, however generating this key is relatively expensive.   Visitors
 | 
						|
    should check the "_annotations" dict for non-None first before creating
 | 
						|
    this key.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_plain_obj = symbol("PO")
 | 
						|
    """Visit a plain python object.
 | 
						|
 | 
						|
    The value should be immutable and hashable, such as an integer.
 | 
						|
    The value is considered to be significant for cache key generation.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_named_ddl_element = symbol("DD")
 | 
						|
    """Visit a simple named DDL element.
 | 
						|
 | 
						|
    The current object used by this method is the :class:`.Sequence`.
 | 
						|
 | 
						|
    The object is only considered to be important for cache key generation
 | 
						|
    as far as its name, but not any other aspects of it.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_prefix_sequence = symbol("PS")
 | 
						|
    """Visit the sequence represented by :class:`_expression.HasPrefixes`
 | 
						|
    or :class:`_expression.HasSuffixes`.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_table_hint_list = symbol("TH")
 | 
						|
    """Visit the ``_hints`` collection of a :class:`_expression.Select`
 | 
						|
    object.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_setup_join_tuple = symbol("SJ")
 | 
						|
 | 
						|
    dp_memoized_select_entities = symbol("ME")
 | 
						|
 | 
						|
    dp_statement_hint_list = symbol("SH")
 | 
						|
    """Visit the ``_statement_hints`` collection of a
 | 
						|
    :class:`_expression.Select`
 | 
						|
    object.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_unknown_structure = symbol("UK")
 | 
						|
    """Visit an unknown structure.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_dml_ordered_values = symbol("DML_OV")
 | 
						|
    """Visit the values() ordered tuple list of an
 | 
						|
    :class:`_expression.Update` object."""
 | 
						|
 | 
						|
    dp_dml_values = symbol("DML_V")
 | 
						|
    """Visit the values() dictionary of a :class:`.ValuesBase`
 | 
						|
    (e.g. Insert or Update) object.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_dml_multi_values = symbol("DML_MV")
 | 
						|
    """Visit the values() multi-valued list of dictionaries of an
 | 
						|
    :class:`_expression.Insert` object.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_propagate_attrs = symbol("PA")
 | 
						|
    """Visit the propagate attrs dict.  This hardcodes to the particular
 | 
						|
    elements we care about right now."""
 | 
						|
 | 
						|
 | 
						|
class ExtendedInternalTraversal(InternalTraversal):
 | 
						|
    """Defines additional symbols that are useful in caching applications.
 | 
						|
 | 
						|
    Traversals for :class:`_expression.ClauseElement` objects only need to use
 | 
						|
    those symbols present in :class:`.InternalTraversal`.  However, for
 | 
						|
    additional caching use cases within the ORM, symbols dealing with the
 | 
						|
    :class:`.HasCacheKey` class are added here.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_ignore = symbol("IG")
 | 
						|
    """Specify an object that should be ignored entirely.
 | 
						|
 | 
						|
    This currently applies function call argument caching where some
 | 
						|
    arguments should not be considered to be part of a cache key.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_inspectable = symbol("IS")
 | 
						|
    """Visit an inspectable object where the return value is a
 | 
						|
    :class:`.HasCacheKey` object."""
 | 
						|
 | 
						|
    dp_multi = symbol("M")
 | 
						|
    """Visit an object that may be a :class:`.HasCacheKey` or may be a
 | 
						|
    plain hashable object."""
 | 
						|
 | 
						|
    dp_multi_list = symbol("MT")
 | 
						|
    """Visit a tuple containing elements that may be :class:`.HasCacheKey` or
 | 
						|
    may be a plain hashable object."""
 | 
						|
 | 
						|
    dp_has_cache_key_tuples = symbol("HT")
 | 
						|
    """Visit a list of tuples which contain :class:`.HasCacheKey`
 | 
						|
    objects.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    dp_inspectable_list = symbol("IL")
 | 
						|
    """Visit a list of inspectable objects which upon inspection are
 | 
						|
    HasCacheKey objects."""
 | 
						|
 | 
						|
 | 
						|
class ExternalTraversal(object):
 | 
						|
    """Base class for visitor objects which can traverse externally using
 | 
						|
    the :func:`.visitors.traverse` function.
 | 
						|
 | 
						|
    Direct usage of the :func:`.visitors.traverse` function is usually
 | 
						|
    preferred.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    __traverse_options__ = {}
 | 
						|
 | 
						|
    def traverse_single(self, obj, **kw):
 | 
						|
        for v in self.visitor_iterator:
 | 
						|
            meth = getattr(v, "visit_%s" % obj.__visit_name__, None)
 | 
						|
            if meth:
 | 
						|
                return meth(obj, **kw)
 | 
						|
 | 
						|
    def iterate(self, obj):
 | 
						|
        """Traverse the given expression structure, returning an iterator
 | 
						|
        of all elements.
 | 
						|
 | 
						|
        """
 | 
						|
        return iterate(obj, self.__traverse_options__)
 | 
						|
 | 
						|
    def traverse(self, obj):
 | 
						|
        """Traverse and visit the given expression structure."""
 | 
						|
 | 
						|
        return traverse(obj, self.__traverse_options__, self._visitor_dict)
 | 
						|
 | 
						|
    @util.memoized_property
 | 
						|
    def _visitor_dict(self):
 | 
						|
        visitors = {}
 | 
						|
 | 
						|
        for name in dir(self):
 | 
						|
            if name.startswith("visit_"):
 | 
						|
                visitors[name[6:]] = getattr(self, name)
 | 
						|
        return visitors
 | 
						|
 | 
						|
    @property
 | 
						|
    def visitor_iterator(self):
 | 
						|
        """Iterate through this visitor and each 'chained' visitor."""
 | 
						|
 | 
						|
        v = self
 | 
						|
        while v:
 | 
						|
            yield v
 | 
						|
            v = getattr(v, "_next", None)
 | 
						|
 | 
						|
    def chain(self, visitor):
 | 
						|
        """'Chain' an additional ClauseVisitor onto this ClauseVisitor.
 | 
						|
 | 
						|
        The chained visitor will receive all visit events after this one.
 | 
						|
 | 
						|
        """
 | 
						|
        tail = list(self.visitor_iterator)[-1]
 | 
						|
        tail._next = visitor
 | 
						|
        return self
 | 
						|
 | 
						|
 | 
						|
class CloningExternalTraversal(ExternalTraversal):
 | 
						|
    """Base class for visitor objects which can traverse using
 | 
						|
    the :func:`.visitors.cloned_traverse` function.
 | 
						|
 | 
						|
    Direct usage of the :func:`.visitors.cloned_traverse` function is usually
 | 
						|
    preferred.
 | 
						|
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def copy_and_process(self, list_):
 | 
						|
        """Apply cloned traversal to the given list of elements, and return
 | 
						|
        the new list.
 | 
						|
 | 
						|
        """
 | 
						|
        return [self.traverse(x) for x in list_]
 | 
						|
 | 
						|
    def traverse(self, obj):
 | 
						|
        """Traverse and visit the given expression structure."""
 | 
						|
 | 
						|
        return cloned_traverse(
 | 
						|
            obj, self.__traverse_options__, self._visitor_dict
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class ReplacingExternalTraversal(CloningExternalTraversal):
 | 
						|
    """Base class for visitor objects which can traverse using
 | 
						|
    the :func:`.visitors.replacement_traverse` function.
 | 
						|
 | 
						|
    Direct usage of the :func:`.visitors.replacement_traverse` function is
 | 
						|
    usually preferred.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    def replace(self, elem):
 | 
						|
        """Receive pre-copied elements during a cloning traversal.
 | 
						|
 | 
						|
        If the method returns a new element, the element is used
 | 
						|
        instead of creating a simple copy of the element.  Traversal
 | 
						|
        will halt on the newly returned element if it is re-encountered.
 | 
						|
        """
 | 
						|
        return None
 | 
						|
 | 
						|
    def traverse(self, obj):
 | 
						|
        """Traverse and visit the given expression structure."""
 | 
						|
 | 
						|
        def replace(elem):
 | 
						|
            for v in self.visitor_iterator:
 | 
						|
                e = v.replace(elem)
 | 
						|
                if e is not None:
 | 
						|
                    return e
 | 
						|
 | 
						|
        return replacement_traverse(obj, self.__traverse_options__, replace)
 | 
						|
 | 
						|
 | 
						|
# backwards compatibility
 | 
						|
Visitable = Traversible
 | 
						|
VisitableType = TraversibleType
 | 
						|
ClauseVisitor = ExternalTraversal
 | 
						|
CloningVisitor = CloningExternalTraversal
 | 
						|
ReplacingCloningVisitor = ReplacingExternalTraversal
 | 
						|
 | 
						|
 | 
						|
def iterate(obj, opts=util.immutabledict()):
 | 
						|
    r"""Traverse the given expression structure, returning an iterator.
 | 
						|
 | 
						|
    Traversal is configured to be breadth-first.
 | 
						|
 | 
						|
    The central API feature used by the :func:`.visitors.iterate`
 | 
						|
    function is the
 | 
						|
    :meth:`_expression.ClauseElement.get_children` method of
 | 
						|
    :class:`_expression.ClauseElement` objects.  This method should return all
 | 
						|
    the :class:`_expression.ClauseElement` objects which are associated with a
 | 
						|
    particular :class:`_expression.ClauseElement` object. For example, a
 | 
						|
    :class:`.Case` structure will refer to a series of
 | 
						|
    :class:`_expression.ColumnElement` objects within its "whens" and "else\_"
 | 
						|
    member variables.
 | 
						|
 | 
						|
    :param obj: :class:`_expression.ClauseElement` structure to be traversed
 | 
						|
 | 
						|
    :param opts: dictionary of iteration options.   This dictionary is usually
 | 
						|
     empty in modern usage.
 | 
						|
 | 
						|
    """
 | 
						|
    yield obj
 | 
						|
    children = obj.get_children(**opts)
 | 
						|
 | 
						|
    if not children:
 | 
						|
        return
 | 
						|
 | 
						|
    stack = deque([children])
 | 
						|
    while stack:
 | 
						|
        t_iterator = stack.popleft()
 | 
						|
        for t in t_iterator:
 | 
						|
            yield t
 | 
						|
            stack.append(t.get_children(**opts))
 | 
						|
 | 
						|
 | 
						|
def traverse_using(iterator, obj, visitors):
 | 
						|
    """Visit the given expression structure using the given iterator of
 | 
						|
    objects.
 | 
						|
 | 
						|
    :func:`.visitors.traverse_using` is usually called internally as the result
 | 
						|
    of the :func:`.visitors.traverse` function.
 | 
						|
 | 
						|
    :param iterator: an iterable or sequence which will yield
 | 
						|
     :class:`_expression.ClauseElement`
 | 
						|
     structures; the iterator is assumed to be the
 | 
						|
     product of the :func:`.visitors.iterate` function.
 | 
						|
 | 
						|
    :param obj: the :class:`_expression.ClauseElement`
 | 
						|
     that was used as the target of the
 | 
						|
     :func:`.iterate` function.
 | 
						|
 | 
						|
    :param visitors: dictionary of visit functions.  See :func:`.traverse`
 | 
						|
     for details on this dictionary.
 | 
						|
 | 
						|
    .. seealso::
 | 
						|
 | 
						|
        :func:`.traverse`
 | 
						|
 | 
						|
 | 
						|
    """
 | 
						|
    for target in iterator:
 | 
						|
        meth = visitors.get(target.__visit_name__, None)
 | 
						|
        if meth:
 | 
						|
            meth(target)
 | 
						|
    return obj
 | 
						|
 | 
						|
 | 
						|
def traverse(obj, opts, visitors):
 | 
						|
    """Traverse and visit the given expression structure using the default
 | 
						|
    iterator.
 | 
						|
 | 
						|
     e.g.::
 | 
						|
 | 
						|
        from sqlalchemy.sql import visitors
 | 
						|
 | 
						|
        stmt = select(some_table).where(some_table.c.foo == 'bar')
 | 
						|
 | 
						|
        def visit_bindparam(bind_param):
 | 
						|
            print("found bound value: %s" % bind_param.value)
 | 
						|
 | 
						|
        visitors.traverse(stmt, {}, {"bindparam": visit_bindparam})
 | 
						|
 | 
						|
    The iteration of objects uses the :func:`.visitors.iterate` function,
 | 
						|
    which does a breadth-first traversal using a stack.
 | 
						|
 | 
						|
    :param obj: :class:`_expression.ClauseElement` structure to be traversed
 | 
						|
 | 
						|
    :param opts: dictionary of iteration options.   This dictionary is usually
 | 
						|
     empty in modern usage.
 | 
						|
 | 
						|
    :param visitors: dictionary of visit functions.   The dictionary should
 | 
						|
     have strings as keys, each of which would correspond to the
 | 
						|
     ``__visit_name__`` of a particular kind of SQL expression object, and
 | 
						|
     callable functions  as values, each of which represents a visitor function
 | 
						|
     for that kind of object.
 | 
						|
 | 
						|
    """
 | 
						|
    return traverse_using(iterate(obj, opts), obj, visitors)
 | 
						|
 | 
						|
 | 
						|
def cloned_traverse(obj, opts, visitors):
 | 
						|
    """Clone the given expression structure, allowing modifications by
 | 
						|
    visitors.
 | 
						|
 | 
						|
    Traversal usage is the same as that of :func:`.visitors.traverse`.
 | 
						|
    The visitor functions present in the ``visitors`` dictionary may also
 | 
						|
    modify the internals of the given structure as the traversal proceeds.
 | 
						|
 | 
						|
    The central API feature used by the :func:`.visitors.cloned_traverse`
 | 
						|
    and :func:`.visitors.replacement_traverse` functions, in addition to the
 | 
						|
    :meth:`_expression.ClauseElement.get_children`
 | 
						|
    function that is used to achieve
 | 
						|
    the iteration, is the :meth:`_expression.ClauseElement._copy_internals`
 | 
						|
    method.
 | 
						|
    For a :class:`_expression.ClauseElement`
 | 
						|
    structure to support cloning and replacement
 | 
						|
    traversals correctly, it needs to be able to pass a cloning function into
 | 
						|
    its internal members in order to make copies of them.
 | 
						|
 | 
						|
    .. seealso::
 | 
						|
 | 
						|
        :func:`.visitors.traverse`
 | 
						|
 | 
						|
        :func:`.visitors.replacement_traverse`
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    cloned = {}
 | 
						|
    stop_on = set(opts.get("stop_on", []))
 | 
						|
 | 
						|
    def deferred_copy_internals(obj):
 | 
						|
        return cloned_traverse(obj, opts, visitors)
 | 
						|
 | 
						|
    def clone(elem, **kw):
 | 
						|
        if elem in stop_on:
 | 
						|
            return elem
 | 
						|
        else:
 | 
						|
            if id(elem) not in cloned:
 | 
						|
 | 
						|
                if "replace" in kw:
 | 
						|
                    newelem = kw["replace"](elem)
 | 
						|
                    if newelem is not None:
 | 
						|
                        cloned[id(elem)] = newelem
 | 
						|
                        return newelem
 | 
						|
 | 
						|
                cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw)
 | 
						|
                newelem._copy_internals(clone=clone, **kw)
 | 
						|
                meth = visitors.get(newelem.__visit_name__, None)
 | 
						|
                if meth:
 | 
						|
                    meth(newelem)
 | 
						|
            return cloned[id(elem)]
 | 
						|
 | 
						|
    if obj is not None:
 | 
						|
        obj = clone(
 | 
						|
            obj, deferred_copy_internals=deferred_copy_internals, **opts
 | 
						|
        )
 | 
						|
    clone = None  # remove gc cycles
 | 
						|
    return obj
 | 
						|
 | 
						|
 | 
						|
def replacement_traverse(obj, opts, replace):
 | 
						|
    """Clone the given expression structure, allowing element
 | 
						|
    replacement by a given replacement function.
 | 
						|
 | 
						|
    This function is very similar to the :func:`.visitors.cloned_traverse`
 | 
						|
    function, except instead of being passed a dictionary of visitors, all
 | 
						|
    elements are unconditionally passed into the given replace function.
 | 
						|
    The replace function then has the option to return an entirely new object
 | 
						|
    which will replace the one given.  If it returns ``None``, then the object
 | 
						|
    is kept in place.
 | 
						|
 | 
						|
    The difference in usage between :func:`.visitors.cloned_traverse` and
 | 
						|
    :func:`.visitors.replacement_traverse` is that in the former case, an
 | 
						|
    already-cloned object is passed to the visitor function, and the visitor
 | 
						|
    function can then manipulate the internal state of the object.
 | 
						|
    In the case of the latter, the visitor function should only return an
 | 
						|
    entirely different object, or do nothing.
 | 
						|
 | 
						|
    The use case for :func:`.visitors.replacement_traverse` is that of
 | 
						|
    replacing a FROM clause inside of a SQL structure with a different one,
 | 
						|
    as is a common use case within the ORM.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    cloned = {}
 | 
						|
    stop_on = {id(x) for x in opts.get("stop_on", [])}
 | 
						|
 | 
						|
    def deferred_copy_internals(obj):
 | 
						|
        return replacement_traverse(obj, opts, replace)
 | 
						|
 | 
						|
    def clone(elem, **kw):
 | 
						|
        if (
 | 
						|
            id(elem) in stop_on
 | 
						|
            or "no_replacement_traverse" in elem._annotations
 | 
						|
        ):
 | 
						|
            return elem
 | 
						|
        else:
 | 
						|
            newelem = replace(elem)
 | 
						|
            if newelem is not None:
 | 
						|
                stop_on.add(id(newelem))
 | 
						|
                return newelem
 | 
						|
            else:
 | 
						|
                # base "already seen" on id(), not hash, so that we don't
 | 
						|
                # replace an Annotated element with its non-annotated one, and
 | 
						|
                # vice versa
 | 
						|
                id_elem = id(elem)
 | 
						|
                if id_elem not in cloned:
 | 
						|
                    if "replace" in kw:
 | 
						|
                        newelem = kw["replace"](elem)
 | 
						|
                        if newelem is not None:
 | 
						|
                            cloned[id_elem] = newelem
 | 
						|
                            return newelem
 | 
						|
 | 
						|
                    cloned[id_elem] = newelem = elem._clone(**kw)
 | 
						|
                    newelem._copy_internals(clone=clone, **kw)
 | 
						|
                return cloned[id_elem]
 | 
						|
 | 
						|
    if obj is not None:
 | 
						|
        obj = clone(
 | 
						|
            obj, deferred_copy_internals=deferred_copy_internals, **opts
 | 
						|
        )
 | 
						|
    clone = None  # remove gc cycles
 | 
						|
    return obj
 |