Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/sql/visitors.py: 70%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

371 statements  

1# sql/visitors.py 

2# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: https://www.opensource.org/licenses/mit-license.php 

7 

8"""Visitor/traversal interface and library functions.""" 

9 

10from __future__ import annotations 

11 

12from collections import deque 

13from enum import Enum 

14import itertools 

15import operator 

16import typing 

17from typing import Any 

18from typing import Callable 

19from typing import cast 

20from typing import ClassVar 

21from typing import Dict 

22from typing import Iterable 

23from typing import Iterator 

24from typing import List 

25from typing import Literal 

26from typing import Mapping 

27from typing import Optional 

28from typing import overload 

29from typing import Protocol 

30from typing import Tuple 

31from typing import Type 

32from typing import TYPE_CHECKING 

33from typing import TypeVar 

34from typing import Union 

35 

36from ._util_cy import anon_map as anon_map 

37from ._util_cy import prefix_anon_map as prefix_anon_map # noqa: F401 

38from .. import exc 

39from .. import util 

40from ..util import langhelpers 

41from ..util.typing import Self 

42 

43if TYPE_CHECKING: 

44 from .annotation import _AnnotationDict 

45 from .elements import ColumnElement 

46 

47 

48__all__ = [ 

49 "iterate", 

50 "traverse_using", 

51 "traverse", 

52 "cloned_traverse", 

53 "replacement_traverse", 

54 "Visitable", 

55 "ExternalTraversal", 

56 "InternalTraversal", 

57 "anon_map", 

58] 

59 

60 

61class _CompilerDispatchType(Protocol): 

62 def __call__(_self, self: Visitable, visitor: Any, **kw: Any) -> Any: ... 

63 

64 

65class Visitable: 

66 """Base class for visitable objects. 

67 

68 :class:`.Visitable` is used to implement the SQL compiler dispatch 

69 functions. Other forms of traversal such as for cache key generation 

70 are implemented separately using the :class:`.HasTraverseInternals` 

71 interface. 

72 

73 .. versionchanged:: 2.0 The :class:`.Visitable` class was named 

74 :class:`.Traversible` in the 1.4 series; the name is changed back 

75 to :class:`.Visitable` in 2.0 which is what it was prior to 1.4. 

76 

77 Both names remain importable in both 1.4 and 2.0 versions. 

78 

79 """ 

80 

81 __slots__ = () 

82 

83 __visit_name__: str 

84 

85 _original_compiler_dispatch: _CompilerDispatchType 

86 

87 if typing.TYPE_CHECKING: 

88 

89 def _compiler_dispatch(self, visitor: Any, **kw: Any) -> str: ... 

90 

91 def __init_subclass__(cls) -> None: 

92 if "__visit_name__" in cls.__dict__: 

93 cls._generate_compiler_dispatch() 

94 super().__init_subclass__() 

95 

96 @classmethod 

97 def _generate_compiler_dispatch(cls) -> None: 

98 visit_name = cls.__visit_name__ 

99 

100 if "_compiler_dispatch" in cls.__dict__: 

101 # class has a fixed _compiler_dispatch() method. 

102 # copy it to "original" so that we can get it back if 

103 # sqlalchemy.ext.compiles overrides it. 

104 cls._original_compiler_dispatch = cls._compiler_dispatch 

105 return 

106 

107 if not isinstance(visit_name, str): 

108 raise exc.InvalidRequestError( 

109 f"__visit_name__ on class {cls.__name__} must be a string " 

110 "at the class level" 

111 ) 

112 

113 name = "visit_%s" % visit_name 

114 getter = operator.attrgetter(name) 

115 

116 def _compiler_dispatch( 

117 self: Visitable, visitor: Any, **kw: Any 

118 ) -> str: 

119 """Look for an attribute named "visit_<visit_name>" on the 

120 visitor, and call it with the same kw params. 

121 

122 """ 

123 try: 

124 meth = getter(visitor) 

125 except AttributeError as err: 

126 return visitor.visit_unsupported_compilation(self, err, **kw) # type: ignore # noqa: E501 

127 else: 

128 return meth(self, **kw) # type: ignore # noqa: E501 

129 

130 cls._compiler_dispatch = ( # type: ignore 

131 cls._original_compiler_dispatch 

132 ) = _compiler_dispatch 

133 

134 def __class_getitem__(cls, key: Any) -> Any: 

135 # allow generic classes in py3.9+ 

136 return cls 

137 

138 

139class InternalTraversal(Enum): 

140 r"""Defines visitor symbols used for internal traversal. 

141 

142 The :class:`.InternalTraversal` class is used in two ways. One is that 

143 it can serve as the superclass for an object that implements the 

144 various visit methods of the class. The other is that the symbols 

145 themselves of :class:`.InternalTraversal` are used within 

146 the ``_traverse_internals`` collection. Such as, the :class:`.Case` 

147 object defines ``_traverse_internals`` as :: 

148 

149 class Case(ColumnElement[_T]): 

150 _traverse_internals = [ 

151 ("value", InternalTraversal.dp_clauseelement), 

152 ("whens", InternalTraversal.dp_clauseelement_tuples), 

153 ("else_", InternalTraversal.dp_clauseelement), 

154 ] 

155 

156 Above, the :class:`.Case` class indicates its internal state as the 

157 attributes named ``value``, ``whens``, and ``else_``. They each 

158 link to an :class:`.InternalTraversal` method which indicates the type 

159 of datastructure to which each attribute refers. 

160 

161 Using the ``_traverse_internals`` structure, objects of type 

162 :class:`.InternalTraversible` will have the following methods automatically 

163 implemented: 

164 

165 * :meth:`.HasTraverseInternals.get_children` 

166 

167 * :meth:`.HasTraverseInternals._copy_internals` 

168 

169 * :meth:`.HasCacheKey._gen_cache_key` 

170 

171 Subclasses can also implement these methods directly, particularly for the 

172 :meth:`.HasTraverseInternals._copy_internals` method, when special steps 

173 are needed. 

174 

175 .. versionadded:: 1.4 

176 

177 """ 

178 

179 dp_has_cache_key = "HC" 

180 """Visit a :class:`.HasCacheKey` object.""" 

181 

182 dp_has_cache_key_list = "HL" 

183 """Visit a list of :class:`.HasCacheKey` objects.""" 

184 

185 dp_clauseelement = "CE" 

186 """Visit a :class:`_expression.ClauseElement` object.""" 

187 

188 dp_fromclause_canonical_column_collection = "FC" 

189 """Visit a :class:`_expression.FromClause` object in the context of the 

190 ``columns`` attribute. 

191 

192 The column collection is "canonical", meaning it is the originally 

193 defined location of the :class:`.ColumnClause` objects. Right now 

194 this means that the object being visited is a 

195 :class:`_expression.TableClause` 

196 or :class:`_schema.Table` object only. 

197 

198 """ 

199 

200 dp_clauseelement_tuples = "CTS" 

201 """Visit a list of tuples which contain :class:`_expression.ClauseElement` 

202 objects. 

203 

204 """ 

205 

206 dp_clauseelement_list = "CL" 

207 """Visit a list of :class:`_expression.ClauseElement` objects. 

208 

209 """ 

210 

211 dp_clauseelement_tuple = "CT" 

212 """Visit a tuple of :class:`_expression.ClauseElement` objects. 

213 

214 """ 

215 

216 dp_executable_options = "EO" 

217 

218 dp_compile_state_funcs = "WC" 

219 

220 dp_fromclause_ordered_set = "CO" 

221 """Visit an ordered set of :class:`_expression.FromClause` objects. """ 

222 

223 dp_string = "S" 

224 """Visit a plain string value. 

225 

226 Examples include table and column names, bound parameter keys, special 

227 keywords such as "UNION", "UNION ALL". 

228 

229 The string value is considered to be significant for cache key 

230 generation. 

231 

232 """ 

233 

234 dp_string_list = "SL" 

235 """Visit a list of strings.""" 

236 

237 dp_anon_name = "AN" 

238 """Visit a potentially "anonymized" string value. 

239 

240 The string value is considered to be significant for cache key 

241 generation. 

242 

243 """ 

244 

245 dp_boolean = "B" 

246 """Visit a boolean value. 

247 

248 The boolean value is considered to be significant for cache key 

249 generation. 

250 

251 """ 

252 

253 dp_operator = "O" 

254 """Visit an operator. 

255 

256 The operator is a function from the :mod:`sqlalchemy.sql.operators` 

257 module. 

258 

259 The operator value is considered to be significant for cache key 

260 generation. 

261 

262 """ 

263 

264 dp_type = "T" 

265 """Visit a :class:`.TypeEngine` object 

266 

267 The type object is considered to be significant for cache key 

268 generation. 

269 

270 """ 

271 

272 dp_plain_dict = "PD" 

273 """Visit a dictionary with string keys. 

274 

275 The keys of the dictionary should be strings, the values should 

276 be immutable and hashable. The dictionary is considered to be 

277 significant for cache key generation. 

278 

279 """ 

280 

281 dp_dialect_options = "DO" 

282 """Visit a dialect options structure.""" 

283 

284 dp_string_clauseelement_dict = "CD" 

285 """Visit a dictionary of string keys to :class:`_expression.ClauseElement` 

286 objects. 

287 

288 """ 

289 

290 dp_string_multi_dict = "MD" 

291 """Visit a dictionary of string keys to values which may either be 

292 plain immutable/hashable or :class:`.HasCacheKey` objects. 

293 

294 """ 

295 

296 dp_annotations_key = "AK" 

297 """Visit the _annotations_cache_key element. 

298 

299 This is a dictionary of additional information about a ClauseElement 

300 that modifies its role. It should be included when comparing or caching 

301 objects, however generating this key is relatively expensive. Visitors 

302 should check the "_annotations" dict for non-None first before creating 

303 this key. 

304 

305 """ 

306 

307 dp_plain_obj = "PO" 

308 """Visit a plain python object. 

309 

310 The value should be immutable and hashable, such as an integer. 

311 The value is considered to be significant for cache key generation. 

312 

313 """ 

314 

315 dp_named_ddl_element = "DD" 

316 """Visit a simple named DDL element. 

317 

318 The current object used by this method is the :class:`.Sequence`. 

319 

320 The object is only considered to be important for cache key generation 

321 as far as its name, but not any other aspects of it. 

322 

323 """ 

324 

325 dp_prefix_sequence = "PS" 

326 """Visit the sequence represented by :class:`_expression.HasPrefixes` 

327 or :class:`_expression.HasSuffixes`. 

328 

329 """ 

330 

331 dp_table_hint_list = "TH" 

332 """Visit the ``_hints`` collection of a :class:`_expression.Select` 

333 object. 

334 

335 """ 

336 

337 dp_setup_join_tuple = "SJ" 

338 

339 dp_memoized_select_entities = "ME" 

340 

341 dp_statement_hint_list = "SH" 

342 """Visit the ``_statement_hints`` collection of a 

343 :class:`_expression.Select` 

344 object. 

345 

346 """ 

347 

348 dp_unknown_structure = "UK" 

349 """Visit an unknown structure. 

350 

351 """ 

352 

353 dp_dml_ordered_values = "DML_OV" 

354 """Visit the values() ordered tuple list of an 

355 :class:`_expression.Update` object.""" 

356 

357 dp_dml_values = "DML_V" 

358 """Visit the values() dictionary of a :class:`.ValuesBase` 

359 (e.g. Insert or Update) object. 

360 

361 """ 

362 

363 dp_dml_multi_values = "DML_MV" 

364 """Visit the values() multi-valued list of dictionaries of an 

365 :class:`_expression.Insert` object. 

366 

367 """ 

368 

369 dp_propagate_attrs = "PA" 

370 """Visit the propagate attrs dict. This hardcodes to the particular 

371 elements we care about right now.""" 

372 

373 """Symbols that follow are additional symbols that are useful in 

374 caching applications. 

375 

376 Traversals for :class:`_expression.ClauseElement` objects only need to use 

377 those symbols present in :class:`.InternalTraversal`. However, for 

378 additional caching use cases within the ORM, symbols dealing with the 

379 :class:`.HasCacheKey` class are added here. 

380 

381 """ 

382 

383 dp_ignore = "IG" 

384 """Specify an object that should be ignored entirely. 

385 

386 This currently applies function call argument caching where some 

387 arguments should not be considered to be part of a cache key. 

388 

389 """ 

390 

391 dp_inspectable = "IS" 

392 """Visit an inspectable object where the return value is a 

393 :class:`.HasCacheKey` object.""" 

394 

395 dp_multi = "M" 

396 """Visit an object that may be a :class:`.HasCacheKey` or may be a 

397 plain hashable object.""" 

398 

399 dp_multi_list = "MT" 

400 """Visit a tuple containing elements that may be :class:`.HasCacheKey` or 

401 may be a plain hashable object.""" 

402 

403 dp_has_cache_key_tuples = "HT" 

404 """Visit a list of tuples which contain :class:`.HasCacheKey` 

405 objects. 

406 

407 """ 

408 

409 dp_inspectable_list = "IL" 

410 """Visit a list of inspectable objects which upon inspection are 

411 HasCacheKey objects.""" 

412 

413 dp_params = "PM" 

414 """Visit the _params collection of ExecutableStatement""" 

415 

416 

417_TraverseInternalsType = List[Tuple[str, InternalTraversal]] 

418"""a structure that defines how a HasTraverseInternals should be 

419traversed. 

420 

421This structure consists of a list of (attributename, internaltraversal) 

422tuples, where the "attributename" refers to the name of an attribute on an 

423instance of the HasTraverseInternals object, and "internaltraversal" refers 

424to an :class:`.InternalTraversal` enumeration symbol defining what kind 

425of data this attribute stores, which indicates to the traverser how it should 

426be handled. 

427 

428""" 

429 

430 

431class HasTraverseInternals: 

432 """base for classes that have a "traverse internals" element, 

433 which defines all kinds of ways of traversing the elements of an object. 

434 

435 Compared to :class:`.Visitable`, which relies upon an external visitor to 

436 define how the object is travered (i.e. the :class:`.SQLCompiler`), the 

437 :class:`.HasTraverseInternals` interface allows classes to define their own 

438 traversal, that is, what attributes are accessed and in what order. 

439 

440 """ 

441 

442 __slots__ = () 

443 

444 _traverse_internals: _TraverseInternalsType 

445 

446 _is_immutable: bool = False 

447 

448 @util.preload_module("sqlalchemy.sql.traversals") 

449 def get_children( 

450 self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any 

451 ) -> Iterable[HasTraverseInternals]: 

452 r"""Return immediate child :class:`.visitors.HasTraverseInternals` 

453 elements of this :class:`.visitors.HasTraverseInternals`. 

454 

455 This is used for visit traversal. 

456 

457 \**kw may contain flags that change the collection that is 

458 returned, for example to return a subset of items in order to 

459 cut down on larger traversals, or to return child items from a 

460 different context (such as schema-level collections instead of 

461 clause-level). 

462 

463 """ 

464 

465 traversals = util.preloaded.sql_traversals 

466 

467 try: 

468 traverse_internals = self._traverse_internals 

469 except AttributeError: 

470 # user-defined classes may not have a _traverse_internals 

471 return [] 

472 

473 dispatch = traversals._get_children.run_generated_dispatch 

474 return itertools.chain.from_iterable( 

475 meth(obj, **kw) 

476 for attrname, obj, meth in dispatch( 

477 self, traverse_internals, "_generated_get_children_traversal" 

478 ) 

479 if attrname not in omit_attrs and obj is not None 

480 ) 

481 

482 

483class _InternalTraversalDispatchType(Protocol): 

484 def __call__(s, self: object, visitor: HasTraversalDispatch) -> Any: ... 

485 

486 

487class HasTraversalDispatch: 

488 r"""Define infrastructure for classes that perform internal traversals 

489 

490 .. versionadded:: 2.0 

491 

492 """ 

493 

494 __slots__ = () 

495 

496 _dispatch_lookup: ClassVar[Dict[Union[InternalTraversal, str], str]] = {} 

497 

498 def dispatch(self, visit_symbol: InternalTraversal) -> Callable[..., Any]: 

499 """Given a method from :class:`.HasTraversalDispatch`, return the 

500 corresponding method on a subclass. 

501 

502 """ 

503 name = _dispatch_lookup[visit_symbol] 

504 return getattr(self, name, None) # type: ignore 

505 

506 def run_generated_dispatch( 

507 self, 

508 target: object, 

509 internal_dispatch: _TraverseInternalsType, 

510 generate_dispatcher_name: str, 

511 ) -> Any: 

512 dispatcher: _InternalTraversalDispatchType 

513 try: 

514 dispatcher = target.__class__.__dict__[generate_dispatcher_name] 

515 except KeyError: 

516 # traversals.py -> _preconfigure_traversals() 

517 # may be used to run these ahead of time, but 

518 # is not enabled right now. 

519 # this block will generate any remaining dispatchers. 

520 dispatcher = self.generate_dispatch( 

521 target.__class__, internal_dispatch, generate_dispatcher_name 

522 ) 

523 return dispatcher(target, self) 

524 

525 def generate_dispatch( 

526 self, 

527 target_cls: Type[object], 

528 internal_dispatch: _TraverseInternalsType, 

529 generate_dispatcher_name: str, 

530 ) -> _InternalTraversalDispatchType: 

531 dispatcher = self._generate_dispatcher( 

532 internal_dispatch, generate_dispatcher_name 

533 ) 

534 # assert isinstance(target_cls, type) 

535 setattr(target_cls, generate_dispatcher_name, dispatcher) 

536 return dispatcher 

537 

538 def _generate_dispatcher( 

539 self, internal_dispatch: _TraverseInternalsType, method_name: str 

540 ) -> _InternalTraversalDispatchType: 

541 names = [] 

542 for attrname, visit_sym in internal_dispatch: 

543 meth = self.dispatch(visit_sym) 

544 if meth is not None: 

545 visit_name = _dispatch_lookup[visit_sym] 

546 names.append((attrname, visit_name)) 

547 

548 code = ( 

549 (" return [\n") 

550 + ( 

551 ", \n".join( 

552 " (%r, self.%s, visitor.%s)" 

553 % (attrname, attrname, visit_name) 

554 for attrname, visit_name in names 

555 ) 

556 ) 

557 + ("\n ]\n") 

558 ) 

559 meth_text = ("def %s(self, visitor):\n" % method_name) + code + "\n" 

560 return cast( 

561 _InternalTraversalDispatchType, 

562 langhelpers._exec_code_in_env(meth_text, {}, method_name), 

563 ) 

564 

565 

566ExtendedInternalTraversal = InternalTraversal 

567 

568 

569def _generate_traversal_dispatch() -> None: 

570 lookup = _dispatch_lookup 

571 

572 for sym in InternalTraversal: 

573 key = sym.name 

574 if key.startswith("dp_"): 

575 visit_key = key.replace("dp_", "visit_") 

576 sym_name = sym.value 

577 assert sym_name not in lookup, sym_name 

578 lookup[sym] = lookup[sym_name] = visit_key 

579 

580 

581_dispatch_lookup = HasTraversalDispatch._dispatch_lookup 

582_generate_traversal_dispatch() 

583 

584 

585class ExternallyTraversible(HasTraverseInternals, Visitable): 

586 __slots__ = () 

587 

588 _annotations: Mapping[Any, Any] = util.EMPTY_DICT 

589 

590 if typing.TYPE_CHECKING: 

591 

592 def _annotate(self, values: _AnnotationDict) -> Self: ... 

593 

594 def get_children( 

595 self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any 

596 ) -> Iterable[ExternallyTraversible]: ... 

597 

598 def _clone(self, **kw: Any) -> Self: 

599 """clone this element""" 

600 raise NotImplementedError() 

601 

602 def _copy_internals( 

603 self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any 

604 ) -> None: 

605 """Reassign internal elements to be clones of themselves. 

606 

607 Called during a copy-and-traverse operation on newly 

608 shallow-copied elements to create a deep copy. 

609 

610 The given clone function should be used, which may be applying 

611 additional transformations to the element (i.e. replacement 

612 traversal, cloned traversal, annotations). 

613 

614 """ 

615 raise NotImplementedError() 

616 

617 

618_ET = TypeVar("_ET", bound=ExternallyTraversible) 

619 

620_CE = TypeVar("_CE", bound="ColumnElement[Any]") 

621 

622_TraverseCallableType = Callable[[_ET], None] 

623 

624 

625class _CloneCallableType(Protocol): 

626 def __call__(self, element: _ET, **kw: Any) -> _ET: ... 

627 

628 

629class _TraverseTransformCallableType(Protocol[_ET]): 

630 def __call__(self, element: _ET, **kw: Any) -> Optional[_ET]: ... 

631 

632 

633_ExtT = TypeVar("_ExtT", bound="ExternalTraversal") 

634 

635 

636class ExternalTraversal(util.MemoizedSlots): 

637 """Base class for visitor objects which can traverse externally using 

638 the :func:`.visitors.traverse` function. 

639 

640 Direct usage of the :func:`.visitors.traverse` function is usually 

641 preferred. 

642 

643 """ 

644 

645 __slots__ = ("_visitor_dict", "_next") 

646 

647 __traverse_options__: Dict[str, Any] = {} 

648 _next: Optional[ExternalTraversal] 

649 

650 def traverse_single(self, obj: Visitable, **kw: Any) -> Any: 

651 for v in self.visitor_iterator: 

652 meth = getattr(v, "visit_%s" % obj.__visit_name__, None) 

653 if meth: 

654 return meth(obj, **kw) 

655 

656 def iterate( 

657 self, obj: Optional[ExternallyTraversible] 

658 ) -> Iterator[ExternallyTraversible]: 

659 """Traverse the given expression structure, returning an iterator 

660 of all elements. 

661 

662 """ 

663 return iterate(obj, self.__traverse_options__) 

664 

665 @overload 

666 def traverse(self, obj: Literal[None]) -> None: ... 

667 

668 @overload 

669 def traverse( 

670 self, obj: ExternallyTraversible 

671 ) -> ExternallyTraversible: ... 

672 

673 def traverse( 

674 self, obj: Optional[ExternallyTraversible] 

675 ) -> Optional[ExternallyTraversible]: 

676 """Traverse and visit the given expression structure.""" 

677 

678 return traverse(obj, self.__traverse_options__, self._visitor_dict) 

679 

680 def _memoized_attr__visitor_dict( 

681 self, 

682 ) -> Dict[str, _TraverseCallableType[Any]]: 

683 visitors = {} 

684 

685 for name in dir(self): 

686 if name.startswith("visit_"): 

687 visitors[name[6:]] = getattr(self, name) 

688 return visitors 

689 

690 @property 

691 def visitor_iterator(self) -> Iterator[ExternalTraversal]: 

692 """Iterate through this visitor and each 'chained' visitor.""" 

693 

694 v: Optional[ExternalTraversal] = self 

695 while v: 

696 yield v 

697 v = getattr(v, "_next", None) 

698 

699 def chain(self: _ExtT, visitor: ExternalTraversal) -> _ExtT: 

700 """'Chain' an additional ExternalTraversal onto this ExternalTraversal 

701 

702 The chained visitor will receive all visit events after this one. 

703 

704 """ 

705 tail = list(self.visitor_iterator)[-1] 

706 tail._next = visitor 

707 return self 

708 

709 

710class CloningExternalTraversal(ExternalTraversal): 

711 """Base class for visitor objects which can traverse using 

712 the :func:`.visitors.cloned_traverse` function. 

713 

714 Direct usage of the :func:`.visitors.cloned_traverse` function is usually 

715 preferred. 

716 

717 

718 """ 

719 

720 __slots__ = () 

721 

722 def copy_and_process( 

723 self, list_: List[ExternallyTraversible] 

724 ) -> List[ExternallyTraversible]: 

725 """Apply cloned traversal to the given list of elements, and return 

726 the new list. 

727 

728 """ 

729 return [self.traverse(x) for x in list_] 

730 

731 @overload 

732 def traverse(self, obj: Literal[None]) -> None: ... 

733 

734 @overload 

735 def traverse( 

736 self, obj: ExternallyTraversible 

737 ) -> ExternallyTraversible: ... 

738 

739 def traverse( 

740 self, obj: Optional[ExternallyTraversible] 

741 ) -> Optional[ExternallyTraversible]: 

742 """Traverse and visit the given expression structure.""" 

743 

744 return cloned_traverse( 

745 obj, self.__traverse_options__, self._visitor_dict 

746 ) 

747 

748 

749class ReplacingExternalTraversal(CloningExternalTraversal): 

750 """Base class for visitor objects which can traverse using 

751 the :func:`.visitors.replacement_traverse` function. 

752 

753 Direct usage of the :func:`.visitors.replacement_traverse` function is 

754 usually preferred. 

755 

756 """ 

757 

758 __slots__ = () 

759 

760 def replace( 

761 self, elem: ExternallyTraversible 

762 ) -> Optional[ExternallyTraversible]: 

763 """Receive pre-copied elements during a cloning traversal. 

764 

765 If the method returns a new element, the element is used 

766 instead of creating a simple copy of the element. Traversal 

767 will halt on the newly returned element if it is re-encountered. 

768 """ 

769 return None 

770 

771 @overload 

772 def traverse(self, obj: Literal[None]) -> None: ... 

773 

774 @overload 

775 def traverse( 

776 self, obj: ExternallyTraversible 

777 ) -> ExternallyTraversible: ... 

778 

779 def traverse( 

780 self, obj: Optional[ExternallyTraversible] 

781 ) -> Optional[ExternallyTraversible]: 

782 """Traverse and visit the given expression structure.""" 

783 

784 def replace( 

785 element: ExternallyTraversible, 

786 **kw: Any, 

787 ) -> Optional[ExternallyTraversible]: 

788 for v in self.visitor_iterator: 

789 e = cast(ReplacingExternalTraversal, v).replace(element) 

790 if e is not None: 

791 return e 

792 

793 return None 

794 

795 return replacement_traverse(obj, self.__traverse_options__, replace) 

796 

797 

798# backwards compatibility 

799Traversible = Visitable 

800 

801ClauseVisitor = ExternalTraversal 

802CloningVisitor = CloningExternalTraversal 

803ReplacingCloningVisitor = ReplacingExternalTraversal 

804 

805 

806def iterate( 

807 obj: Optional[ExternallyTraversible], 

808 opts: Mapping[str, Any] = util.EMPTY_DICT, 

809) -> Iterator[ExternallyTraversible]: 

810 r"""Traverse the given expression structure, returning an iterator. 

811 

812 Traversal is configured to be breadth-first. 

813 

814 The central API feature used by the :func:`.visitors.iterate` 

815 function is the 

816 :meth:`_expression.ClauseElement.get_children` method of 

817 :class:`_expression.ClauseElement` objects. This method should return all 

818 the :class:`_expression.ClauseElement` objects which are associated with a 

819 particular :class:`_expression.ClauseElement` object. For example, a 

820 :class:`.Case` structure will refer to a series of 

821 :class:`_expression.ColumnElement` objects within its "whens" and "else\_" 

822 member variables. 

823 

824 :param obj: :class:`_expression.ClauseElement` structure to be traversed 

825 

826 :param opts: dictionary of iteration options. This dictionary is usually 

827 empty in modern usage. 

828 

829 """ 

830 if obj is None: 

831 return 

832 

833 yield obj 

834 children = obj.get_children(**opts) 

835 

836 if not children: 

837 return 

838 

839 stack = deque([children]) 

840 while stack: 

841 t_iterator = stack.popleft() 

842 for t in t_iterator: 

843 yield t 

844 stack.append(t.get_children(**opts)) 

845 

846 

847@overload 

848def traverse_using( 

849 iterator: Iterable[ExternallyTraversible], 

850 obj: Literal[None], 

851 visitors: Mapping[str, _TraverseCallableType[Any]], 

852) -> None: ... 

853 

854 

855@overload 

856def traverse_using( 

857 iterator: Iterable[ExternallyTraversible], 

858 obj: ExternallyTraversible, 

859 visitors: Mapping[str, _TraverseCallableType[Any]], 

860) -> ExternallyTraversible: ... 

861 

862 

863def traverse_using( 

864 iterator: Iterable[ExternallyTraversible], 

865 obj: Optional[ExternallyTraversible], 

866 visitors: Mapping[str, _TraverseCallableType[Any]], 

867) -> Optional[ExternallyTraversible]: 

868 """Visit the given expression structure using the given iterator of 

869 objects. 

870 

871 :func:`.visitors.traverse_using` is usually called internally as the result 

872 of the :func:`.visitors.traverse` function. 

873 

874 :param iterator: an iterable or sequence which will yield 

875 :class:`_expression.ClauseElement` 

876 structures; the iterator is assumed to be the 

877 product of the :func:`.visitors.iterate` function. 

878 

879 :param obj: the :class:`_expression.ClauseElement` 

880 that was used as the target of the 

881 :func:`.iterate` function. 

882 

883 :param visitors: dictionary of visit functions. See :func:`.traverse` 

884 for details on this dictionary. 

885 

886 .. seealso:: 

887 

888 :func:`.traverse` 

889 

890 

891 """ 

892 for target in iterator: 

893 meth = visitors.get(target.__visit_name__, None) 

894 if meth: 

895 meth(target) 

896 return obj 

897 

898 

899@overload 

900def traverse( 

901 obj: Literal[None], 

902 opts: Mapping[str, Any], 

903 visitors: Mapping[str, _TraverseCallableType[Any]], 

904) -> None: ... 

905 

906 

907@overload 

908def traverse( 

909 obj: ExternallyTraversible, 

910 opts: Mapping[str, Any], 

911 visitors: Mapping[str, _TraverseCallableType[Any]], 

912) -> ExternallyTraversible: ... 

913 

914 

915def traverse( 

916 obj: Optional[ExternallyTraversible], 

917 opts: Mapping[str, Any], 

918 visitors: Mapping[str, _TraverseCallableType[Any]], 

919) -> Optional[ExternallyTraversible]: 

920 """Traverse and visit the given expression structure using the default 

921 iterator. 

922 

923 e.g.:: 

924 

925 from sqlalchemy.sql import visitors 

926 

927 stmt = select(some_table).where(some_table.c.foo == "bar") 

928 

929 

930 def visit_bindparam(bind_param): 

931 print("found bound value: %s" % bind_param.value) 

932 

933 

934 visitors.traverse(stmt, {}, {"bindparam": visit_bindparam}) 

935 

936 The iteration of objects uses the :func:`.visitors.iterate` function, 

937 which does a breadth-first traversal using a stack. 

938 

939 :param obj: :class:`_expression.ClauseElement` structure to be traversed 

940 

941 :param opts: dictionary of iteration options. This dictionary is usually 

942 empty in modern usage. 

943 

944 :param visitors: dictionary of visit functions. The dictionary should 

945 have strings as keys, each of which would correspond to the 

946 ``__visit_name__`` of a particular kind of SQL expression object, and 

947 callable functions as values, each of which represents a visitor function 

948 for that kind of object. 

949 

950 """ 

951 return traverse_using(iterate(obj, opts), obj, visitors) 

952 

953 

954@overload 

955def cloned_traverse( 

956 obj: Literal[None], 

957 opts: Mapping[str, Any], 

958 visitors: Mapping[str, _TraverseCallableType[Any]], 

959) -> None: ... 

960 

961 

962# a bit of controversy here, as the clone of the lead element 

963# *could* in theory replace with an entirely different kind of element. 

964# however this is really not how cloned_traverse is ever used internally 

965# at least. 

966@overload 

967def cloned_traverse( 

968 obj: _ET, 

969 opts: Mapping[str, Any], 

970 visitors: Mapping[str, _TraverseCallableType[Any]], 

971) -> _ET: ... 

972 

973 

974def cloned_traverse( 

975 obj: Optional[ExternallyTraversible], 

976 opts: Mapping[str, Any], 

977 visitors: Mapping[str, _TraverseCallableType[Any]], 

978) -> Optional[ExternallyTraversible]: 

979 """Clone the given expression structure, allowing modifications by 

980 visitors for mutable objects. 

981 

982 Traversal usage is the same as that of :func:`.visitors.traverse`. 

983 The visitor functions present in the ``visitors`` dictionary may also 

984 modify the internals of the given structure as the traversal proceeds. 

985 

986 The :func:`.cloned_traverse` function does **not** provide objects that are 

987 part of the :class:`.Immutable` interface to the visit methods (this 

988 primarily includes :class:`.ColumnClause`, :class:`.Column`, 

989 :class:`.TableClause` and :class:`.Table` objects). As this traversal is 

990 only intended to allow in-place mutation of objects, :class:`.Immutable` 

991 objects are skipped. The :meth:`.Immutable._clone` method is still called 

992 on each object to allow for objects to replace themselves with a different 

993 object based on a clone of their sub-internals (e.g. a 

994 :class:`.ColumnClause` that clones its subquery to return a new 

995 :class:`.ColumnClause`). 

996 

997 .. versionchanged:: 2.0 The :func:`.cloned_traverse` function omits 

998 objects that are part of the :class:`.Immutable` interface. 

999 

1000 The central API feature used by the :func:`.visitors.cloned_traverse` 

1001 and :func:`.visitors.replacement_traverse` functions, in addition to the 

1002 :meth:`_expression.ClauseElement.get_children` 

1003 function that is used to achieve 

1004 the iteration, is the :meth:`_expression.ClauseElement._copy_internals` 

1005 method. 

1006 For a :class:`_expression.ClauseElement` 

1007 structure to support cloning and replacement 

1008 traversals correctly, it needs to be able to pass a cloning function into 

1009 its internal members in order to make copies of them. 

1010 

1011 .. seealso:: 

1012 

1013 :func:`.visitors.traverse` 

1014 

1015 :func:`.visitors.replacement_traverse` 

1016 

1017 """ 

1018 

1019 cloned: Dict[int, ExternallyTraversible] = {} 

1020 stop_on = set(opts.get("stop_on", [])) 

1021 

1022 def deferred_copy_internals( 

1023 obj: ExternallyTraversible, 

1024 ) -> ExternallyTraversible: 

1025 return cloned_traverse(obj, opts, visitors) 

1026 

1027 def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible: 

1028 if elem in stop_on: 

1029 return elem 

1030 else: 

1031 if id(elem) not in cloned: 

1032 if "replace" in kw: 

1033 newelem = cast( 

1034 Optional[ExternallyTraversible], kw["replace"](elem) 

1035 ) 

1036 if newelem is not None: 

1037 cloned[id(elem)] = newelem 

1038 return newelem 

1039 

1040 # the _clone method for immutable normally returns "self". 

1041 # however, the method is still allowed to return a 

1042 # different object altogether; ColumnClause._clone() will 

1043 # based on options clone the subquery to which it is associated 

1044 # and return the new corresponding column. 

1045 cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw) 

1046 newelem._copy_internals(clone=clone, **kw) 

1047 

1048 # however, visit methods which are tasked with in-place 

1049 # mutation of the object should not get access to the immutable 

1050 # object. 

1051 if not elem._is_immutable: 

1052 meth = visitors.get(newelem.__visit_name__, None) 

1053 if meth: 

1054 meth(newelem) 

1055 return cloned[id(elem)] 

1056 

1057 if obj is not None: 

1058 obj = clone( 

1059 obj, deferred_copy_internals=deferred_copy_internals, **opts 

1060 ) 

1061 clone = None # type: ignore[assignment] # remove gc cycles 

1062 return obj 

1063 

1064 

1065@overload 

1066def replacement_traverse( 

1067 obj: Literal[None], 

1068 opts: Mapping[str, Any], 

1069 replace: _TraverseTransformCallableType[Any], 

1070) -> None: ... 

1071 

1072 

1073@overload 

1074def replacement_traverse( 

1075 obj: _CE, 

1076 opts: Mapping[str, Any], 

1077 replace: _TraverseTransformCallableType[Any], 

1078) -> _CE: ... 

1079 

1080 

1081@overload 

1082def replacement_traverse( 

1083 obj: ExternallyTraversible, 

1084 opts: Mapping[str, Any], 

1085 replace: _TraverseTransformCallableType[Any], 

1086) -> ExternallyTraversible: ... 

1087 

1088 

1089def replacement_traverse( 

1090 obj: Optional[ExternallyTraversible], 

1091 opts: Mapping[str, Any], 

1092 replace: _TraverseTransformCallableType[Any], 

1093) -> Optional[ExternallyTraversible]: 

1094 """Clone the given expression structure, allowing element 

1095 replacement by a given replacement function. 

1096 

1097 This function is very similar to the :func:`.visitors.cloned_traverse` 

1098 function, except instead of being passed a dictionary of visitors, all 

1099 elements are unconditionally passed into the given replace function. 

1100 The replace function then has the option to return an entirely new object 

1101 which will replace the one given. If it returns ``None``, then the object 

1102 is kept in place. 

1103 

1104 The difference in usage between :func:`.visitors.cloned_traverse` and 

1105 :func:`.visitors.replacement_traverse` is that in the former case, an 

1106 already-cloned object is passed to the visitor function, and the visitor 

1107 function can then manipulate the internal state of the object. 

1108 In the case of the latter, the visitor function should only return an 

1109 entirely different object, or do nothing. 

1110 

1111 The use case for :func:`.visitors.replacement_traverse` is that of 

1112 replacing a FROM clause inside of a SQL structure with a different one, 

1113 as is a common use case within the ORM. 

1114 

1115 """ 

1116 

1117 cloned = {} 

1118 stop_on = {id(x) for x in opts.get("stop_on", [])} 

1119 

1120 def deferred_copy_internals( 

1121 obj: ExternallyTraversible, 

1122 ) -> ExternallyTraversible: 

1123 return replacement_traverse(obj, opts, replace) 

1124 

1125 def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible: 

1126 if ( 

1127 id(elem) in stop_on 

1128 or "no_replacement_traverse" in elem._annotations 

1129 ): 

1130 return elem 

1131 else: 

1132 newelem = replace(elem) 

1133 if newelem is not None: 

1134 stop_on.add(id(newelem)) 

1135 return newelem # type: ignore 

1136 else: 

1137 # base "already seen" on id(), not hash, so that we don't 

1138 # replace an Annotated element with its non-annotated one, and 

1139 # vice versa 

1140 id_elem = id(elem) 

1141 if id_elem not in cloned: 

1142 if "replace" in kw: 

1143 newelem = kw["replace"](elem) 

1144 if newelem is not None: 

1145 cloned[id_elem] = newelem 

1146 return newelem # type: ignore 

1147 

1148 cloned[id_elem] = newelem = elem._clone(**kw) 

1149 newelem._copy_internals(clone=clone, **kw) 

1150 return cloned[id_elem] # type: ignore 

1151 

1152 if obj is not None: 

1153 obj = clone( 

1154 obj, deferred_copy_internals=deferred_copy_internals, **opts 

1155 ) 

1156 clone = None # type: ignore[assignment] # remove gc cycles 

1157 return obj