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

369 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 Mapping 

26from typing import Optional 

27from typing import overload 

28from typing import Protocol 

29from typing import Tuple 

30from typing import Type 

31from typing import TYPE_CHECKING 

32from typing import TypeVar 

33from typing import Union 

34 

35from ._util_cy import anon_map as anon_map 

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

37from .. import exc 

38from .. import util 

39from ..util import langhelpers 

40from ..util.typing import Literal 

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 

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

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

416traversed. 

417 

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

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

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

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

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

423be handled. 

424 

425""" 

426 

427 

428class HasTraverseInternals: 

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

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

431 

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

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

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

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

436 

437 """ 

438 

439 __slots__ = () 

440 

441 _traverse_internals: _TraverseInternalsType 

442 

443 _is_immutable: bool = False 

444 

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

446 def get_children( 

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

448 ) -> Iterable[HasTraverseInternals]: 

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

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

451 

452 This is used for visit traversal. 

453 

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

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

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

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

458 clause-level). 

459 

460 """ 

461 

462 traversals = util.preloaded.sql_traversals 

463 

464 try: 

465 traverse_internals = self._traverse_internals 

466 except AttributeError: 

467 # user-defined classes may not have a _traverse_internals 

468 return [] 

469 

470 dispatch = traversals._get_children.run_generated_dispatch 

471 return itertools.chain.from_iterable( 

472 meth(obj, **kw) 

473 for attrname, obj, meth in dispatch( 

474 self, traverse_internals, "_generated_get_children_traversal" 

475 ) 

476 if attrname not in omit_attrs and obj is not None 

477 ) 

478 

479 

480class _InternalTraversalDispatchType(Protocol): 

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

482 

483 

484class HasTraversalDispatch: 

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

486 

487 .. versionadded:: 2.0 

488 

489 """ 

490 

491 __slots__ = () 

492 

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

494 

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

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

497 corresponding method on a subclass. 

498 

499 """ 

500 name = _dispatch_lookup[visit_symbol] 

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

502 

503 def run_generated_dispatch( 

504 self, 

505 target: object, 

506 internal_dispatch: _TraverseInternalsType, 

507 generate_dispatcher_name: str, 

508 ) -> Any: 

509 dispatcher: _InternalTraversalDispatchType 

510 try: 

511 dispatcher = target.__class__.__dict__[generate_dispatcher_name] 

512 except KeyError: 

513 # traversals.py -> _preconfigure_traversals() 

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

515 # is not enabled right now. 

516 # this block will generate any remaining dispatchers. 

517 dispatcher = self.generate_dispatch( 

518 target.__class__, internal_dispatch, generate_dispatcher_name 

519 ) 

520 return dispatcher(target, self) 

521 

522 def generate_dispatch( 

523 self, 

524 target_cls: Type[object], 

525 internal_dispatch: _TraverseInternalsType, 

526 generate_dispatcher_name: str, 

527 ) -> _InternalTraversalDispatchType: 

528 dispatcher = self._generate_dispatcher( 

529 internal_dispatch, generate_dispatcher_name 

530 ) 

531 # assert isinstance(target_cls, type) 

532 setattr(target_cls, generate_dispatcher_name, dispatcher) 

533 return dispatcher 

534 

535 def _generate_dispatcher( 

536 self, internal_dispatch: _TraverseInternalsType, method_name: str 

537 ) -> _InternalTraversalDispatchType: 

538 names = [] 

539 for attrname, visit_sym in internal_dispatch: 

540 meth = self.dispatch(visit_sym) 

541 if meth is not None: 

542 visit_name = _dispatch_lookup[visit_sym] 

543 names.append((attrname, visit_name)) 

544 

545 code = ( 

546 (" return [\n") 

547 + ( 

548 ", \n".join( 

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

550 % (attrname, attrname, visit_name) 

551 for attrname, visit_name in names 

552 ) 

553 ) 

554 + ("\n ]\n") 

555 ) 

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

557 return cast( 

558 _InternalTraversalDispatchType, 

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

560 ) 

561 

562 

563ExtendedInternalTraversal = InternalTraversal 

564 

565 

566def _generate_traversal_dispatch() -> None: 

567 lookup = _dispatch_lookup 

568 

569 for sym in InternalTraversal: 

570 key = sym.name 

571 if key.startswith("dp_"): 

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

573 sym_name = sym.value 

574 assert sym_name not in lookup, sym_name 

575 lookup[sym] = lookup[sym_name] = visit_key 

576 

577 

578_dispatch_lookup = HasTraversalDispatch._dispatch_lookup 

579_generate_traversal_dispatch() 

580 

581 

582class ExternallyTraversible(HasTraverseInternals, Visitable): 

583 __slots__ = () 

584 

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

586 

587 if typing.TYPE_CHECKING: 

588 

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

590 

591 def get_children( 

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

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

594 

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

596 """clone this element""" 

597 raise NotImplementedError() 

598 

599 def _copy_internals( 

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

601 ) -> None: 

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

603 

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

605 shallow-copied elements to create a deep copy. 

606 

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

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

609 traversal, cloned traversal, annotations). 

610 

611 """ 

612 raise NotImplementedError() 

613 

614 

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

616 

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

618 

619_TraverseCallableType = Callable[[_ET], None] 

620 

621 

622class _CloneCallableType(Protocol): 

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

624 

625 

626class _TraverseTransformCallableType(Protocol[_ET]): 

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

628 

629 

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

631 

632 

633class ExternalTraversal(util.MemoizedSlots): 

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

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

636 

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

638 preferred. 

639 

640 """ 

641 

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

643 

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

645 _next: Optional[ExternalTraversal] 

646 

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

648 for v in self.visitor_iterator: 

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

650 if meth: 

651 return meth(obj, **kw) 

652 

653 def iterate( 

654 self, obj: Optional[ExternallyTraversible] 

655 ) -> Iterator[ExternallyTraversible]: 

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

657 of all elements. 

658 

659 """ 

660 return iterate(obj, self.__traverse_options__) 

661 

662 @overload 

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

664 

665 @overload 

666 def traverse( 

667 self, obj: ExternallyTraversible 

668 ) -> ExternallyTraversible: ... 

669 

670 def traverse( 

671 self, obj: Optional[ExternallyTraversible] 

672 ) -> Optional[ExternallyTraversible]: 

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

674 

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

676 

677 def _memoized_attr__visitor_dict( 

678 self, 

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

680 visitors = {} 

681 

682 for name in dir(self): 

683 if name.startswith("visit_"): 

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

685 return visitors 

686 

687 @property 

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

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

690 

691 v: Optional[ExternalTraversal] = self 

692 while v: 

693 yield v 

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

695 

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

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

698 

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

700 

701 """ 

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

703 tail._next = visitor 

704 return self 

705 

706 

707class CloningExternalTraversal(ExternalTraversal): 

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

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

710 

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

712 preferred. 

713 

714 

715 """ 

716 

717 __slots__ = () 

718 

719 def copy_and_process( 

720 self, list_: List[ExternallyTraversible] 

721 ) -> List[ExternallyTraversible]: 

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

723 the new list. 

724 

725 """ 

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

727 

728 @overload 

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

730 

731 @overload 

732 def traverse( 

733 self, obj: ExternallyTraversible 

734 ) -> ExternallyTraversible: ... 

735 

736 def traverse( 

737 self, obj: Optional[ExternallyTraversible] 

738 ) -> Optional[ExternallyTraversible]: 

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

740 

741 return cloned_traverse( 

742 obj, self.__traverse_options__, self._visitor_dict 

743 ) 

744 

745 

746class ReplacingExternalTraversal(CloningExternalTraversal): 

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

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

749 

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

751 usually preferred. 

752 

753 """ 

754 

755 __slots__ = () 

756 

757 def replace( 

758 self, elem: ExternallyTraversible 

759 ) -> Optional[ExternallyTraversible]: 

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

761 

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

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

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

765 """ 

766 return None 

767 

768 @overload 

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

770 

771 @overload 

772 def traverse( 

773 self, obj: ExternallyTraversible 

774 ) -> ExternallyTraversible: ... 

775 

776 def traverse( 

777 self, obj: Optional[ExternallyTraversible] 

778 ) -> Optional[ExternallyTraversible]: 

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

780 

781 def replace( 

782 element: ExternallyTraversible, 

783 **kw: Any, 

784 ) -> Optional[ExternallyTraversible]: 

785 for v in self.visitor_iterator: 

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

787 if e is not None: 

788 return e 

789 

790 return None 

791 

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

793 

794 

795# backwards compatibility 

796Traversible = Visitable 

797 

798ClauseVisitor = ExternalTraversal 

799CloningVisitor = CloningExternalTraversal 

800ReplacingCloningVisitor = ReplacingExternalTraversal 

801 

802 

803def iterate( 

804 obj: Optional[ExternallyTraversible], 

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

806) -> Iterator[ExternallyTraversible]: 

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

808 

809 Traversal is configured to be breadth-first. 

810 

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

812 function is the 

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

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

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

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

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

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

819 member variables. 

820 

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

822 

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

824 empty in modern usage. 

825 

826 """ 

827 if obj is None: 

828 return 

829 

830 yield obj 

831 children = obj.get_children(**opts) 

832 

833 if not children: 

834 return 

835 

836 stack = deque([children]) 

837 while stack: 

838 t_iterator = stack.popleft() 

839 for t in t_iterator: 

840 yield t 

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

842 

843 

844@overload 

845def traverse_using( 

846 iterator: Iterable[ExternallyTraversible], 

847 obj: Literal[None], 

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

849) -> None: ... 

850 

851 

852@overload 

853def traverse_using( 

854 iterator: Iterable[ExternallyTraversible], 

855 obj: ExternallyTraversible, 

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

857) -> ExternallyTraversible: ... 

858 

859 

860def traverse_using( 

861 iterator: Iterable[ExternallyTraversible], 

862 obj: Optional[ExternallyTraversible], 

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

864) -> Optional[ExternallyTraversible]: 

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

866 objects. 

867 

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

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

870 

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

872 :class:`_expression.ClauseElement` 

873 structures; the iterator is assumed to be the 

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

875 

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

877 that was used as the target of the 

878 :func:`.iterate` function. 

879 

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

881 for details on this dictionary. 

882 

883 .. seealso:: 

884 

885 :func:`.traverse` 

886 

887 

888 """ 

889 for target in iterator: 

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

891 if meth: 

892 meth(target) 

893 return obj 

894 

895 

896@overload 

897def traverse( 

898 obj: Literal[None], 

899 opts: Mapping[str, Any], 

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

901) -> None: ... 

902 

903 

904@overload 

905def traverse( 

906 obj: ExternallyTraversible, 

907 opts: Mapping[str, Any], 

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

909) -> ExternallyTraversible: ... 

910 

911 

912def traverse( 

913 obj: Optional[ExternallyTraversible], 

914 opts: Mapping[str, Any], 

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

916) -> Optional[ExternallyTraversible]: 

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

918 iterator. 

919 

920 e.g.:: 

921 

922 from sqlalchemy.sql import visitors 

923 

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

925 

926 

927 def visit_bindparam(bind_param): 

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

929 

930 

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

932 

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

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

935 

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

937 

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

939 empty in modern usage. 

940 

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

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

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

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

945 for that kind of object. 

946 

947 """ 

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

949 

950 

951@overload 

952def cloned_traverse( 

953 obj: Literal[None], 

954 opts: Mapping[str, Any], 

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

956) -> None: ... 

957 

958 

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

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

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

962# at least. 

963@overload 

964def cloned_traverse( 

965 obj: _ET, 

966 opts: Mapping[str, Any], 

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

968) -> _ET: ... 

969 

970 

971def cloned_traverse( 

972 obj: Optional[ExternallyTraversible], 

973 opts: Mapping[str, Any], 

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

975) -> Optional[ExternallyTraversible]: 

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

977 visitors for mutable objects. 

978 

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

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

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

982 

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

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

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

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

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

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

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

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

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

992 :class:`.ColumnClause`). 

993 

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

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

996 

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

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

999 :meth:`_expression.ClauseElement.get_children` 

1000 function that is used to achieve 

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

1002 method. 

1003 For a :class:`_expression.ClauseElement` 

1004 structure to support cloning and replacement 

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

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

1007 

1008 .. seealso:: 

1009 

1010 :func:`.visitors.traverse` 

1011 

1012 :func:`.visitors.replacement_traverse` 

1013 

1014 """ 

1015 

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

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

1018 

1019 def deferred_copy_internals( 

1020 obj: ExternallyTraversible, 

1021 ) -> ExternallyTraversible: 

1022 return cloned_traverse(obj, opts, visitors) 

1023 

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

1025 if elem in stop_on: 

1026 return elem 

1027 else: 

1028 if id(elem) not in cloned: 

1029 if "replace" in kw: 

1030 newelem = cast( 

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

1032 ) 

1033 if newelem is not None: 

1034 cloned[id(elem)] = newelem 

1035 return newelem 

1036 

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

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

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

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

1041 # and return the new corresponding column. 

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

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

1044 

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

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

1047 # object. 

1048 if not elem._is_immutable: 

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

1050 if meth: 

1051 meth(newelem) 

1052 return cloned[id(elem)] 

1053 

1054 if obj is not None: 

1055 obj = clone( 

1056 obj, deferred_copy_internals=deferred_copy_internals, **opts 

1057 ) 

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

1059 return obj 

1060 

1061 

1062@overload 

1063def replacement_traverse( 

1064 obj: Literal[None], 

1065 opts: Mapping[str, Any], 

1066 replace: _TraverseTransformCallableType[Any], 

1067) -> None: ... 

1068 

1069 

1070@overload 

1071def replacement_traverse( 

1072 obj: _CE, 

1073 opts: Mapping[str, Any], 

1074 replace: _TraverseTransformCallableType[Any], 

1075) -> _CE: ... 

1076 

1077 

1078@overload 

1079def replacement_traverse( 

1080 obj: ExternallyTraversible, 

1081 opts: Mapping[str, Any], 

1082 replace: _TraverseTransformCallableType[Any], 

1083) -> ExternallyTraversible: ... 

1084 

1085 

1086def replacement_traverse( 

1087 obj: Optional[ExternallyTraversible], 

1088 opts: Mapping[str, Any], 

1089 replace: _TraverseTransformCallableType[Any], 

1090) -> Optional[ExternallyTraversible]: 

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

1092 replacement by a given replacement function. 

1093 

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

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

1096 elements are unconditionally passed into the given replace function. 

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

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

1099 is kept in place. 

1100 

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

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

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

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

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

1106 entirely different object, or do nothing. 

1107 

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

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

1110 as is a common use case within the ORM. 

1111 

1112 """ 

1113 

1114 cloned = {} 

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

1116 

1117 def deferred_copy_internals( 

1118 obj: ExternallyTraversible, 

1119 ) -> ExternallyTraversible: 

1120 return replacement_traverse(obj, opts, replace) 

1121 

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

1123 if ( 

1124 id(elem) in stop_on 

1125 or "no_replacement_traverse" in elem._annotations 

1126 ): 

1127 return elem 

1128 else: 

1129 newelem = replace(elem) 

1130 if newelem is not None: 

1131 stop_on.add(id(newelem)) 

1132 return newelem # type: ignore 

1133 else: 

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

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

1136 # vice versa 

1137 id_elem = id(elem) 

1138 if id_elem not in cloned: 

1139 if "replace" in kw: 

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

1141 if newelem is not None: 

1142 cloned[id_elem] = newelem 

1143 return newelem # type: ignore 

1144 

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

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

1147 return cloned[id_elem] # type: ignore 

1148 

1149 if obj is not None: 

1150 obj = clone( 

1151 obj, deferred_copy_internals=deferred_copy_internals, **opts 

1152 ) 

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

1154 return obj