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

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

369 statements  

1# sql/visitors.py 

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

3# <see AUTHORS file> 

4# 

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

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

7 

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

9 

10from __future__ import annotations 

11 

12from collections import deque 

13from enum import Enum 

14import itertools 

15import operator 

16import typing 

17from typing import Any 

18from typing import Callable 

19from typing import cast 

20from typing import ClassVar 

21from typing import Dict 

22from typing import Iterable 

23from typing import Iterator 

24from typing import List 

25from typing import 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[no-any-return] # noqa: E501 

127 else: 

128 return meth(self, **kw) # type: ignore[no-any-return] # noqa: E501 

129 

130 cls._compiler_dispatch = ( # type: ignore[method-assign] 

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_params = "PM" 

410 """Visit the _params collection of ExecutableStatement""" 

411 

412 

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

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

415traversed. 

416 

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

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

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

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

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

422be handled. 

423 

424""" 

425 

426 

427class HasTraverseInternals: 

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

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

430 

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

432 define how the object is traversed (i.e. the :class:`.SQLCompiler`), the 

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

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

435 

436 """ 

437 

438 __slots__ = () 

439 

440 _traverse_internals: _TraverseInternalsType 

441 

442 _is_immutable: bool = False 

443 

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

445 def get_children( 

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

447 ) -> Iterable[HasTraverseInternals]: 

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

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

450 

451 This is used for visit traversal. 

452 

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

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

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

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

457 clause-level). 

458 

459 """ 

460 

461 traversals = util.preloaded.sql_traversals 

462 

463 try: 

464 traverse_internals = self._traverse_internals 

465 except AttributeError: 

466 # user-defined classes may not have a _traverse_internals 

467 return [] 

468 

469 dispatch = traversals._get_children.run_generated_dispatch 

470 return itertools.chain.from_iterable( 

471 meth(obj, **kw) 

472 for attrname, obj, meth in dispatch( 

473 self, traverse_internals, "_generated_get_children_traversal" 

474 ) 

475 if attrname not in omit_attrs and obj is not None 

476 ) 

477 

478 

479class _InternalTraversalDispatchType(Protocol): 

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

481 

482 

483class HasTraversalDispatch: 

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

485 

486 .. versionadded:: 2.0 

487 

488 """ 

489 

490 __slots__ = () 

491 

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

493 

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

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

496 corresponding method on a subclass. 

497 

498 """ 

499 name = _dispatch_lookup[visit_symbol] 

500 return getattr(self, name, None) # type: ignore[return-value] 

501 

502 def run_generated_dispatch( 

503 self, 

504 target: object, 

505 internal_dispatch: _TraverseInternalsType, 

506 generate_dispatcher_name: str, 

507 ) -> Any: 

508 dispatcher: _InternalTraversalDispatchType 

509 try: 

510 dispatcher = target.__class__.__dict__[generate_dispatcher_name] 

511 except KeyError: 

512 # traversals.py -> _preconfigure_traversals() 

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

514 # is not enabled right now. 

515 # this block will generate any remaining dispatchers. 

516 dispatcher = self.generate_dispatch( 

517 target.__class__, internal_dispatch, generate_dispatcher_name 

518 ) 

519 return dispatcher(target, self) 

520 

521 def generate_dispatch( 

522 self, 

523 target_cls: Type[object], 

524 internal_dispatch: _TraverseInternalsType, 

525 generate_dispatcher_name: str, 

526 ) -> _InternalTraversalDispatchType: 

527 dispatcher = self._generate_dispatcher( 

528 internal_dispatch, generate_dispatcher_name 

529 ) 

530 # assert isinstance(target_cls, type) 

531 setattr(target_cls, generate_dispatcher_name, dispatcher) 

532 return dispatcher 

533 

534 def _generate_dispatcher( 

535 self, internal_dispatch: _TraverseInternalsType, method_name: str 

536 ) -> _InternalTraversalDispatchType: 

537 names = [] 

538 for attrname, visit_sym in internal_dispatch: 

539 meth = self.dispatch(visit_sym) 

540 if meth is not None: 

541 visit_name = _dispatch_lookup[visit_sym] 

542 names.append((attrname, visit_name)) 

543 

544 code = ( 

545 (" return [\n") 

546 + ( 

547 ", \n".join( 

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

549 % (attrname, attrname, visit_name) 

550 for attrname, visit_name in names 

551 ) 

552 ) 

553 + ("\n ]\n") 

554 ) 

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

556 return cast( 

557 _InternalTraversalDispatchType, 

558 langhelpers.exec_code_in_env(meth_text, {}, method_name), 

559 ) 

560 

561 

562ExtendedInternalTraversal = InternalTraversal 

563 

564 

565def _generate_traversal_dispatch() -> None: 

566 lookup = _dispatch_lookup 

567 

568 for sym in InternalTraversal: 

569 key = sym.name 

570 if key.startswith("dp_"): 

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

572 sym_name = sym.value 

573 assert sym_name not in lookup, sym_name 

574 lookup[sym] = lookup[sym_name] = visit_key 

575 

576 

577_dispatch_lookup = HasTraversalDispatch._dispatch_lookup 

578_generate_traversal_dispatch() 

579 

580 

581class ExternallyTraversible(HasTraverseInternals, Visitable): 

582 __slots__ = () 

583 

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

585 

586 if typing.TYPE_CHECKING: 

587 

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

589 

590 def get_children( 

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

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

593 

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

595 """clone this element""" 

596 raise NotImplementedError() 

597 

598 def _copy_internals( 

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

600 ) -> None: 

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

602 

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

604 shallow-copied elements to create a deep copy. 

605 

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

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

608 traversal, cloned traversal, annotations). 

609 

610 """ 

611 raise NotImplementedError() 

612 

613 

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

615 

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

617 

618_TraverseCallableType = Callable[[_ET], None] 

619 

620 

621class _CloneCallableType(Protocol): 

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

623 

624 

625class _TraverseTransformCallableType(Protocol[_ET]): 

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

627 

628 

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

630 

631 

632class ExternalTraversal(util.MemoizedSlots): 

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

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

635 

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

637 preferred. 

638 

639 """ 

640 

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

642 

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

644 _next: Optional[ExternalTraversal] 

645 

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

647 for v in self.visitor_iterator: 

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

649 if meth: 

650 return meth(obj, **kw) 

651 

652 def iterate( 

653 self, obj: Optional[ExternallyTraversible] 

654 ) -> Iterator[ExternallyTraversible]: 

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

656 of all elements. 

657 

658 """ 

659 return iterate(obj, self.__traverse_options__) 

660 

661 @overload 

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

663 

664 @overload 

665 def traverse( 

666 self, obj: ExternallyTraversible 

667 ) -> ExternallyTraversible: ... 

668 

669 def traverse( 

670 self, obj: Optional[ExternallyTraversible] 

671 ) -> Optional[ExternallyTraversible]: 

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

673 

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

675 

676 def _memoized_attr__visitor_dict( 

677 self, 

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

679 visitors = {} 

680 

681 for name in dir(self): 

682 if name.startswith("visit_"): 

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

684 return visitors 

685 

686 @property 

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

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

689 

690 v: Optional[ExternalTraversal] = self 

691 while v: 

692 yield v 

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

694 

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

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

697 

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

699 

700 """ 

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

702 tail._next = visitor 

703 return self 

704 

705 

706class CloningExternalTraversal(ExternalTraversal): 

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

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

709 

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

711 preferred. 

712 

713 

714 """ 

715 

716 __slots__ = () 

717 

718 def copy_and_process( 

719 self, list_: List[ExternallyTraversible] 

720 ) -> List[ExternallyTraversible]: 

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

722 the new list. 

723 

724 """ 

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

726 

727 @overload 

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

729 

730 @overload 

731 def traverse( 

732 self, obj: ExternallyTraversible 

733 ) -> ExternallyTraversible: ... 

734 

735 def traverse( 

736 self, obj: Optional[ExternallyTraversible] 

737 ) -> Optional[ExternallyTraversible]: 

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

739 

740 return cloned_traverse( 

741 obj, self.__traverse_options__, self._visitor_dict 

742 ) 

743 

744 

745class ReplacingExternalTraversal(CloningExternalTraversal): 

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

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

748 

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

750 usually preferred. 

751 

752 """ 

753 

754 __slots__ = () 

755 

756 def replace( 

757 self, elem: ExternallyTraversible 

758 ) -> Optional[ExternallyTraversible]: 

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

760 

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

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

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

764 """ 

765 return None 

766 

767 @overload 

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

769 

770 @overload 

771 def traverse( 

772 self, obj: ExternallyTraversible 

773 ) -> ExternallyTraversible: ... 

774 

775 def traverse( 

776 self, obj: Optional[ExternallyTraversible] 

777 ) -> Optional[ExternallyTraversible]: 

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

779 

780 def replace( 

781 element: ExternallyTraversible, 

782 **kw: Any, 

783 ) -> Optional[ExternallyTraversible]: 

784 for v in self.visitor_iterator: 

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

786 if e is not None: 

787 return e 

788 

789 return None 

790 

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

792 

793 

794# backwards compatibility 

795Traversible = Visitable 

796 

797ClauseVisitor = ExternalTraversal 

798CloningVisitor = CloningExternalTraversal 

799ReplacingCloningVisitor = ReplacingExternalTraversal 

800 

801 

802def iterate( 

803 obj: Optional[ExternallyTraversible], 

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

805) -> Iterator[ExternallyTraversible]: 

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

807 

808 Traversal is configured to be breadth-first. 

809 

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

811 function is the 

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

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

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

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

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

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

818 member variables. 

819 

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

821 

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

823 empty in modern usage. 

824 

825 """ 

826 if obj is None: 

827 return 

828 

829 yield obj 

830 children = obj.get_children(**opts) 

831 

832 if not children: 

833 return 

834 

835 stack = deque([children]) 

836 while stack: 

837 t_iterator = stack.popleft() 

838 for t in t_iterator: 

839 yield t 

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

841 

842 

843@overload 

844def traverse_using( 

845 iterator: Iterable[ExternallyTraversible], 

846 obj: Literal[None], 

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

848) -> None: ... 

849 

850 

851@overload 

852def traverse_using( 

853 iterator: Iterable[ExternallyTraversible], 

854 obj: ExternallyTraversible, 

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

856) -> ExternallyTraversible: ... 

857 

858 

859def traverse_using( 

860 iterator: Iterable[ExternallyTraversible], 

861 obj: Optional[ExternallyTraversible], 

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

863) -> Optional[ExternallyTraversible]: 

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

865 objects. 

866 

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

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

869 

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

871 :class:`_expression.ClauseElement` 

872 structures; the iterator is assumed to be the 

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

874 

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

876 that was used as the target of the 

877 :func:`.iterate` function. 

878 

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

880 for details on this dictionary. 

881 

882 .. seealso:: 

883 

884 :func:`.traverse` 

885 

886 

887 """ 

888 for target in iterator: 

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

890 if meth: 

891 meth(target) 

892 return obj 

893 

894 

895@overload 

896def traverse( 

897 obj: Literal[None], 

898 opts: Mapping[str, Any], 

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

900) -> None: ... 

901 

902 

903@overload 

904def traverse( 

905 obj: ExternallyTraversible, 

906 opts: Mapping[str, Any], 

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

908) -> ExternallyTraversible: ... 

909 

910 

911def traverse( 

912 obj: Optional[ExternallyTraversible], 

913 opts: Mapping[str, Any], 

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

915) -> Optional[ExternallyTraversible]: 

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

917 iterator. 

918 

919 e.g.:: 

920 

921 from sqlalchemy.sql import visitors 

922 

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

924 

925 

926 def visit_bindparam(bind_param): 

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

928 

929 

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

931 

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

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

934 

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

936 

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

938 empty in modern usage. 

939 

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

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

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

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

944 for that kind of object. 

945 

946 """ 

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

948 

949 

950@overload 

951def cloned_traverse( 

952 obj: Literal[None], 

953 opts: Mapping[str, Any], 

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

955) -> None: ... 

956 

957 

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

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

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

961# at least. 

962@overload 

963def cloned_traverse( 

964 obj: _ET, 

965 opts: Mapping[str, Any], 

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

967) -> _ET: ... 

968 

969 

970def cloned_traverse( 

971 obj: Optional[ExternallyTraversible], 

972 opts: Mapping[str, Any], 

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

974) -> Optional[ExternallyTraversible]: 

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

976 visitors for mutable objects. 

977 

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

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

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

981 

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

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

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

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

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

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

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

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

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

991 :class:`.ColumnClause`). 

992 

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

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

995 

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

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

998 :meth:`_expression.ClauseElement.get_children` 

999 function that is used to achieve 

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

1001 method. 

1002 For a :class:`_expression.ClauseElement` 

1003 structure to support cloning and replacement 

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

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

1006 

1007 .. seealso:: 

1008 

1009 :func:`.visitors.traverse` 

1010 

1011 :func:`.visitors.replacement_traverse` 

1012 

1013 """ 

1014 

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

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

1017 

1018 def deferred_copy_internals( 

1019 obj: ExternallyTraversible, 

1020 ) -> ExternallyTraversible: 

1021 return cloned_traverse(obj, opts, visitors) 

1022 

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

1024 if elem in stop_on: 

1025 return elem 

1026 else: 

1027 if id(elem) not in cloned: 

1028 if "replace" in kw: 

1029 newelem = cast( 

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

1031 ) 

1032 if newelem is not None: 

1033 cloned[id(elem)] = newelem 

1034 return newelem 

1035 

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

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

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

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

1040 # and return the new corresponding column. 

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

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

1043 

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

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

1046 # object. 

1047 if not elem._is_immutable: 

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

1049 if meth: 

1050 meth(newelem) 

1051 return cloned[id(elem)] 

1052 

1053 if obj is not None: 

1054 obj = clone( 

1055 obj, deferred_copy_internals=deferred_copy_internals, **opts 

1056 ) 

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

1058 return obj 

1059 

1060 

1061@overload 

1062def replacement_traverse( 

1063 obj: Literal[None], 

1064 opts: Mapping[str, Any], 

1065 replace: _TraverseTransformCallableType[Any], 

1066) -> None: ... 

1067 

1068 

1069@overload 

1070def replacement_traverse( 

1071 obj: _CE, 

1072 opts: Mapping[str, Any], 

1073 replace: _TraverseTransformCallableType[Any], 

1074) -> _CE: ... 

1075 

1076 

1077@overload 

1078def replacement_traverse( 

1079 obj: ExternallyTraversible, 

1080 opts: Mapping[str, Any], 

1081 replace: _TraverseTransformCallableType[Any], 

1082) -> ExternallyTraversible: ... 

1083 

1084 

1085def replacement_traverse( 

1086 obj: Optional[ExternallyTraversible], 

1087 opts: Mapping[str, Any], 

1088 replace: _TraverseTransformCallableType[Any], 

1089) -> Optional[ExternallyTraversible]: 

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

1091 replacement by a given replacement function. 

1092 

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

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

1095 elements are unconditionally passed into the given replace function. 

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

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

1098 is kept in place. 

1099 

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

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

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

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

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

1105 entirely different object, or do nothing. 

1106 

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

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

1109 as is a common use case within the ORM. 

1110 

1111 """ 

1112 

1113 cloned = {} 

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

1115 

1116 def deferred_copy_internals( 

1117 obj: ExternallyTraversible, 

1118 ) -> ExternallyTraversible: 

1119 return replacement_traverse(obj, opts, replace) 

1120 

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

1122 if ( 

1123 id(elem) in stop_on 

1124 or "no_replacement_traverse" in elem._annotations 

1125 ): 

1126 return elem 

1127 else: 

1128 newelem = replace(elem) 

1129 if newelem is not None: 

1130 stop_on.add(id(newelem)) 

1131 return newelem # type: ignore[no-any-return] 

1132 else: 

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

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

1135 # vice versa 

1136 id_elem = id(elem) 

1137 if id_elem not in cloned: 

1138 if "replace" in kw: 

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

1140 if newelem is not None: 

1141 cloned[id_elem] = newelem 

1142 return newelem # type: ignore[no-any-return] 

1143 

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

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

1146 return cloned[id_elem] # type: ignore[no-any-return] 

1147 

1148 if obj is not None: 

1149 obj = clone( 

1150 obj, deferred_copy_internals=deferred_copy_internals, **opts 

1151 ) 

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

1153 return obj