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

240 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1# sql/visitors.py 

2# Copyright (C) 2005-2023 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 

10SQLAlchemy schema and expression constructs rely on a Python-centric 

11version of the classic "visitor" pattern as the primary way in which 

12they apply functionality. The most common use of this pattern 

13is statement compilation, where individual expression classes match 

14up to rendering methods that produce a string result. Beyond this, 

15the visitor system is also used to inspect expressions for various 

16information and patterns, as well as for the purposes of applying 

17transformations to expressions. 

18 

19Examples of how the visit system is used can be seen in the source code 

20of for example the ``sqlalchemy.sql.util`` and the ``sqlalchemy.sql.compiler`` 

21modules. Some background on clause adaption is also at 

22https://techspot.zzzeek.org/2008/01/23/expression-transformations/ . 

23 

24""" 

25 

26from collections import deque 

27import itertools 

28import operator 

29 

30from .. import exc 

31from .. import util 

32from ..util import langhelpers 

33from ..util import symbol 

34 

35__all__ = [ 

36 "iterate", 

37 "traverse_using", 

38 "traverse", 

39 "cloned_traverse", 

40 "replacement_traverse", 

41 "Traversible", 

42 "TraversibleType", 

43 "ExternalTraversal", 

44 "InternalTraversal", 

45] 

46 

47 

48def _generate_compiler_dispatch(cls): 

49 """Generate a _compiler_dispatch() external traversal on classes with a 

50 __visit_name__ attribute. 

51 

52 """ 

53 visit_name = cls.__visit_name__ 

54 

55 if "_compiler_dispatch" in cls.__dict__: 

56 # class has a fixed _compiler_dispatch() method. 

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

58 # sqlalchemy.ext.compiles overrides it. 

59 cls._original_compiler_dispatch = cls._compiler_dispatch 

60 return 

61 

62 if not isinstance(visit_name, util.compat.string_types): 

63 raise exc.InvalidRequestError( 

64 "__visit_name__ on class %s must be a string at the class level" 

65 % cls.__name__ 

66 ) 

67 

68 name = "visit_%s" % visit_name 

69 getter = operator.attrgetter(name) 

70 

71 def _compiler_dispatch(self, visitor, **kw): 

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

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

74 

75 """ 

76 try: 

77 meth = getter(visitor) 

78 except AttributeError as err: 

79 return visitor.visit_unsupported_compilation(self, err, **kw) 

80 

81 else: 

82 return meth(self, **kw) 

83 

84 cls._compiler_dispatch = ( 

85 cls._original_compiler_dispatch 

86 ) = _compiler_dispatch 

87 

88 

89class TraversibleType(type): 

90 """Metaclass which assigns dispatch attributes to various kinds of 

91 "visitable" classes. 

92 

93 Attributes include: 

94 

95 * The ``_compiler_dispatch`` method, corresponding to ``__visit_name__``. 

96 This is called "external traversal" because the caller of each visit() 

97 method is responsible for sub-traversing the inner elements of each 

98 object. This is appropriate for string compilers and other traversals 

99 that need to call upon the inner elements in a specific pattern. 

100 

101 * internal traversal collections ``_children_traversal``, 

102 ``_cache_key_traversal``, ``_copy_internals_traversal``, generated from 

103 an optional ``_traverse_internals`` collection of symbols which comes 

104 from the :class:`.InternalTraversal` list of symbols. This is called 

105 "internal traversal" MARKMARK 

106 

107 """ 

108 

109 def __init__(cls, clsname, bases, clsdict): 

110 if clsname != "Traversible": 

111 if "__visit_name__" in clsdict: 

112 _generate_compiler_dispatch(cls) 

113 

114 super(TraversibleType, cls).__init__(clsname, bases, clsdict) 

115 

116 

117class Traversible(util.with_metaclass(TraversibleType)): 

118 """Base class for visitable objects, applies the 

119 :class:`.visitors.TraversibleType` metaclass. 

120 

121 """ 

122 

123 def __class_getitem__(cls, key): 

124 # allow generic classes in py3.9+ 

125 return cls 

126 

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

128 def get_children(self, omit_attrs=(), **kw): 

129 r"""Return immediate child :class:`.visitors.Traversible` 

130 elements of this :class:`.visitors.Traversible`. 

131 

132 This is used for visit traversal. 

133 

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

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

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

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

138 clause-level). 

139 

140 """ 

141 

142 traversals = util.preloaded.sql_traversals 

143 

144 try: 

145 traverse_internals = self._traverse_internals 

146 except AttributeError: 

147 # user-defined classes may not have a _traverse_internals 

148 return [] 

149 

150 dispatch = traversals._get_children.run_generated_dispatch 

151 return itertools.chain.from_iterable( 

152 meth(obj, **kw) 

153 for attrname, obj, meth in dispatch( 

154 self, traverse_internals, "_generated_get_children_traversal" 

155 ) 

156 if attrname not in omit_attrs and obj is not None 

157 ) 

158 

159 

160class _InternalTraversalType(type): 

161 def __init__(cls, clsname, bases, clsdict): 

162 if cls.__name__ in ("InternalTraversal", "ExtendedInternalTraversal"): 

163 lookup = {} 

164 for key, sym in clsdict.items(): 

165 if key.startswith("dp_"): 

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

167 sym_name = sym.name 

168 assert sym_name not in lookup, sym_name 

169 lookup[sym] = lookup[sym_name] = visit_key 

170 if hasattr(cls, "_dispatch_lookup"): 

171 lookup.update(cls._dispatch_lookup) 

172 cls._dispatch_lookup = lookup 

173 

174 super(_InternalTraversalType, cls).__init__(clsname, bases, clsdict) 

175 

176 

177def _generate_dispatcher(visitor, internal_dispatch, method_name): 

178 names = [] 

179 for attrname, visit_sym in internal_dispatch: 

180 meth = visitor.dispatch(visit_sym) 

181 if meth: 

182 visit_name = ExtendedInternalTraversal._dispatch_lookup[visit_sym] 

183 names.append((attrname, visit_name)) 

184 

185 code = ( 

186 (" return [\n") 

187 + ( 

188 ", \n".join( 

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

190 % (attrname, attrname, visit_name) 

191 for attrname, visit_name in names 

192 ) 

193 ) 

194 + ("\n ]\n") 

195 ) 

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

197 # print(meth_text) 

198 return langhelpers._exec_code_in_env(meth_text, {}, method_name) 

199 

200 

201class InternalTraversal(util.with_metaclass(_InternalTraversalType, object)): 

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

203 

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

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

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

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

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

209 object defines ``_traverse_internals`` as :: 

210 

211 _traverse_internals = [ 

212 ("value", InternalTraversal.dp_clauseelement), 

213 ("whens", InternalTraversal.dp_clauseelement_tuples), 

214 ("else_", InternalTraversal.dp_clauseelement), 

215 ] 

216 

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

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

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

220 of datastructure referred towards. 

221 

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

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

224 implemented: 

225 

226 * :meth:`.Traversible.get_children` 

227 

228 * :meth:`.Traversible._copy_internals` 

229 

230 * :meth:`.Traversible._gen_cache_key` 

231 

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

233 :meth:`.Traversible._copy_internals` method, when special steps 

234 are needed. 

235 

236 .. versionadded:: 1.4 

237 

238 """ 

239 

240 def dispatch(self, visit_symbol): 

241 """Given a method from :class:`.InternalTraversal`, return the 

242 corresponding method on a subclass. 

243 

244 """ 

245 name = self._dispatch_lookup[visit_symbol] 

246 return getattr(self, name, None) 

247 

248 def run_generated_dispatch( 

249 self, target, internal_dispatch, generate_dispatcher_name 

250 ): 

251 try: 

252 dispatcher = target.__class__.__dict__[generate_dispatcher_name] 

253 except KeyError: 

254 # most of the dispatchers are generated up front 

255 # in sqlalchemy/sql/__init__.py -> 

256 # traversals.py-> _preconfigure_traversals(). 

257 # this block will generate any remaining dispatchers. 

258 dispatcher = self.generate_dispatch( 

259 target.__class__, internal_dispatch, generate_dispatcher_name 

260 ) 

261 return dispatcher(target, self) 

262 

263 def generate_dispatch( 

264 self, target_cls, internal_dispatch, generate_dispatcher_name 

265 ): 

266 dispatcher = _generate_dispatcher( 

267 self, internal_dispatch, generate_dispatcher_name 

268 ) 

269 # assert isinstance(target_cls, type) 

270 setattr(target_cls, generate_dispatcher_name, dispatcher) 

271 return dispatcher 

272 

273 dp_has_cache_key = symbol("HC") 

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

275 

276 dp_has_cache_key_list = symbol("HL") 

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

278 

279 dp_clauseelement = symbol("CE") 

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

281 

282 dp_fromclause_canonical_column_collection = symbol("FC") 

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

284 ``columns`` attribute. 

285 

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

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

288 this means that the object being visited is a 

289 :class:`_expression.TableClause` 

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

291 

292 """ 

293 

294 dp_clauseelement_tuples = symbol("CTS") 

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

296 objects. 

297 

298 """ 

299 

300 dp_clauseelement_list = symbol("CL") 

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

302 

303 """ 

304 

305 dp_clauseelement_tuple = symbol("CT") 

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

307 

308 """ 

309 

310 dp_executable_options = symbol("EO") 

311 

312 dp_with_context_options = symbol("WC") 

313 

314 dp_fromclause_ordered_set = symbol("CO") 

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

316 

317 dp_string = symbol("S") 

318 """Visit a plain string value. 

319 

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

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

322 

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

324 generation. 

325 

326 """ 

327 

328 dp_string_list = symbol("SL") 

329 """Visit a list of strings.""" 

330 

331 dp_anon_name = symbol("AN") 

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

333 

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

335 generation. 

336 

337 """ 

338 

339 dp_boolean = symbol("B") 

340 """Visit a boolean value. 

341 

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

343 generation. 

344 

345 """ 

346 

347 dp_operator = symbol("O") 

348 """Visit an operator. 

349 

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

351 module. 

352 

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

354 generation. 

355 

356 """ 

357 

358 dp_type = symbol("T") 

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

360 

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

362 generation. 

363 

364 """ 

365 

366 dp_plain_dict = symbol("PD") 

367 """Visit a dictionary with string keys. 

368 

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

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

371 significant for cache key generation. 

372 

373 """ 

374 

375 dp_dialect_options = symbol("DO") 

376 """Visit a dialect options structure.""" 

377 

378 dp_string_clauseelement_dict = symbol("CD") 

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

380 objects. 

381 

382 """ 

383 

384 dp_string_multi_dict = symbol("MD") 

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

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

387 

388 """ 

389 

390 dp_annotations_key = symbol("AK") 

391 """Visit the _annotations_cache_key element. 

392 

393 This is a dictionary of additional information about a ClauseElement 

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

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

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

397 this key. 

398 

399 """ 

400 

401 dp_plain_obj = symbol("PO") 

402 """Visit a plain python object. 

403 

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

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

406 

407 """ 

408 

409 dp_named_ddl_element = symbol("DD") 

410 """Visit a simple named DDL element. 

411 

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

413 

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

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

416 

417 """ 

418 

419 dp_prefix_sequence = symbol("PS") 

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

421 or :class:`_expression.HasSuffixes`. 

422 

423 """ 

424 

425 dp_table_hint_list = symbol("TH") 

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

427 object. 

428 

429 """ 

430 

431 dp_setup_join_tuple = symbol("SJ") 

432 

433 dp_memoized_select_entities = symbol("ME") 

434 

435 dp_statement_hint_list = symbol("SH") 

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

437 :class:`_expression.Select` 

438 object. 

439 

440 """ 

441 

442 dp_unknown_structure = symbol("UK") 

443 """Visit an unknown structure. 

444 

445 """ 

446 

447 dp_dml_ordered_values = symbol("DML_OV") 

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

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

450 

451 dp_dml_values = symbol("DML_V") 

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

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

454 

455 """ 

456 

457 dp_dml_multi_values = symbol("DML_MV") 

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

459 :class:`_expression.Insert` object. 

460 

461 """ 

462 

463 dp_propagate_attrs = symbol("PA") 

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

465 elements we care about right now.""" 

466 

467 

468class ExtendedInternalTraversal(InternalTraversal): 

469 """Defines additional symbols that are useful in caching applications. 

470 

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

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

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

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

475 

476 """ 

477 

478 dp_ignore = symbol("IG") 

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

480 

481 This currently applies function call argument caching where some 

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

483 

484 """ 

485 

486 dp_inspectable = symbol("IS") 

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

488 :class:`.HasCacheKey` object.""" 

489 

490 dp_multi = symbol("M") 

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

492 plain hashable object.""" 

493 

494 dp_multi_list = symbol("MT") 

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

496 may be a plain hashable object.""" 

497 

498 dp_has_cache_key_tuples = symbol("HT") 

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

500 objects. 

501 

502 """ 

503 

504 dp_inspectable_list = symbol("IL") 

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

506 HasCacheKey objects.""" 

507 

508 

509class ExternalTraversal(object): 

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

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

512 

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

514 preferred. 

515 

516 """ 

517 

518 __traverse_options__ = {} 

519 

520 def traverse_single(self, obj, **kw): 

521 for v in self.visitor_iterator: 

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

523 if meth: 

524 return meth(obj, **kw) 

525 

526 def iterate(self, obj): 

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

528 of all elements. 

529 

530 """ 

531 return iterate(obj, self.__traverse_options__) 

532 

533 def traverse(self, obj): 

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

535 

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

537 

538 @util.memoized_property 

539 def _visitor_dict(self): 

540 visitors = {} 

541 

542 for name in dir(self): 

543 if name.startswith("visit_"): 

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

545 return visitors 

546 

547 @property 

548 def visitor_iterator(self): 

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

550 

551 v = self 

552 while v: 

553 yield v 

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

555 

556 def chain(self, visitor): 

557 """'Chain' an additional ClauseVisitor onto this ClauseVisitor. 

558 

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

560 

561 """ 

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

563 tail._next = visitor 

564 return self 

565 

566 

567class CloningExternalTraversal(ExternalTraversal): 

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

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

570 

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

572 preferred. 

573 

574 

575 """ 

576 

577 def copy_and_process(self, list_): 

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

579 the new list. 

580 

581 """ 

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

583 

584 def traverse(self, obj): 

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

586 

587 return cloned_traverse( 

588 obj, self.__traverse_options__, self._visitor_dict 

589 ) 

590 

591 

592class ReplacingExternalTraversal(CloningExternalTraversal): 

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

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

595 

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

597 usually preferred. 

598 

599 """ 

600 

601 def replace(self, elem): 

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

603 

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

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

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

607 """ 

608 return None 

609 

610 def traverse(self, obj): 

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

612 

613 def replace(elem): 

614 for v in self.visitor_iterator: 

615 e = v.replace(elem) 

616 if e is not None: 

617 return e 

618 

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

620 

621 

622# backwards compatibility 

623Visitable = Traversible 

624VisitableType = TraversibleType 

625ClauseVisitor = ExternalTraversal 

626CloningVisitor = CloningExternalTraversal 

627ReplacingCloningVisitor = ReplacingExternalTraversal 

628 

629 

630def iterate(obj, opts=util.immutabledict()): 

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

632 

633 Traversal is configured to be breadth-first. 

634 

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

636 function is the 

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

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

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

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

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

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

643 member variables. 

644 

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

646 

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

648 empty in modern usage. 

649 

650 """ 

651 yield obj 

652 children = obj.get_children(**opts) 

653 

654 if not children: 

655 return 

656 

657 stack = deque([children]) 

658 while stack: 

659 t_iterator = stack.popleft() 

660 for t in t_iterator: 

661 yield t 

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

663 

664 

665def traverse_using(iterator, obj, visitors): 

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

667 objects. 

668 

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

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

671 

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

673 :class:`_expression.ClauseElement` 

674 structures; the iterator is assumed to be the 

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

676 

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

678 that was used as the target of the 

679 :func:`.iterate` function. 

680 

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

682 for details on this dictionary. 

683 

684 .. seealso:: 

685 

686 :func:`.traverse` 

687 

688 

689 """ 

690 for target in iterator: 

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

692 if meth: 

693 meth(target) 

694 return obj 

695 

696 

697def traverse(obj, opts, visitors): 

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

699 iterator. 

700 

701 e.g.:: 

702 

703 from sqlalchemy.sql import visitors 

704 

705 stmt = select(some_table).where(some_table.c.foo == 'bar') 

706 

707 def visit_bindparam(bind_param): 

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

709 

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

711 

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

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

714 

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

716 

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

718 empty in modern usage. 

719 

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

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

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

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

724 for that kind of object. 

725 

726 """ 

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

728 

729 

730def cloned_traverse(obj, opts, visitors): 

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

732 visitors. 

733 

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

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

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

737 

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

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

740 :meth:`_expression.ClauseElement.get_children` 

741 function that is used to achieve 

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

743 method. 

744 For a :class:`_expression.ClauseElement` 

745 structure to support cloning and replacement 

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

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

748 

749 .. seealso:: 

750 

751 :func:`.visitors.traverse` 

752 

753 :func:`.visitors.replacement_traverse` 

754 

755 """ 

756 

757 cloned = {} 

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

759 

760 def deferred_copy_internals(obj): 

761 return cloned_traverse(obj, opts, visitors) 

762 

763 def clone(elem, **kw): 

764 if elem in stop_on: 

765 return elem 

766 else: 

767 if id(elem) not in cloned: 

768 

769 if "replace" in kw: 

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

771 if newelem is not None: 

772 cloned[id(elem)] = newelem 

773 return newelem 

774 

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

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

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

778 if meth: 

779 meth(newelem) 

780 return cloned[id(elem)] 

781 

782 if obj is not None: 

783 obj = clone( 

784 obj, deferred_copy_internals=deferred_copy_internals, **opts 

785 ) 

786 clone = None # remove gc cycles 

787 return obj 

788 

789 

790def replacement_traverse(obj, opts, replace): 

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

792 replacement by a given replacement function. 

793 

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

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

796 elements are unconditionally passed into the given replace function. 

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

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

799 is kept in place. 

800 

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

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

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

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

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

806 entirely different object, or do nothing. 

807 

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

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

810 as is a common use case within the ORM. 

811 

812 """ 

813 

814 cloned = {} 

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

816 

817 def deferred_copy_internals(obj): 

818 return replacement_traverse(obj, opts, replace) 

819 

820 def clone(elem, **kw): 

821 if ( 

822 id(elem) in stop_on 

823 or "no_replacement_traverse" in elem._annotations 

824 ): 

825 return elem 

826 else: 

827 newelem = replace(elem) 

828 if newelem is not None: 

829 stop_on.add(id(newelem)) 

830 return newelem 

831 else: 

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

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

834 # vice versa 

835 id_elem = id(elem) 

836 if id_elem not in cloned: 

837 if "replace" in kw: 

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

839 if newelem is not None: 

840 cloned[id_elem] = newelem 

841 return newelem 

842 

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

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

845 return cloned[id_elem] 

846 

847 if obj is not None: 

848 obj = clone( 

849 obj, deferred_copy_internals=deferred_copy_internals, **opts 

850 ) 

851 clone = None # remove gc cycles 

852 return obj