Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/interfaces.py: 64%

231 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1# orm/interfaces.py 

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

9 

10Contains various base classes used throughout the ORM. 

11 

12Defines some key base classes prominent within the internals. 

13 

14This module and the classes within are mostly private, though some attributes 

15are exposed when inspecting mappings. 

16 

17""" 

18 

19from __future__ import absolute_import 

20 

21import collections 

22 

23from . import exc as orm_exc 

24from . import path_registry 

25from .base import _MappedAttribute # noqa 

26from .base import EXT_CONTINUE 

27from .base import EXT_SKIP 

28from .base import EXT_STOP 

29from .base import InspectionAttr # noqa 

30from .base import InspectionAttrInfo # noqa 

31from .base import MANYTOMANY 

32from .base import MANYTOONE 

33from .base import NOT_EXTENSION 

34from .base import ONETOMANY 

35from .. import inspect 

36from .. import inspection 

37from .. import util 

38from ..sql import operators 

39from ..sql import roles 

40from ..sql import visitors 

41from ..sql.base import ExecutableOption 

42from ..sql.traversals import HasCacheKey 

43 

44 

45__all__ = ( 

46 "EXT_CONTINUE", 

47 "EXT_STOP", 

48 "EXT_SKIP", 

49 "ONETOMANY", 

50 "MANYTOMANY", 

51 "MANYTOONE", 

52 "NOT_EXTENSION", 

53 "LoaderStrategy", 

54 "MapperOption", 

55 "LoaderOption", 

56 "MapperProperty", 

57 "PropComparator", 

58 "StrategizedProperty", 

59) 

60 

61 

62class ORMStatementRole(roles.StatementRole): 

63 _role_name = ( 

64 "Executable SQL or text() construct, including ORM " "aware objects" 

65 ) 

66 

67 

68class ORMColumnsClauseRole(roles.ColumnsClauseRole): 

69 _role_name = "ORM mapped entity, aliased entity, or Column expression" 

70 

71 

72class ORMEntityColumnsClauseRole(ORMColumnsClauseRole): 

73 _role_name = "ORM mapped or aliased entity" 

74 

75 

76class ORMFromClauseRole(roles.StrictFromClauseRole): 

77 _role_name = "ORM mapped entity, aliased entity, or FROM expression" 

78 

79 

80@inspection._self_inspects 

81class MapperProperty( 

82 HasCacheKey, _MappedAttribute, InspectionAttr, util.MemoizedSlots 

83): 

84 """Represent a particular class attribute mapped by :class:`_orm.Mapper`. 

85 

86 The most common occurrences of :class:`.MapperProperty` are the 

87 mapped :class:`_schema.Column`, which is represented in a mapping as 

88 an instance of :class:`.ColumnProperty`, 

89 and a reference to another class produced by :func:`_orm.relationship`, 

90 represented in the mapping as an instance of 

91 :class:`.RelationshipProperty`. 

92 

93 """ 

94 

95 __slots__ = ( 

96 "_configure_started", 

97 "_configure_finished", 

98 "parent", 

99 "key", 

100 "info", 

101 ) 

102 

103 _cache_key_traversal = [ 

104 ("parent", visitors.ExtendedInternalTraversal.dp_has_cache_key), 

105 ("key", visitors.ExtendedInternalTraversal.dp_string), 

106 ] 

107 

108 cascade = frozenset() 

109 """The set of 'cascade' attribute names. 

110 

111 This collection is checked before the 'cascade_iterator' method is called. 

112 

113 The collection typically only applies to a RelationshipProperty. 

114 

115 """ 

116 

117 is_property = True 

118 """Part of the InspectionAttr interface; states this object is a 

119 mapper property. 

120 

121 """ 

122 

123 @property 

124 def _links_to_entity(self): 

125 """True if this MapperProperty refers to a mapped entity. 

126 

127 Should only be True for RelationshipProperty, False for all others. 

128 

129 """ 

130 raise NotImplementedError() 

131 

132 def _memoized_attr_info(self): 

133 """Info dictionary associated with the object, allowing user-defined 

134 data to be associated with this :class:`.InspectionAttr`. 

135 

136 The dictionary is generated when first accessed. Alternatively, 

137 it can be specified as a constructor argument to the 

138 :func:`.column_property`, :func:`_orm.relationship`, or 

139 :func:`.composite` 

140 functions. 

141 

142 .. versionchanged:: 1.0.0 :attr:`.MapperProperty.info` is also 

143 available on extension types via the 

144 :attr:`.InspectionAttrInfo.info` attribute, so that it can apply 

145 to a wider variety of ORM and extension constructs. 

146 

147 .. seealso:: 

148 

149 :attr:`.QueryableAttribute.info` 

150 

151 :attr:`.SchemaItem.info` 

152 

153 """ 

154 return {} 

155 

156 def setup(self, context, query_entity, path, adapter, **kwargs): 

157 """Called by Query for the purposes of constructing a SQL statement. 

158 

159 Each MapperProperty associated with the target mapper processes the 

160 statement referenced by the query context, adding columns and/or 

161 criterion as appropriate. 

162 

163 """ 

164 

165 def create_row_processor( 

166 self, context, query_entity, path, mapper, result, adapter, populators 

167 ): 

168 """Produce row processing functions and append to the given 

169 set of populators lists. 

170 

171 """ 

172 

173 def cascade_iterator( 

174 self, type_, state, dict_, visited_states, halt_on=None 

175 ): 

176 """Iterate through instances related to the given instance for 

177 a particular 'cascade', starting with this MapperProperty. 

178 

179 Return an iterator3-tuples (instance, mapper, state). 

180 

181 Note that the 'cascade' collection on this MapperProperty is 

182 checked first for the given type before cascade_iterator is called. 

183 

184 This method typically only applies to RelationshipProperty. 

185 

186 """ 

187 

188 return iter(()) 

189 

190 def set_parent(self, parent, init): 

191 """Set the parent mapper that references this MapperProperty. 

192 

193 This method is overridden by some subclasses to perform extra 

194 setup when the mapper is first known. 

195 

196 """ 

197 self.parent = parent 

198 

199 def instrument_class(self, mapper): 

200 """Hook called by the Mapper to the property to initiate 

201 instrumentation of the class attribute managed by this 

202 MapperProperty. 

203 

204 The MapperProperty here will typically call out to the 

205 attributes module to set up an InstrumentedAttribute. 

206 

207 This step is the first of two steps to set up an InstrumentedAttribute, 

208 and is called early in the mapper setup process. 

209 

210 The second step is typically the init_class_attribute step, 

211 called from StrategizedProperty via the post_instrument_class() 

212 hook. This step assigns additional state to the InstrumentedAttribute 

213 (specifically the "impl") which has been determined after the 

214 MapperProperty has determined what kind of persistence 

215 management it needs to do (e.g. scalar, object, collection, etc). 

216 

217 """ 

218 

219 def __init__(self): 

220 self._configure_started = False 

221 self._configure_finished = False 

222 

223 def init(self): 

224 """Called after all mappers are created to assemble 

225 relationships between mappers and perform other post-mapper-creation 

226 initialization steps. 

227 

228 

229 """ 

230 self._configure_started = True 

231 self.do_init() 

232 self._configure_finished = True 

233 

234 @property 

235 def class_attribute(self): 

236 """Return the class-bound descriptor corresponding to this 

237 :class:`.MapperProperty`. 

238 

239 This is basically a ``getattr()`` call:: 

240 

241 return getattr(self.parent.class_, self.key) 

242 

243 I.e. if this :class:`.MapperProperty` were named ``addresses``, 

244 and the class to which it is mapped is ``User``, this sequence 

245 is possible:: 

246 

247 >>> from sqlalchemy import inspect 

248 >>> mapper = inspect(User) 

249 >>> addresses_property = mapper.attrs.addresses 

250 >>> addresses_property.class_attribute is User.addresses 

251 True 

252 >>> User.addresses.property is addresses_property 

253 True 

254 

255 

256 """ 

257 

258 return getattr(self.parent.class_, self.key) 

259 

260 def do_init(self): 

261 """Perform subclass-specific initialization post-mapper-creation 

262 steps. 

263 

264 This is a template method called by the ``MapperProperty`` 

265 object's init() method. 

266 

267 """ 

268 

269 def post_instrument_class(self, mapper): 

270 """Perform instrumentation adjustments that need to occur 

271 after init() has completed. 

272 

273 The given Mapper is the Mapper invoking the operation, which 

274 may not be the same Mapper as self.parent in an inheritance 

275 scenario; however, Mapper will always at least be a sub-mapper of 

276 self.parent. 

277 

278 This method is typically used by StrategizedProperty, which delegates 

279 it to LoaderStrategy.init_class_attribute() to perform final setup 

280 on the class-bound InstrumentedAttribute. 

281 

282 """ 

283 

284 def merge( 

285 self, 

286 session, 

287 source_state, 

288 source_dict, 

289 dest_state, 

290 dest_dict, 

291 load, 

292 _recursive, 

293 _resolve_conflict_map, 

294 ): 

295 """Merge the attribute represented by this ``MapperProperty`` 

296 from source to destination object. 

297 

298 """ 

299 

300 def __repr__(self): 

301 return "<%s at 0x%x; %s>" % ( 

302 self.__class__.__name__, 

303 id(self), 

304 getattr(self, "key", "no key"), 

305 ) 

306 

307 

308@inspection._self_inspects 

309class PropComparator(operators.ColumnOperators): 

310 r"""Defines SQL operators for :class:`.MapperProperty` objects. 

311 

312 SQLAlchemy allows for operators to 

313 be redefined at both the Core and ORM level. :class:`.PropComparator` 

314 is the base class of operator redefinition for ORM-level operations, 

315 including those of :class:`.ColumnProperty`, 

316 :class:`.RelationshipProperty`, and :class:`.CompositeProperty`. 

317 

318 .. note:: With the advent of Hybrid properties introduced in SQLAlchemy 

319 0.7, as well as Core-level operator redefinition in 

320 SQLAlchemy 0.8, the use case for user-defined :class:`.PropComparator` 

321 instances is extremely rare. See :ref:`hybrids_toplevel` as well 

322 as :ref:`types_operators`. 

323 

324 User-defined subclasses of :class:`.PropComparator` may be created. The 

325 built-in Python comparison and math operator methods, such as 

326 :meth:`.operators.ColumnOperators.__eq__`, 

327 :meth:`.operators.ColumnOperators.__lt__`, and 

328 :meth:`.operators.ColumnOperators.__add__`, can be overridden to provide 

329 new operator behavior. The custom :class:`.PropComparator` is passed to 

330 the :class:`.MapperProperty` instance via the ``comparator_factory`` 

331 argument. In each case, 

332 the appropriate subclass of :class:`.PropComparator` should be used:: 

333 

334 # definition of custom PropComparator subclasses 

335 

336 from sqlalchemy.orm.properties import \ 

337 ColumnProperty,\ 

338 CompositeProperty,\ 

339 RelationshipProperty 

340 

341 class MyColumnComparator(ColumnProperty.Comparator): 

342 def __eq__(self, other): 

343 return self.__clause_element__() == other 

344 

345 class MyRelationshipComparator(RelationshipProperty.Comparator): 

346 def any(self, expression): 

347 "define the 'any' operation" 

348 # ... 

349 

350 class MyCompositeComparator(CompositeProperty.Comparator): 

351 def __gt__(self, other): 

352 "redefine the 'greater than' operation" 

353 

354 return sql.and_(*[a>b for a, b in 

355 zip(self.__clause_element__().clauses, 

356 other.__composite_values__())]) 

357 

358 

359 # application of custom PropComparator subclasses 

360 

361 from sqlalchemy.orm import column_property, relationship, composite 

362 from sqlalchemy import Column, String 

363 

364 class SomeMappedClass(Base): 

365 some_column = column_property(Column("some_column", String), 

366 comparator_factory=MyColumnComparator) 

367 

368 some_relationship = relationship(SomeOtherClass, 

369 comparator_factory=MyRelationshipComparator) 

370 

371 some_composite = composite( 

372 Column("a", String), Column("b", String), 

373 comparator_factory=MyCompositeComparator 

374 ) 

375 

376 Note that for column-level operator redefinition, it's usually 

377 simpler to define the operators at the Core level, using the 

378 :attr:`.TypeEngine.comparator_factory` attribute. See 

379 :ref:`types_operators` for more detail. 

380 

381 .. seealso:: 

382 

383 :class:`.ColumnProperty.Comparator` 

384 

385 :class:`.RelationshipProperty.Comparator` 

386 

387 :class:`.CompositeProperty.Comparator` 

388 

389 :class:`.ColumnOperators` 

390 

391 :ref:`types_operators` 

392 

393 :attr:`.TypeEngine.comparator_factory` 

394 

395 """ 

396 

397 __slots__ = "prop", "property", "_parententity", "_adapt_to_entity" 

398 

399 __visit_name__ = "orm_prop_comparator" 

400 

401 def __init__( 

402 self, 

403 prop, 

404 parentmapper, 

405 adapt_to_entity=None, 

406 ): 

407 self.prop = self.property = prop 

408 self._parententity = adapt_to_entity or parentmapper 

409 self._adapt_to_entity = adapt_to_entity 

410 

411 def __clause_element__(self): 

412 raise NotImplementedError("%r" % self) 

413 

414 def _bulk_update_tuples(self, value): 

415 """Receive a SQL expression that represents a value in the SET 

416 clause of an UPDATE statement. 

417 

418 Return a tuple that can be passed to a :class:`_expression.Update` 

419 construct. 

420 

421 """ 

422 

423 return [(self.__clause_element__(), value)] 

424 

425 def adapt_to_entity(self, adapt_to_entity): 

426 """Return a copy of this PropComparator which will use the given 

427 :class:`.AliasedInsp` to produce corresponding expressions. 

428 """ 

429 return self.__class__(self.prop, self._parententity, adapt_to_entity) 

430 

431 @property 

432 def _parentmapper(self): 

433 """legacy; this is renamed to _parententity to be 

434 compatible with QueryableAttribute.""" 

435 return inspect(self._parententity).mapper 

436 

437 @property 

438 def _propagate_attrs(self): 

439 # this suits the case in coercions where we don't actually 

440 # call ``__clause_element__()`` but still need to get 

441 # resolved._propagate_attrs. See #6558. 

442 return util.immutabledict( 

443 { 

444 "compile_state_plugin": "orm", 

445 "plugin_subject": self._parentmapper, 

446 } 

447 ) 

448 

449 @property 

450 def adapter(self): 

451 """Produce a callable that adapts column expressions 

452 to suit an aliased version of this comparator. 

453 

454 """ 

455 if self._adapt_to_entity is None: 

456 return None 

457 else: 

458 return self._adapt_to_entity._adapt_element 

459 

460 @property 

461 def info(self): 

462 return self.property.info 

463 

464 @staticmethod 

465 def any_op(a, b, **kwargs): 

466 return a.any(b, **kwargs) 

467 

468 @staticmethod 

469 def has_op(a, b, **kwargs): 

470 return a.has(b, **kwargs) 

471 

472 @staticmethod 

473 def of_type_op(a, class_): 

474 return a.of_type(class_) 

475 

476 def of_type(self, class_): 

477 r"""Redefine this object in terms of a polymorphic subclass, 

478 :func:`_orm.with_polymorphic` construct, or :func:`_orm.aliased` 

479 construct. 

480 

481 Returns a new PropComparator from which further criterion can be 

482 evaluated. 

483 

484 e.g.:: 

485 

486 query.join(Company.employees.of_type(Engineer)).\ 

487 filter(Engineer.name=='foo') 

488 

489 :param \class_: a class or mapper indicating that criterion will be 

490 against this specific subclass. 

491 

492 .. seealso:: 

493 

494 :ref:`queryguide_join_onclause` - in the :ref:`queryguide_toplevel` 

495 

496 :ref:`inheritance_of_type` 

497 

498 """ 

499 

500 return self.operate(PropComparator.of_type_op, class_) 

501 

502 def and_(self, *criteria): 

503 """Add additional criteria to the ON clause that's represented by this 

504 relationship attribute. 

505 

506 E.g.:: 

507 

508 

509 stmt = select(User).join( 

510 User.addresses.and_(Address.email_address != 'foo') 

511 ) 

512 

513 stmt = select(User).options( 

514 joinedload(User.addresses.and_(Address.email_address != 'foo')) 

515 ) 

516 

517 .. versionadded:: 1.4 

518 

519 .. seealso:: 

520 

521 :ref:`orm_queryguide_join_on_augmented` 

522 

523 :ref:`loader_option_criteria` 

524 

525 :func:`.with_loader_criteria` 

526 

527 """ 

528 return self.operate(operators.and_, *criteria) 

529 

530 def any(self, criterion=None, **kwargs): 

531 r"""Return true if this collection contains any member that meets the 

532 given criterion. 

533 

534 The usual implementation of ``any()`` is 

535 :meth:`.RelationshipProperty.Comparator.any`. 

536 

537 :param criterion: an optional ClauseElement formulated against the 

538 member class' table or attributes. 

539 

540 :param \**kwargs: key/value pairs corresponding to member class 

541 attribute names which will be compared via equality to the 

542 corresponding values. 

543 

544 """ 

545 

546 return self.operate(PropComparator.any_op, criterion, **kwargs) 

547 

548 def has(self, criterion=None, **kwargs): 

549 r"""Return true if this element references a member which meets the 

550 given criterion. 

551 

552 The usual implementation of ``has()`` is 

553 :meth:`.RelationshipProperty.Comparator.has`. 

554 

555 :param criterion: an optional ClauseElement formulated against the 

556 member class' table or attributes. 

557 

558 :param \**kwargs: key/value pairs corresponding to member class 

559 attribute names which will be compared via equality to the 

560 corresponding values. 

561 

562 """ 

563 

564 return self.operate(PropComparator.has_op, criterion, **kwargs) 

565 

566 

567class StrategizedProperty(MapperProperty): 

568 """A MapperProperty which uses selectable strategies to affect 

569 loading behavior. 

570 

571 There is a single strategy selected by default. Alternate 

572 strategies can be selected at Query time through the usage of 

573 ``StrategizedOption`` objects via the Query.options() method. 

574 

575 The mechanics of StrategizedProperty are used for every Query 

576 invocation for every mapped attribute participating in that Query, 

577 to determine first how the attribute will be rendered in SQL 

578 and secondly how the attribute will retrieve a value from a result 

579 row and apply it to a mapped object. The routines here are very 

580 performance-critical. 

581 

582 """ 

583 

584 __slots__ = ( 

585 "_strategies", 

586 "strategy", 

587 "_wildcard_token", 

588 "_default_path_loader_key", 

589 ) 

590 inherit_cache = True 

591 strategy_wildcard_key = None 

592 

593 def _memoized_attr__wildcard_token(self): 

594 return ( 

595 "%s:%s" 

596 % (self.strategy_wildcard_key, path_registry._WILDCARD_TOKEN), 

597 ) 

598 

599 def _memoized_attr__default_path_loader_key(self): 

600 return ( 

601 "loader", 

602 ( 

603 "%s:%s" 

604 % (self.strategy_wildcard_key, path_registry._DEFAULT_TOKEN), 

605 ), 

606 ) 

607 

608 def _get_context_loader(self, context, path): 

609 load = None 

610 

611 search_path = path[self] 

612 

613 # search among: exact match, "attr.*", "default" strategy 

614 # if any. 

615 for path_key in ( 

616 search_path._loader_key, 

617 search_path._wildcard_path_loader_key, 

618 search_path._default_path_loader_key, 

619 ): 

620 if path_key in context.attributes: 

621 load = context.attributes[path_key] 

622 break 

623 

624 return load 

625 

626 def _get_strategy(self, key): 

627 try: 

628 return self._strategies[key] 

629 except KeyError: 

630 pass 

631 

632 # run outside to prevent transfer of exception context 

633 cls = self._strategy_lookup(self, *key) 

634 # this previously was setting self._strategies[cls], that's 

635 # a bad idea; should use strategy key at all times because every 

636 # strategy has multiple keys at this point 

637 self._strategies[key] = strategy = cls(self, key) 

638 return strategy 

639 

640 def setup(self, context, query_entity, path, adapter, **kwargs): 

641 loader = self._get_context_loader(context, path) 

642 if loader and loader.strategy: 

643 strat = self._get_strategy(loader.strategy) 

644 else: 

645 strat = self.strategy 

646 strat.setup_query( 

647 context, query_entity, path, loader, adapter, **kwargs 

648 ) 

649 

650 def create_row_processor( 

651 self, context, query_entity, path, mapper, result, adapter, populators 

652 ): 

653 loader = self._get_context_loader(context, path) 

654 if loader and loader.strategy: 

655 strat = self._get_strategy(loader.strategy) 

656 else: 

657 strat = self.strategy 

658 strat.create_row_processor( 

659 context, 

660 query_entity, 

661 path, 

662 loader, 

663 mapper, 

664 result, 

665 adapter, 

666 populators, 

667 ) 

668 

669 def do_init(self): 

670 self._strategies = {} 

671 self.strategy = self._get_strategy(self.strategy_key) 

672 

673 def post_instrument_class(self, mapper): 

674 if ( 

675 not self.parent.non_primary 

676 and not mapper.class_manager._attr_has_impl(self.key) 

677 ): 

678 self.strategy.init_class_attribute(mapper) 

679 

680 _all_strategies = collections.defaultdict(dict) 

681 

682 @classmethod 

683 def strategy_for(cls, **kw): 

684 def decorate(dec_cls): 

685 # ensure each subclass of the strategy has its 

686 # own _strategy_keys collection 

687 if "_strategy_keys" not in dec_cls.__dict__: 

688 dec_cls._strategy_keys = [] 

689 key = tuple(sorted(kw.items())) 

690 cls._all_strategies[cls][key] = dec_cls 

691 dec_cls._strategy_keys.append(key) 

692 return dec_cls 

693 

694 return decorate 

695 

696 @classmethod 

697 def _strategy_lookup(cls, requesting_property, *key): 

698 requesting_property.parent._with_polymorphic_mappers 

699 

700 for prop_cls in cls.__mro__: 

701 if prop_cls in cls._all_strategies: 

702 strategies = cls._all_strategies[prop_cls] 

703 try: 

704 return strategies[key] 

705 except KeyError: 

706 pass 

707 

708 for property_type, strats in cls._all_strategies.items(): 

709 if key in strats: 

710 intended_property_type = property_type 

711 actual_strategy = strats[key] 

712 break 

713 else: 

714 intended_property_type = None 

715 actual_strategy = None 

716 

717 raise orm_exc.LoaderStrategyException( 

718 cls, 

719 requesting_property, 

720 intended_property_type, 

721 actual_strategy, 

722 key, 

723 ) 

724 

725 

726class ORMOption(ExecutableOption): 

727 """Base class for option objects that are passed to ORM queries. 

728 

729 These options may be consumed by :meth:`.Query.options`, 

730 :meth:`.Select.options`, or in a more general sense by any 

731 :meth:`.Executable.options` method. They are interpreted at 

732 statement compile time or execution time in modern use. The 

733 deprecated :class:`.MapperOption` is consumed at ORM query construction 

734 time. 

735 

736 .. versionadded:: 1.4 

737 

738 """ 

739 

740 __slots__ = () 

741 

742 _is_legacy_option = False 

743 

744 propagate_to_loaders = False 

745 """if True, indicate this option should be carried along 

746 to "secondary" SELECT statements that occur for relationship 

747 lazy loaders as well as attribute load / refresh operations. 

748 

749 """ 

750 

751 _is_compile_state = False 

752 

753 _is_criteria_option = False 

754 

755 _is_strategy_option = False 

756 

757 def _adapt_cached_option_to_uncached_option(self, context, uncached_opt): 

758 """given "self" which is an option from a cached query, as well as the 

759 corresponding option from the uncached version of the same query, 

760 return the option we should use in a new query, in the context of a 

761 loader strategy being asked to load related rows on behalf of that 

762 cached query, which is assumed to be building a new query based on 

763 entities passed to us from the cached query. 

764 

765 Currently this routine chooses between "self" and "uncached" without 

766 manufacturing anything new. If the option is itself a loader strategy 

767 option which has a path, that path needs to match to the entities being 

768 passed to us by the cached query, so the :class:`_orm.Load` subclass 

769 overrides this to return "self". For all other options, we return the 

770 uncached form which may have changing state, such as a 

771 with_loader_criteria() option which will very often have new state. 

772 

773 This routine could in the future involve 

774 generating a new option based on both inputs if use cases arise, 

775 such as if with_loader_criteria() needed to match up to 

776 ``AliasedClass`` instances given in the parent query. 

777 

778 However, longer term it might be better to restructure things such that 

779 ``AliasedClass`` entities are always matched up on their cache key, 

780 instead of identity, in things like paths and such, so that this whole 

781 issue of "the uncached option does not match the entities" goes away. 

782 However this would make ``PathRegistry`` more complicated and difficult 

783 to debug as well as potentially less performant in that it would be 

784 hashing enormous cache keys rather than a simple AliasedInsp. UNLESS, 

785 we could get cache keys overall to be reliably hashed into something 

786 like an md5 key. 

787 

788 .. versionadded:: 1.4.41 

789 

790 

791 """ 

792 if uncached_opt is not None: 

793 return uncached_opt 

794 else: 

795 return self 

796 

797 

798class CompileStateOption(HasCacheKey, ORMOption): 

799 """base for :class:`.ORMOption` classes that affect the compilation of 

800 a SQL query and therefore need to be part of the cache key. 

801 

802 .. note:: :class:`.CompileStateOption` is generally non-public and 

803 should not be used as a base class for user-defined options; instead, 

804 use :class:`.UserDefinedOption`, which is easier to use as it does not 

805 interact with ORM compilation internals or caching. 

806 

807 :class:`.CompileStateOption` defines an internal attribute 

808 ``_is_compile_state=True`` which has the effect of the ORM compilation 

809 routines for SELECT and other statements will call upon these options when 

810 a SQL string is being compiled. As such, these classes implement 

811 :class:`.HasCacheKey` and need to provide robust ``_cache_key_traversal`` 

812 structures. 

813 

814 The :class:`.CompileStateOption` class is used to implement the ORM 

815 :class:`.LoaderOption` and :class:`.CriteriaOption` classes. 

816 

817 .. versionadded:: 1.4.28 

818 

819 

820 """ 

821 

822 _is_compile_state = True 

823 

824 def process_compile_state(self, compile_state): 

825 """Apply a modification to a given :class:`.CompileState`.""" 

826 

827 def process_compile_state_replaced_entities( 

828 self, compile_state, mapper_entities 

829 ): 

830 """Apply a modification to a given :class:`.CompileState`, 

831 given entities that were replaced by with_only_columns() or 

832 with_entities(). 

833 

834 .. versionadded:: 1.4.19 

835 

836 """ 

837 

838 

839class LoaderOption(CompileStateOption): 

840 """Describe a loader modification to an ORM statement at compilation time. 

841 

842 .. versionadded:: 1.4 

843 

844 """ 

845 

846 def process_compile_state_replaced_entities( 

847 self, compile_state, mapper_entities 

848 ): 

849 """Apply a modification to a given :class:`.CompileState`, 

850 given entities that were replaced by with_only_columns() or 

851 with_entities(). 

852 

853 .. versionadded:: 1.4.19 

854 

855 """ 

856 self.process_compile_state(compile_state) 

857 

858 def process_compile_state(self, compile_state): 

859 """Apply a modification to a given :class:`.CompileState`.""" 

860 

861 

862class CriteriaOption(CompileStateOption): 

863 """Describe a WHERE criteria modification to an ORM statement at 

864 compilation time. 

865 

866 .. versionadded:: 1.4 

867 

868 """ 

869 

870 _is_criteria_option = True 

871 

872 def process_compile_state(self, compile_state): 

873 """Apply a modification to a given :class:`.CompileState`.""" 

874 

875 def get_global_criteria(self, attributes): 

876 """update additional entity criteria options in the given 

877 attributes dictionary. 

878 

879 """ 

880 

881 

882class UserDefinedOption(ORMOption): 

883 """Base class for a user-defined option that can be consumed from the 

884 :meth:`.SessionEvents.do_orm_execute` event hook. 

885 

886 """ 

887 

888 _is_legacy_option = False 

889 

890 propagate_to_loaders = False 

891 """if True, indicate this option should be carried along 

892 to "secondary" Query objects produced during lazy loads 

893 or refresh operations. 

894 

895 """ 

896 

897 def __init__(self, payload=None): 

898 self.payload = payload 

899 

900 

901@util.deprecated_cls( 

902 "1.4", 

903 "The :class:`.MapperOption class is deprecated and will be removed " 

904 "in a future release. For " 

905 "modifications to queries on a per-execution basis, use the " 

906 ":class:`.UserDefinedOption` class to establish state within a " 

907 ":class:`.Query` or other Core statement, then use the " 

908 ":meth:`.SessionEvents.before_orm_execute` hook to consume them.", 

909 constructor=None, 

910) 

911class MapperOption(ORMOption): 

912 """Describe a modification to a Query""" 

913 

914 _is_legacy_option = True 

915 

916 propagate_to_loaders = False 

917 """if True, indicate this option should be carried along 

918 to "secondary" Query objects produced during lazy loads 

919 or refresh operations. 

920 

921 """ 

922 

923 def process_query(self, query): 

924 """Apply a modification to the given :class:`_query.Query`.""" 

925 

926 def process_query_conditionally(self, query): 

927 """same as process_query(), except that this option may not 

928 apply to the given query. 

929 

930 This is typically applied during a lazy load or scalar refresh 

931 operation to propagate options stated in the original Query to the 

932 new Query being used for the load. It occurs for those options that 

933 specify propagate_to_loaders=True. 

934 

935 """ 

936 

937 self.process_query(query) 

938 

939 

940class LoaderStrategy(object): 

941 """Describe the loading behavior of a StrategizedProperty object. 

942 

943 The ``LoaderStrategy`` interacts with the querying process in three 

944 ways: 

945 

946 * it controls the configuration of the ``InstrumentedAttribute`` 

947 placed on a class to handle the behavior of the attribute. this 

948 may involve setting up class-level callable functions to fire 

949 off a select operation when the attribute is first accessed 

950 (i.e. a lazy load) 

951 

952 * it processes the ``QueryContext`` at statement construction time, 

953 where it can modify the SQL statement that is being produced. 

954 For example, simple column attributes will add their represented 

955 column to the list of selected columns, a joined eager loader 

956 may establish join clauses to add to the statement. 

957 

958 * It produces "row processor" functions at result fetching time. 

959 These "row processor" functions populate a particular attribute 

960 on a particular mapped instance. 

961 

962 """ 

963 

964 __slots__ = ( 

965 "parent_property", 

966 "is_class_level", 

967 "parent", 

968 "key", 

969 "strategy_key", 

970 "strategy_opts", 

971 ) 

972 

973 def __init__(self, parent, strategy_key): 

974 self.parent_property = parent 

975 self.is_class_level = False 

976 self.parent = self.parent_property.parent 

977 self.key = self.parent_property.key 

978 self.strategy_key = strategy_key 

979 self.strategy_opts = dict(strategy_key) 

980 

981 def init_class_attribute(self, mapper): 

982 pass 

983 

984 def setup_query( 

985 self, compile_state, query_entity, path, loadopt, adapter, **kwargs 

986 ): 

987 """Establish column and other state for a given QueryContext. 

988 

989 This method fulfills the contract specified by MapperProperty.setup(). 

990 

991 StrategizedProperty delegates its setup() method 

992 directly to this method. 

993 

994 """ 

995 

996 def create_row_processor( 

997 self, 

998 context, 

999 query_entity, 

1000 path, 

1001 loadopt, 

1002 mapper, 

1003 result, 

1004 adapter, 

1005 populators, 

1006 ): 

1007 """Establish row processing functions for a given QueryContext. 

1008 

1009 This method fulfills the contract specified by 

1010 MapperProperty.create_row_processor(). 

1011 

1012 StrategizedProperty delegates its create_row_processor() method 

1013 directly to this method. 

1014 

1015 """ 

1016 

1017 def __str__(self): 

1018 return str(self.parent_property)