Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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

332 statements  

1# sql/visitors.py 

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

3# <see AUTHORS file> 

4# 

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

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

7 

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

9 

10 

11""" 

12 

13from __future__ import annotations 

14 

15from collections import deque 

16from enum import Enum 

17import itertools 

18import operator 

19import typing 

20from typing import Any 

21from typing import Callable 

22from typing import cast 

23from typing import ClassVar 

24from typing import Dict 

25from typing import Iterable 

26from typing import Iterator 

27from typing import List 

28from typing import Mapping 

29from typing import Optional 

30from typing import overload 

31from typing import Protocol 

32from typing import Tuple 

33from typing import Type 

34from typing import TYPE_CHECKING 

35from typing import TypeVar 

36from typing import Union 

37 

38from ._util_cy import anon_map as anon_map 

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

40from .. import exc 

41from .. import util 

42from ..util import langhelpers 

43from ..util.typing import Literal 

44from ..util.typing import Self 

45 

46if TYPE_CHECKING: 

47 from .annotation import _AnnotationDict 

48 from .elements import ColumnElement 

49 

50 

51__all__ = [ 

52 "iterate", 

53 "traverse_using", 

54 "traverse", 

55 "cloned_traverse", 

56 "replacement_traverse", 

57 "Visitable", 

58 "ExternalTraversal", 

59 "InternalTraversal", 

60 "anon_map", 

61] 

62 

63 

64class _CompilerDispatchType(Protocol): 

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

66 

67 

68class Visitable: 

69 """Base class for visitable objects. 

70 

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

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

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

74 interface. 

75 

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

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

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

79 

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

81 

82 """ 

83 

84 __slots__ = () 

85 

86 __visit_name__: str 

87 

88 _original_compiler_dispatch: _CompilerDispatchType 

89 

90 if typing.TYPE_CHECKING: 

91 

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

93 

94 def __init_subclass__(cls) -> None: 

95 if "__visit_name__" in cls.__dict__: 

96 cls._generate_compiler_dispatch() 

97 super().__init_subclass__() 

98 

99 @classmethod 

100 def _generate_compiler_dispatch(cls) -> None: 

101 visit_name = cls.__visit_name__ 

102 

103 if "_compiler_dispatch" in cls.__dict__: 

104 # class has a fixed _compiler_dispatch() method. 

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

106 # sqlalchemy.ext.compiles overrides it. 

107 cls._original_compiler_dispatch = cls._compiler_dispatch 

108 return 

109 

110 if not isinstance(visit_name, str): 

111 raise exc.InvalidRequestError( 

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

113 "at the class level" 

114 ) 

115 

116 name = "visit_%s" % visit_name 

117 getter = operator.attrgetter(name) 

118 

119 def _compiler_dispatch( 

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

121 ) -> str: 

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

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

124 

125 """ 

126 try: 

127 meth = getter(visitor) 

128 except AttributeError as err: 

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

130 else: 

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

132 

133 cls._compiler_dispatch = ( # type: ignore 

134 cls._original_compiler_dispatch 

135 ) = _compiler_dispatch 

136 

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

138 # allow generic classes in py3.9+ 

139 return cls 

140 

141 

142class InternalTraversal(Enum): 

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

144 

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

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

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

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

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

150 object defines ``_traverse_internals`` as :: 

151 

152 class Case(ColumnElement[_T]): 

153 _traverse_internals = [ 

154 ("value", InternalTraversal.dp_clauseelement), 

155 ("whens", InternalTraversal.dp_clauseelement_tuples), 

156 ("else_", InternalTraversal.dp_clauseelement), 

157 ] 

158 

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

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

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

162 of datastructure to which each attribute refers. 

163 

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

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

166 implemented: 

167 

168 * :meth:`.HasTraverseInternals.get_children` 

169 

170 * :meth:`.HasTraverseInternals._copy_internals` 

171 

172 * :meth:`.HasCacheKey._gen_cache_key` 

173 

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

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

176 are needed. 

177 

178 .. versionadded:: 1.4 

179 

180 """ 

181 

182 dp_has_cache_key = "HC" 

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

184 

185 dp_has_cache_key_list = "HL" 

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

187 

188 dp_clauseelement = "CE" 

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

190 

191 dp_fromclause_canonical_column_collection = "FC" 

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

193 ``columns`` attribute. 

194 

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

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

197 this means that the object being visited is a 

198 :class:`_expression.TableClause` 

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

200 

201 """ 

202 

203 dp_clauseelement_tuples = "CTS" 

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

205 objects. 

206 

207 """ 

208 

209 dp_clauseelement_list = "CL" 

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

211 

212 """ 

213 

214 dp_clauseelement_tuple = "CT" 

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

216 

217 """ 

218 

219 dp_executable_options = "EO" 

220 

221 dp_with_context_options = "WC" 

222 

223 dp_fromclause_ordered_set = "CO" 

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

225 

226 dp_string = "S" 

227 """Visit a plain string value. 

228 

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

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

231 

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

233 generation. 

234 

235 """ 

236 

237 dp_string_list = "SL" 

238 """Visit a list of strings.""" 

239 

240 dp_anon_name = "AN" 

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

242 

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

244 generation. 

245 

246 """ 

247 

248 dp_boolean = "B" 

249 """Visit a boolean value. 

250 

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

252 generation. 

253 

254 """ 

255 

256 dp_operator = "O" 

257 """Visit an operator. 

258 

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

260 module. 

261 

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

263 generation. 

264 

265 """ 

266 

267 dp_type = "T" 

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

269 

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

271 generation. 

272 

273 """ 

274 

275 dp_plain_dict = "PD" 

276 """Visit a dictionary with string keys. 

277 

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

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

280 significant for cache key generation. 

281 

282 """ 

283 

284 dp_dialect_options = "DO" 

285 """Visit a dialect options structure.""" 

286 

287 dp_string_clauseelement_dict = "CD" 

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

289 objects. 

290 

291 """ 

292 

293 dp_string_multi_dict = "MD" 

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

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

296 

297 """ 

298 

299 dp_annotations_key = "AK" 

300 """Visit the _annotations_cache_key element. 

301 

302 This is a dictionary of additional information about a ClauseElement 

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

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

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

306 this key. 

307 

308 """ 

309 

310 dp_plain_obj = "PO" 

311 """Visit a plain python object. 

312 

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

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

315 

316 """ 

317 

318 dp_named_ddl_element = "DD" 

319 """Visit a simple named DDL element. 

320 

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

322 

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

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

325 

326 """ 

327 

328 dp_prefix_sequence = "PS" 

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

330 or :class:`_expression.HasSuffixes`. 

331 

332 """ 

333 

334 dp_table_hint_list = "TH" 

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

336 object. 

337 

338 """ 

339 

340 dp_setup_join_tuple = "SJ" 

341 

342 dp_memoized_select_entities = "ME" 

343 

344 dp_statement_hint_list = "SH" 

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

346 :class:`_expression.Select` 

347 object. 

348 

349 """ 

350 

351 dp_unknown_structure = "UK" 

352 """Visit an unknown structure. 

353 

354 """ 

355 

356 dp_dml_ordered_values = "DML_OV" 

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

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

359 

360 dp_dml_values = "DML_V" 

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

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

363 

364 """ 

365 

366 dp_dml_multi_values = "DML_MV" 

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

368 :class:`_expression.Insert` object. 

369 

370 """ 

371 

372 dp_propagate_attrs = "PA" 

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

374 elements we care about right now.""" 

375 

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

377 caching applications. 

378 

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

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

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

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

383 

384 """ 

385 

386 dp_ignore = "IG" 

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

388 

389 This currently applies function call argument caching where some 

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

391 

392 """ 

393 

394 dp_inspectable = "IS" 

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

396 :class:`.HasCacheKey` object.""" 

397 

398 dp_multi = "M" 

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

400 plain hashable object.""" 

401 

402 dp_multi_list = "MT" 

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

404 may be a plain hashable object.""" 

405 

406 dp_has_cache_key_tuples = "HT" 

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

408 objects. 

409 

410 """ 

411 

412 dp_inspectable_list = "IL" 

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

414 HasCacheKey objects.""" 

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 def visit_bindparam(bind_param): 

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

931 

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

933 

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

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

936 

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

938 

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

940 empty in modern usage. 

941 

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

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

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

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

946 for that kind of object. 

947 

948 """ 

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

950 

951 

952@overload 

953def cloned_traverse( 

954 obj: Literal[None], 

955 opts: Mapping[str, Any], 

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

957) -> None: ... 

958 

959 

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

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

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

963# at least. 

964@overload 

965def cloned_traverse( 

966 obj: _ET, 

967 opts: Mapping[str, Any], 

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

969) -> _ET: ... 

970 

971 

972def cloned_traverse( 

973 obj: Optional[ExternallyTraversible], 

974 opts: Mapping[str, Any], 

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

976) -> Optional[ExternallyTraversible]: 

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

978 visitors for mutable objects. 

979 

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

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

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

983 

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

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

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

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

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

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

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

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

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

993 :class:`.ColumnClause`). 

994 

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

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

997 

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

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

1000 :meth:`_expression.ClauseElement.get_children` 

1001 function that is used to achieve 

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

1003 method. 

1004 For a :class:`_expression.ClauseElement` 

1005 structure to support cloning and replacement 

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

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

1008 

1009 .. seealso:: 

1010 

1011 :func:`.visitors.traverse` 

1012 

1013 :func:`.visitors.replacement_traverse` 

1014 

1015 """ 

1016 

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

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

1019 

1020 def deferred_copy_internals( 

1021 obj: ExternallyTraversible, 

1022 ) -> ExternallyTraversible: 

1023 return cloned_traverse(obj, opts, visitors) 

1024 

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

1026 if elem in stop_on: 

1027 return elem 

1028 else: 

1029 if id(elem) not in cloned: 

1030 if "replace" in kw: 

1031 newelem = cast( 

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

1033 ) 

1034 if newelem is not None: 

1035 cloned[id(elem)] = newelem 

1036 return newelem 

1037 

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

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

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

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

1042 # and return the new corresponding column. 

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

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

1045 

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

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

1048 # object. 

1049 if not elem._is_immutable: 

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

1051 if meth: 

1052 meth(newelem) 

1053 return cloned[id(elem)] 

1054 

1055 if obj is not None: 

1056 obj = clone( 

1057 obj, deferred_copy_internals=deferred_copy_internals, **opts 

1058 ) 

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

1060 return obj 

1061 

1062 

1063@overload 

1064def replacement_traverse( 

1065 obj: Literal[None], 

1066 opts: Mapping[str, Any], 

1067 replace: _TraverseTransformCallableType[Any], 

1068) -> None: ... 

1069 

1070 

1071@overload 

1072def replacement_traverse( 

1073 obj: _CE, 

1074 opts: Mapping[str, Any], 

1075 replace: _TraverseTransformCallableType[Any], 

1076) -> _CE: ... 

1077 

1078 

1079@overload 

1080def replacement_traverse( 

1081 obj: ExternallyTraversible, 

1082 opts: Mapping[str, Any], 

1083 replace: _TraverseTransformCallableType[Any], 

1084) -> ExternallyTraversible: ... 

1085 

1086 

1087def replacement_traverse( 

1088 obj: Optional[ExternallyTraversible], 

1089 opts: Mapping[str, Any], 

1090 replace: _TraverseTransformCallableType[Any], 

1091) -> Optional[ExternallyTraversible]: 

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

1093 replacement by a given replacement function. 

1094 

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

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

1097 elements are unconditionally passed into the given replace function. 

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

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

1100 is kept in place. 

1101 

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

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

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

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

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

1107 entirely different object, or do nothing. 

1108 

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

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

1111 as is a common use case within the ORM. 

1112 

1113 """ 

1114 

1115 cloned = {} 

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

1117 

1118 def deferred_copy_internals( 

1119 obj: ExternallyTraversible, 

1120 ) -> ExternallyTraversible: 

1121 return replacement_traverse(obj, opts, replace) 

1122 

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

1124 if ( 

1125 id(elem) in stop_on 

1126 or "no_replacement_traverse" in elem._annotations 

1127 ): 

1128 return elem 

1129 else: 

1130 newelem = replace(elem) 

1131 if newelem is not None: 

1132 stop_on.add(id(newelem)) 

1133 return newelem # type: ignore 

1134 else: 

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

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

1137 # vice versa 

1138 id_elem = id(elem) 

1139 if id_elem not in cloned: 

1140 if "replace" in kw: 

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

1142 if newelem is not None: 

1143 cloned[id_elem] = newelem 

1144 return newelem # type: ignore 

1145 

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

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

1148 return cloned[id_elem] # type: ignore 

1149 

1150 if obj is not None: 

1151 obj = clone( 

1152 obj, deferred_copy_internals=deferred_copy_internals, **opts 

1153 ) 

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

1155 return obj