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

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

455 statements  

1# orm/interfaces.py 

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

3# <see AUTHORS file> 

4# 

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

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

7 

8""" 

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 annotations 

20 

21import collections 

22import dataclasses 

23import typing 

24from typing import Any 

25from typing import Callable 

26from typing import cast 

27from typing import ClassVar 

28from typing import Dict 

29from typing import Generic 

30from typing import Iterator 

31from typing import List 

32from typing import Mapping 

33from typing import NamedTuple 

34from typing import NoReturn 

35from typing import Optional 

36from typing import Sequence 

37from typing import Set 

38from typing import Tuple 

39from typing import Type 

40from typing import TYPE_CHECKING 

41from typing import TypedDict 

42from typing import TypeVar 

43from typing import Union 

44 

45from . import exc as orm_exc 

46from . import path_registry 

47from .base import _MappedAttribute as _MappedAttribute 

48from .base import DONT_SET as DONT_SET # noqa: F401 

49from .base import EXT_CONTINUE as EXT_CONTINUE # noqa: F401 

50from .base import EXT_SKIP as EXT_SKIP # noqa: F401 

51from .base import EXT_STOP as EXT_STOP # noqa: F401 

52from .base import InspectionAttr as InspectionAttr # noqa: F401 

53from .base import InspectionAttrInfo as InspectionAttrInfo 

54from .base import MANYTOMANY as MANYTOMANY # noqa: F401 

55from .base import MANYTOONE as MANYTOONE # noqa: F401 

56from .base import NO_KEY as NO_KEY # noqa: F401 

57from .base import NO_VALUE as NO_VALUE # noqa: F401 

58from .base import NotExtension as NotExtension # noqa: F401 

59from .base import ONETOMANY as ONETOMANY # noqa: F401 

60from .base import RelationshipDirection as RelationshipDirection # noqa: F401 

61from .base import SQLORMOperations 

62from .. import ColumnElement 

63from .. import exc as sa_exc 

64from .. import inspection 

65from .. import util 

66from ..sql import operators 

67from ..sql import roles 

68from ..sql import visitors 

69from ..sql.base import _NoArg 

70from ..sql.base import ExecutableOption 

71from ..sql.cache_key import HasCacheKey 

72from ..sql.operators import ColumnOperators 

73from ..sql.schema import Column 

74from ..sql.type_api import TypeEngine 

75from ..util import warn_deprecated 

76from ..util.typing import RODescriptorReference 

77from ..util.typing import TupleAny 

78from ..util.typing import Unpack 

79 

80 

81if typing.TYPE_CHECKING: 

82 from ._typing import _EntityType 

83 from ._typing import _IdentityKeyType 

84 from ._typing import _InstanceDict 

85 from ._typing import _InternalEntityType 

86 from ._typing import _ORMAdapterProto 

87 from .attributes import InstrumentedAttribute 

88 from .base import Mapped 

89 from .context import _MapperEntity 

90 from .context import _ORMCompileState 

91 from .context import QueryContext 

92 from .decl_api import RegistryType 

93 from .decl_base import _ClassScanAbstractConfig 

94 from .decl_base import _DeclarativeMapperConfig 

95 from .loading import _PopulatorDict 

96 from .mapper import Mapper 

97 from .path_registry import _AbstractEntityRegistry 

98 from .query import Query 

99 from .session import Session 

100 from .state import InstanceState 

101 from .strategy_options import _LoadElement 

102 from .util import AliasedInsp 

103 from .util import ORMAdapter 

104 from ..engine.result import Result 

105 from ..sql._typing import _ColumnExpressionArgument 

106 from ..sql._typing import _ColumnsClauseArgument 

107 from ..sql._typing import _DMLColumnArgument 

108 from ..sql._typing import _InfoType 

109 from ..sql.operators import OperatorType 

110 from ..sql.visitors import _TraverseInternalsType 

111 from ..util.typing import _AnnotationScanType 

112 

113_StrategyKey = Tuple[Any, ...] 

114 

115_T = TypeVar("_T", bound=Any) 

116_T_co = TypeVar("_T_co", bound=Any, covariant=True) 

117 

118_TLS = TypeVar("_TLS", bound="Type[LoaderStrategy]") 

119 

120 

121class ORMStatementRole(roles.StatementRole): 

122 __slots__ = () 

123 _role_name = ( 

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

125 ) 

126 

127 

128class ORMColumnsClauseRole( 

129 roles.ColumnsClauseRole, roles.TypedColumnsClauseRole[_T] 

130): 

131 __slots__ = () 

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

133 

134 

135class ORMEntityColumnsClauseRole(ORMColumnsClauseRole[_T]): 

136 __slots__ = () 

137 _role_name = "ORM mapped or aliased entity" 

138 

139 

140class ORMFromClauseRole(roles.FromClauseRole): 

141 __slots__ = () 

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

143 

144 

145class ORMColumnDescription(TypedDict): 

146 name: str 

147 # TODO: add python_type and sql_type here; combining them 

148 # into "type" is a bad idea 

149 type: Union[Type[Any], TypeEngine[Any]] 

150 aliased: bool 

151 expr: _ColumnsClauseArgument[Any] 

152 entity: Optional[_ColumnsClauseArgument[Any]] 

153 

154 

155class _IntrospectsAnnotations: 

156 __slots__ = () 

157 

158 @classmethod 

159 def _mapper_property_name(cls) -> str: 

160 return cls.__name__ 

161 

162 def found_in_pep593_annotated(self) -> Any: 

163 """return a copy of this object to use in declarative when the 

164 object is found inside of an Annotated object.""" 

165 

166 raise NotImplementedError( 

167 f"Use of the {self._mapper_property_name()!r} " 

168 "construct inside of an Annotated object is not yet supported." 

169 ) 

170 

171 def declarative_scan( 

172 self, 

173 decl_scan: _DeclarativeMapperConfig, 

174 registry: RegistryType, 

175 cls: Type[Any], 

176 originating_module: Optional[str], 

177 key: str, 

178 mapped_container: Optional[Type[Mapped[Any]]], 

179 annotation: Optional[_AnnotationScanType], 

180 extracted_mapped_annotation: Optional[_AnnotationScanType], 

181 is_dataclass_field: bool, 

182 ) -> None: 

183 """Perform class-specific initializaton at early declarative scanning 

184 time. 

185 

186 .. versionadded:: 2.0 

187 

188 """ 

189 

190 def _raise_for_required(self, key: str, cls: Type[Any]) -> NoReturn: 

191 raise sa_exc.ArgumentError( 

192 f"Python typing annotation is required for attribute " 

193 f'"{cls.__name__}.{key}" when primary argument(s) for ' 

194 f'"{self._mapper_property_name()}" ' 

195 "construct are None or not present" 

196 ) 

197 

198 

199class _DataclassArguments(TypedDict): 

200 """define arguments that can be passed to ORM Annotated Dataclass 

201 class definitions. 

202 

203 """ 

204 

205 init: Union[_NoArg, bool] 

206 repr: Union[_NoArg, bool] 

207 eq: Union[_NoArg, bool] 

208 order: Union[_NoArg, bool] 

209 unsafe_hash: Union[_NoArg, bool] 

210 match_args: Union[_NoArg, bool] 

211 kw_only: Union[_NoArg, bool] 

212 dataclass_callable: Union[_NoArg, Callable[..., Type[Any]]] 

213 

214 

215class _AttributeOptions(NamedTuple): 

216 """define Python-local attribute behavior options common to all 

217 :class:`.MapperProperty` objects. 

218 

219 Currently this includes dataclass-generation arguments. 

220 

221 .. versionadded:: 2.0 

222 

223 """ 

224 

225 dataclasses_init: Union[_NoArg, bool] 

226 dataclasses_repr: Union[_NoArg, bool] 

227 dataclasses_default: Union[_NoArg, Any] 

228 dataclasses_default_factory: Union[_NoArg, Callable[[], Any]] 

229 dataclasses_compare: Union[_NoArg, bool] 

230 dataclasses_kw_only: Union[_NoArg, bool] 

231 dataclasses_hash: Union[_NoArg, bool, None] 

232 dataclasses_dataclass_metadata: Union[_NoArg, Mapping[Any, Any], None] 

233 

234 def _as_dataclass_field( 

235 self, key: str, dataclass_setup_arguments: _DataclassArguments 

236 ) -> Any: 

237 """Return a ``dataclasses.Field`` object given these arguments.""" 

238 

239 kw: Dict[str, Any] = {} 

240 if self.dataclasses_default_factory is not _NoArg.NO_ARG: 

241 kw["default_factory"] = self.dataclasses_default_factory 

242 if self.dataclasses_default is not _NoArg.NO_ARG: 

243 kw["default"] = self.dataclasses_default 

244 if self.dataclasses_init is not _NoArg.NO_ARG: 

245 kw["init"] = self.dataclasses_init 

246 if self.dataclasses_repr is not _NoArg.NO_ARG: 

247 kw["repr"] = self.dataclasses_repr 

248 if self.dataclasses_compare is not _NoArg.NO_ARG: 

249 kw["compare"] = self.dataclasses_compare 

250 if self.dataclasses_kw_only is not _NoArg.NO_ARG: 

251 kw["kw_only"] = self.dataclasses_kw_only 

252 if self.dataclasses_hash is not _NoArg.NO_ARG: 

253 kw["hash"] = self.dataclasses_hash 

254 if self.dataclasses_dataclass_metadata is not _NoArg.NO_ARG: 

255 kw["metadata"] = self.dataclasses_dataclass_metadata 

256 

257 if "default" in kw and callable(kw["default"]): 

258 # callable defaults are ambiguous. deprecate them in favour of 

259 # insert_default or default_factory. #9936 

260 warn_deprecated( 

261 f"Callable object passed to the ``default`` parameter for " 

262 f"attribute {key!r} in a ORM-mapped Dataclasses context is " 

263 "ambiguous, " 

264 "and this use will raise an error in a future release. " 

265 "If this callable is intended to produce Core level INSERT " 

266 "default values for an underlying ``Column``, use " 

267 "the ``mapped_column.insert_default`` parameter instead. " 

268 "To establish this callable as providing a default value " 

269 "for instances of the dataclass itself, use the " 

270 "``default_factory`` dataclasses parameter.", 

271 "2.0", 

272 ) 

273 

274 if ( 

275 "init" in kw 

276 and not kw["init"] 

277 and "default" in kw 

278 and not callable(kw["default"]) # ignore callable defaults. #9936 

279 and "default_factory" not in kw # illegal but let dc.field raise 

280 ): 

281 # fix for #9879 

282 default = kw.pop("default") 

283 kw["default_factory"] = lambda: default 

284 

285 return dataclasses.field(**kw) 

286 

287 @classmethod 

288 def _get_arguments_for_make_dataclass( 

289 cls, 

290 decl_scan: _ClassScanAbstractConfig, 

291 key: str, 

292 annotation: _AnnotationScanType, 

293 mapped_container: Optional[Any], 

294 elem: Any, 

295 dataclass_setup_arguments: _DataclassArguments, 

296 enable_descriptor_defaults: bool, 

297 ) -> Union[ 

298 Tuple[str, _AnnotationScanType], 

299 Tuple[str, _AnnotationScanType, dataclasses.Field[Any] | None], 

300 ]: 

301 """given attribute key, annotation, and value from a class, return 

302 the argument tuple we would pass to dataclasses.make_dataclass() 

303 for this attribute. 

304 

305 """ 

306 if isinstance(elem, _DCAttributeOptions): 

307 attribute_options = elem._get_dataclass_setup_options( 

308 decl_scan, 

309 key, 

310 dataclass_setup_arguments, 

311 enable_descriptor_defaults, 

312 ) 

313 dc_field = attribute_options._as_dataclass_field( 

314 key, dataclass_setup_arguments 

315 ) 

316 

317 return (key, annotation, dc_field) 

318 elif elem is not _NoArg.NO_ARG: 

319 # why is typing not erroring on this? 

320 return (key, annotation, elem) 

321 elif mapped_container is not None: 

322 # it's Mapped[], but there's no "element", which means declarative 

323 # did not actually do anything for this field. 

324 # prior to 2.1, this would never happen and we had a false 

325 # assertion here, because the mapper _scan_attributes always 

326 # generates a MappedColumn when one is not present 

327 # (see issue #8718). However, in 2.1 we handle this case for the 

328 # non-mapped dataclass use case without the need to generate 

329 # MappedColumn that gets thrown away anyway. 

330 return (key, annotation) 

331 

332 else: 

333 # plain dataclass field, not mapped. Is only possible 

334 # if __allow_unmapped__ is set up. I can see this mode causing 

335 # problems... 

336 return (key, annotation) 

337 

338 

339_DEFAULT_ATTRIBUTE_OPTIONS = _AttributeOptions( 

340 _NoArg.NO_ARG, 

341 _NoArg.NO_ARG, 

342 _NoArg.NO_ARG, 

343 _NoArg.NO_ARG, 

344 _NoArg.NO_ARG, 

345 _NoArg.NO_ARG, 

346 _NoArg.NO_ARG, 

347 _NoArg.NO_ARG, 

348) 

349 

350_DEFAULT_READONLY_ATTRIBUTE_OPTIONS = _AttributeOptions( 

351 False, 

352 _NoArg.NO_ARG, 

353 _NoArg.NO_ARG, 

354 _NoArg.NO_ARG, 

355 _NoArg.NO_ARG, 

356 _NoArg.NO_ARG, 

357 _NoArg.NO_ARG, 

358 _NoArg.NO_ARG, 

359) 

360 

361 

362class _DCAttributeOptions: 

363 """mixin for descriptors or configurational objects that include dataclass 

364 field options. 

365 

366 This includes :class:`.MapperProperty`, :class:`._MapsColumn` within 

367 the ORM, but also includes :class:`.AssociationProxy` within ext. 

368 Can in theory be used for other descriptors that serve a similar role 

369 as association proxy. (*maybe* hybrids, not sure yet.) 

370 

371 """ 

372 

373 __slots__ = () 

374 

375 _attribute_options: _AttributeOptions 

376 """behavioral options for ORM-enabled Python attributes 

377 

378 .. versionadded:: 2.0 

379 

380 """ 

381 

382 _has_dataclass_arguments: bool 

383 

384 def _get_dataclass_setup_options( 

385 self, 

386 decl_scan: _ClassScanAbstractConfig, 

387 key: str, 

388 dataclass_setup_arguments: _DataclassArguments, 

389 enable_descriptor_defaults: bool, 

390 ) -> _AttributeOptions: 

391 return self._attribute_options 

392 

393 

394class _DataclassDefaultsDontSet(_DCAttributeOptions): 

395 __slots__ = () 

396 

397 _default_scalar_value: Any 

398 

399 _disable_dataclass_default_factory: bool = False 

400 

401 def _get_dataclass_setup_options( 

402 self, 

403 decl_scan: _ClassScanAbstractConfig, 

404 key: str, 

405 dataclass_setup_arguments: _DataclassArguments, 

406 enable_descriptor_defaults: bool, 

407 ) -> _AttributeOptions: 

408 

409 disable_descriptor_defaults = ( 

410 not enable_descriptor_defaults 

411 or getattr(decl_scan.cls, "_sa_disable_descriptor_defaults", False) 

412 ) 

413 

414 if disable_descriptor_defaults: 

415 return self._attribute_options 

416 

417 dataclasses_default = self._attribute_options.dataclasses_default 

418 dataclasses_default_factory = ( 

419 self._attribute_options.dataclasses_default_factory 

420 ) 

421 

422 if dataclasses_default is not _NoArg.NO_ARG and not callable( 

423 dataclasses_default 

424 ): 

425 self._default_scalar_value = ( 

426 self._attribute_options.dataclasses_default 

427 ) 

428 return self._attribute_options._replace( 

429 dataclasses_default=DONT_SET, 

430 ) 

431 elif ( 

432 self._disable_dataclass_default_factory 

433 and dataclasses_default_factory is not _NoArg.NO_ARG 

434 ): 

435 return self._attribute_options._replace( 

436 dataclasses_default=DONT_SET, 

437 dataclasses_default_factory=_NoArg.NO_ARG, 

438 ) 

439 return self._attribute_options 

440 

441 

442class _MapsColumns(_DCAttributeOptions, _MappedAttribute[_T]): 

443 """interface for declarative-capable construct that delivers one or more 

444 Column objects to the declarative process to be part of a Table. 

445 """ 

446 

447 __slots__ = () 

448 

449 @property 

450 def mapper_property_to_assign(self) -> Optional[MapperProperty[_T]]: 

451 """return a MapperProperty to be assigned to the declarative mapping""" 

452 raise NotImplementedError() 

453 

454 @property 

455 def columns_to_assign(self) -> List[Tuple[Column[_T], int]]: 

456 """A list of Column objects that should be declaratively added to the 

457 new Table object. 

458 

459 """ 

460 raise NotImplementedError() 

461 

462 

463# NOTE: MapperProperty needs to extend _MappedAttribute so that declarative 

464# typing works, i.e. "Mapped[A] = relationship()". This introduces an 

465# inconvenience which is that all the MapperProperty objects are treated 

466# as descriptors by typing tools, which are misled by this as assignment / 

467# access to a descriptor attribute wants to move through __get__. 

468# Therefore, references to MapperProperty as an instance variable, such 

469# as in PropComparator, may have some special typing workarounds such as the 

470# use of sqlalchemy.util.typing.DescriptorReference to avoid mis-interpretation 

471# by typing tools 

472@inspection._self_inspects 

473class MapperProperty( 

474 HasCacheKey, 

475 _DCAttributeOptions, 

476 _MappedAttribute[_T], 

477 InspectionAttrInfo, 

478 util.MemoizedSlots, 

479): 

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

481 

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

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

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

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

486 represented in the mapping as an instance of 

487 :class:`.Relationship`. 

488 

489 """ 

490 

491 __slots__ = ( 

492 "_configure_started", 

493 "_configure_finished", 

494 "_attribute_options", 

495 "_has_dataclass_arguments", 

496 "parent", 

497 "key", 

498 "info", 

499 "doc", 

500 ) 

501 

502 _cache_key_traversal: _TraverseInternalsType = [ 

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

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

505 ] 

506 

507 if not TYPE_CHECKING: 

508 cascade = None 

509 

510 is_property = True 

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

512 mapper property. 

513 

514 """ 

515 

516 comparator: PropComparator[_T] 

517 """The :class:`_orm.PropComparator` instance that implements SQL 

518 expression construction on behalf of this mapped attribute.""" 

519 

520 key: str 

521 """name of class attribute""" 

522 

523 parent: Mapper[Any] 

524 """the :class:`.Mapper` managing this property.""" 

525 

526 _is_relationship = False 

527 

528 _links_to_entity: bool 

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

530 

531 Should only be True for Relationship, False for all others. 

532 

533 """ 

534 

535 doc: Optional[str] 

536 """optional documentation string""" 

537 

538 info: _InfoType 

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

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

541 

542 The dictionary is generated when first accessed. Alternatively, 

543 it can be specified as a constructor argument to the 

544 :func:`.column_property`, :func:`_orm.relationship`, or :func:`.composite` 

545 functions. 

546 

547 .. seealso:: 

548 

549 :attr:`.QueryableAttribute.info` 

550 

551 :attr:`.SchemaItem.info` 

552 

553 """ 

554 

555 def _memoized_attr_info(self) -> _InfoType: 

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

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

558 

559 The dictionary is generated when first accessed. Alternatively, 

560 it can be specified as a constructor argument to the 

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

562 :func:`.composite` 

563 functions. 

564 

565 .. seealso:: 

566 

567 :attr:`.QueryableAttribute.info` 

568 

569 :attr:`.SchemaItem.info` 

570 

571 """ 

572 return {} 

573 

574 def setup( 

575 self, 

576 context: _ORMCompileState, 

577 query_entity: _MapperEntity, 

578 path: _AbstractEntityRegistry, 

579 adapter: Optional[ORMAdapter], 

580 **kwargs: Any, 

581 ) -> None: 

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

583 

584 Each MapperProperty associated with the target mapper processes the 

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

586 criterion as appropriate. 

587 

588 """ 

589 

590 def create_row_processor( 

591 self, 

592 context: _ORMCompileState, 

593 query_entity: _MapperEntity, 

594 path: _AbstractEntityRegistry, 

595 mapper: Mapper[Any], 

596 result: Result[Unpack[TupleAny]], 

597 adapter: Optional[ORMAdapter], 

598 populators: _PopulatorDict, 

599 ) -> None: 

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

601 set of populators lists. 

602 

603 """ 

604 

605 def cascade_iterator( 

606 self, 

607 type_: str, 

608 state: InstanceState[Any], 

609 dict_: _InstanceDict, 

610 visited_states: Set[InstanceState[Any]], 

611 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

612 ) -> Iterator[ 

613 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

614 ]: 

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

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

617 

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

619 

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

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

622 

623 This method typically only applies to Relationship. 

624 

625 """ 

626 

627 return iter(()) 

628 

629 def set_parent(self, parent: Mapper[Any], init: bool) -> None: 

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

631 

632 This method is overridden by some subclasses to perform extra 

633 setup when the mapper is first known. 

634 

635 """ 

636 self.parent = parent 

637 

638 def instrument_class(self, mapper: Mapper[Any]) -> None: 

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

640 instrumentation of the class attribute managed by this 

641 MapperProperty. 

642 

643 The MapperProperty here will typically call out to the 

644 attributes module to set up an InstrumentedAttribute. 

645 

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

647 and is called early in the mapper setup process. 

648 

649 The second step is typically the init_class_attribute step, 

650 called from StrategizedProperty via the post_instrument_class() 

651 hook. This step assigns additional state to the InstrumentedAttribute 

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

653 MapperProperty has determined what kind of persistence 

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

655 

656 """ 

657 

658 def __init__( 

659 self, 

660 attribute_options: Optional[_AttributeOptions] = None, 

661 _assume_readonly_dc_attributes: bool = False, 

662 ) -> None: 

663 self._configure_started = False 

664 self._configure_finished = False 

665 

666 if _assume_readonly_dc_attributes: 

667 default_attrs = _DEFAULT_READONLY_ATTRIBUTE_OPTIONS 

668 else: 

669 default_attrs = _DEFAULT_ATTRIBUTE_OPTIONS 

670 

671 if attribute_options and attribute_options != default_attrs: 

672 self._has_dataclass_arguments = True 

673 self._attribute_options = attribute_options 

674 else: 

675 self._has_dataclass_arguments = False 

676 self._attribute_options = default_attrs 

677 

678 def init(self) -> None: 

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

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

681 initialization steps. 

682 

683 

684 """ 

685 self._configure_started = True 

686 self.do_init() 

687 self._configure_finished = True 

688 

689 @property 

690 def class_attribute(self) -> InstrumentedAttribute[_T]: 

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

692 :class:`.MapperProperty`. 

693 

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

695 

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

697 

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

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

700 is possible:: 

701 

702 >>> from sqlalchemy import inspect 

703 >>> mapper = inspect(User) 

704 >>> addresses_property = mapper.attrs.addresses 

705 >>> addresses_property.class_attribute is User.addresses 

706 True 

707 >>> User.addresses.property is addresses_property 

708 True 

709 

710 

711 """ 

712 

713 return getattr(self.parent.class_, self.key) # type: ignore 

714 

715 def do_init(self) -> None: 

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

717 steps. 

718 

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

720 object's init() method. 

721 

722 """ 

723 

724 def post_instrument_class(self, mapper: Mapper[Any]) -> None: 

725 """Perform instrumentation adjustments that need to occur 

726 after init() has completed. 

727 

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

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

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

731 self.parent. 

732 

733 This method is typically used by StrategizedProperty, which delegates 

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

735 on the class-bound InstrumentedAttribute. 

736 

737 """ 

738 

739 def merge( 

740 self, 

741 session: Session, 

742 source_state: InstanceState[Any], 

743 source_dict: _InstanceDict, 

744 dest_state: InstanceState[Any], 

745 dest_dict: _InstanceDict, 

746 load: bool, 

747 _recursive: Dict[Any, object], 

748 _resolve_conflict_map: Dict[_IdentityKeyType[Any], object], 

749 ) -> None: 

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

751 from source to destination object. 

752 

753 """ 

754 

755 def __repr__(self) -> str: 

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

757 self.__class__.__name__, 

758 id(self), 

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

760 ) 

761 

762 

763@inspection._self_inspects 

764class PropComparator(SQLORMOperations[_T_co], Generic[_T_co], ColumnOperators): 

765 r"""Defines SQL operations for ORM mapped attributes. 

766 

767 SQLAlchemy allows for operators to 

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

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

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

771 :class:`.Relationship`, and :class:`.Composite`. 

772 

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

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

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

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

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

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

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

780 argument. In each case, 

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

782 

783 # definition of custom PropComparator subclasses 

784 

785 from sqlalchemy.orm.properties import ( 

786 ColumnProperty, 

787 Composite, 

788 Relationship, 

789 ) 

790 

791 

792 class MyColumnComparator(ColumnProperty.Comparator): 

793 def __eq__(self, other): 

794 return self.__clause_element__() == other 

795 

796 

797 class MyRelationshipComparator(Relationship.Comparator): 

798 def any(self, expression): 

799 "define the 'any' operation" 

800 # ... 

801 

802 

803 class MyCompositeComparator(Composite.Comparator): 

804 def __gt__(self, other): 

805 "redefine the 'greater than' operation" 

806 

807 return sql.and_( 

808 *[ 

809 a > b 

810 for a, b in zip( 

811 self.__clause_element__().clauses, 

812 other.__composite_values__(), 

813 ) 

814 ] 

815 ) 

816 

817 

818 # application of custom PropComparator subclasses 

819 

820 from sqlalchemy.orm import column_property, relationship, composite 

821 from sqlalchemy import Column, String 

822 

823 

824 class SomeMappedClass(Base): 

825 some_column = column_property( 

826 Column("some_column", String), 

827 comparator_factory=MyColumnComparator, 

828 ) 

829 

830 some_relationship = relationship( 

831 SomeOtherClass, comparator_factory=MyRelationshipComparator 

832 ) 

833 

834 some_composite = composite( 

835 Column("a", String), 

836 Column("b", String), 

837 comparator_factory=MyCompositeComparator, 

838 ) 

839 

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

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

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

843 :ref:`types_operators` for more detail. 

844 

845 .. seealso:: 

846 

847 :class:`.ColumnProperty.Comparator` 

848 

849 :class:`.Relationship.Comparator` 

850 

851 :class:`.Composite.Comparator` 

852 

853 :class:`.ColumnOperators` 

854 

855 :ref:`types_operators` 

856 

857 :attr:`.TypeEngine.comparator_factory` 

858 

859 """ 

860 

861 __slots__ = "prop", "_parententity", "_adapt_to_entity" 

862 

863 __visit_name__ = "orm_prop_comparator" 

864 

865 _parententity: _InternalEntityType[Any] 

866 _adapt_to_entity: Optional[AliasedInsp[Any]] 

867 prop: RODescriptorReference[MapperProperty[_T_co]] 

868 

869 def __init__( 

870 self, 

871 prop: MapperProperty[_T], 

872 parentmapper: _InternalEntityType[Any], 

873 adapt_to_entity: Optional[AliasedInsp[Any]] = None, 

874 ): 

875 self.prop = prop 

876 self._parententity = adapt_to_entity or parentmapper 

877 self._adapt_to_entity = adapt_to_entity 

878 

879 @util.non_memoized_property 

880 def property(self) -> MapperProperty[_T_co]: 

881 """Return the :class:`.MapperProperty` associated with this 

882 :class:`.PropComparator`. 

883 

884 

885 Return values here will commonly be instances of 

886 :class:`.ColumnProperty` or :class:`.Relationship`. 

887 

888 

889 """ 

890 return self.prop 

891 

892 def __clause_element__(self) -> roles.ColumnsClauseRole: 

893 raise NotImplementedError("%r" % self) 

894 

895 def _bulk_update_tuples( 

896 self, value: Any 

897 ) -> Sequence[Tuple[_DMLColumnArgument, Any]]: 

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

899 clause of an UPDATE statement. 

900 

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

902 construct. 

903 

904 """ 

905 

906 return [(cast("_DMLColumnArgument", self.__clause_element__()), value)] 

907 

908 def _bulk_dml_setter(self, key: str) -> Optional[Callable[..., Any]]: 

909 """return a callable that will process a bulk INSERT value""" 

910 

911 return None 

912 

913 def adapt_to_entity( 

914 self, adapt_to_entity: AliasedInsp[Any] 

915 ) -> PropComparator[_T_co]: 

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

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

918 """ 

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

920 

921 @util.ro_non_memoized_property 

922 def _parentmapper(self) -> Mapper[Any]: 

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

924 compatible with QueryableAttribute.""" 

925 return self._parententity.mapper 

926 

927 def _criterion_exists( 

928 self, 

929 criterion: Optional[_ColumnExpressionArgument[bool]] = None, 

930 **kwargs: Any, 

931 ) -> ColumnElement[Any]: 

932 return self.prop.comparator._criterion_exists(criterion, **kwargs) 

933 

934 @util.ro_non_memoized_property 

935 def adapter(self) -> Optional[_ORMAdapterProto]: 

936 """Produce a callable that adapts column expressions 

937 to suit an aliased version of this comparator. 

938 

939 """ 

940 if self._adapt_to_entity is None: 

941 return None 

942 else: 

943 return self._adapt_to_entity._orm_adapt_element 

944 

945 @util.ro_non_memoized_property 

946 def info(self) -> _InfoType: 

947 return self.prop.info 

948 

949 @staticmethod 

950 def _any_op(a: Any, b: Any, **kwargs: Any) -> Any: 

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

952 

953 @staticmethod 

954 def _has_op(left: Any, other: Any, **kwargs: Any) -> Any: 

955 return left.has(other, **kwargs) 

956 

957 @staticmethod 

958 def _of_type_op(a: Any, class_: Any) -> Any: 

959 return a.of_type(class_) 

960 

961 any_op = cast(operators.OperatorType, _any_op) 

962 has_op = cast(operators.OperatorType, _has_op) 

963 of_type_op = cast(operators.OperatorType, _of_type_op) 

964 

965 if typing.TYPE_CHECKING: 

966 

967 def operate( 

968 self, op: OperatorType, *other: Any, **kwargs: Any 

969 ) -> ColumnElement[Any]: ... 

970 

971 def reverse_operate( 

972 self, op: OperatorType, other: Any, **kwargs: Any 

973 ) -> ColumnElement[Any]: ... 

974 

975 def of_type(self, class_: _EntityType[Any]) -> PropComparator[_T_co]: 

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

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

978 construct. 

979 

980 Returns a new PropComparator from which further criterion can be 

981 evaluated. 

982 

983 e.g.:: 

984 

985 query.join(Company.employees.of_type(Engineer)).filter( 

986 Engineer.name == "foo" 

987 ) 

988 

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

990 against this specific subclass. 

991 

992 .. seealso:: 

993 

994 :ref:`orm_queryguide_joining_relationships_aliased` - in the 

995 :ref:`queryguide_toplevel` 

996 

997 :ref:`inheritance_of_type` 

998 

999 """ 

1000 

1001 return self.operate(PropComparator.of_type_op, class_) # type: ignore 

1002 

1003 def and_( 

1004 self, *criteria: _ColumnExpressionArgument[bool] 

1005 ) -> PropComparator[bool]: 

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

1007 relationship attribute. 

1008 

1009 E.g.:: 

1010 

1011 

1012 stmt = select(User).join( 

1013 User.addresses.and_(Address.email_address != "foo") 

1014 ) 

1015 

1016 stmt = select(User).options( 

1017 joinedload(User.addresses.and_(Address.email_address != "foo")) 

1018 ) 

1019 

1020 .. versionadded:: 1.4 

1021 

1022 .. seealso:: 

1023 

1024 :ref:`orm_queryguide_join_on_augmented` 

1025 

1026 :ref:`loader_option_criteria` 

1027 

1028 :func:`.with_loader_criteria` 

1029 

1030 """ 

1031 return self.operate(operators.and_, *criteria) # type: ignore 

1032 

1033 def any( 

1034 self, 

1035 criterion: Optional[_ColumnExpressionArgument[bool]] = None, 

1036 **kwargs: Any, 

1037 ) -> ColumnElement[bool]: 

1038 r"""Return a SQL expression representing true if this element 

1039 references a member which meets the given criterion. 

1040 

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

1042 :meth:`.Relationship.Comparator.any`. 

1043 

1044 :param criterion: an optional ClauseElement formulated against the 

1045 member class' table or attributes. 

1046 

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

1048 attribute names which will be compared via equality to the 

1049 corresponding values. 

1050 

1051 """ 

1052 

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

1054 

1055 def has( 

1056 self, 

1057 criterion: Optional[_ColumnExpressionArgument[bool]] = None, 

1058 **kwargs: Any, 

1059 ) -> ColumnElement[bool]: 

1060 r"""Return a SQL expression representing true if this element 

1061 references a member which meets the given criterion. 

1062 

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

1064 :meth:`.Relationship.Comparator.has`. 

1065 

1066 :param criterion: an optional ClauseElement formulated against the 

1067 member class' table or attributes. 

1068 

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

1070 attribute names which will be compared via equality to the 

1071 corresponding values. 

1072 

1073 """ 

1074 

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

1076 

1077 

1078class StrategizedProperty(MapperProperty[_T]): 

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

1080 loading behavior. 

1081 

1082 There is a single strategy selected by default. Alternate 

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

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

1085 

1086 The mechanics of StrategizedProperty are used for every Query 

1087 invocation for every mapped attribute participating in that Query, 

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

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

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

1091 performance-critical. 

1092 

1093 """ 

1094 

1095 __slots__ = ( 

1096 "_strategies", 

1097 "strategy", 

1098 "_wildcard_token", 

1099 "_default_path_loader_key", 

1100 "strategy_key", 

1101 ) 

1102 inherit_cache = True 

1103 strategy_wildcard_key: ClassVar[str] 

1104 

1105 strategy_key: _StrategyKey 

1106 

1107 _strategies: Dict[_StrategyKey, LoaderStrategy] 

1108 

1109 def _memoized_attr__wildcard_token(self) -> Tuple[str]: 

1110 return ( 

1111 f"{self.strategy_wildcard_key}:{path_registry._WILDCARD_TOKEN}", 

1112 ) 

1113 

1114 def _memoized_attr__default_path_loader_key( 

1115 self, 

1116 ) -> Tuple[str, Tuple[str]]: 

1117 return ( 

1118 "loader", 

1119 (f"{self.strategy_wildcard_key}:{path_registry._DEFAULT_TOKEN}",), 

1120 ) 

1121 

1122 def _get_context_loader( 

1123 self, context: _ORMCompileState, path: _AbstractEntityRegistry 

1124 ) -> Optional[_LoadElement]: 

1125 load: Optional[_LoadElement] = None 

1126 

1127 search_path = path[self] 

1128 

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

1130 # if any. 

1131 for path_key in ( 

1132 search_path._loader_key, 

1133 search_path._wildcard_path_loader_key, 

1134 search_path._default_path_loader_key, 

1135 ): 

1136 if path_key in context.attributes: 

1137 load = context.attributes[path_key] 

1138 break 

1139 

1140 # note that if strategy_options.Load is placing non-actionable 

1141 # objects in the context like defaultload(), we would 

1142 # need to continue the loop here if we got such an 

1143 # option as below. 

1144 # if load.strategy or load.local_opts: 

1145 # break 

1146 

1147 return load 

1148 

1149 def _get_strategy(self, key: _StrategyKey) -> LoaderStrategy: 

1150 try: 

1151 return self._strategies[key] 

1152 except KeyError: 

1153 pass 

1154 

1155 # run outside to prevent transfer of exception context 

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

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

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

1159 # strategy has multiple keys at this point 

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

1161 return strategy 

1162 

1163 def setup( 

1164 self, 

1165 context: _ORMCompileState, 

1166 query_entity: _MapperEntity, 

1167 path: _AbstractEntityRegistry, 

1168 adapter: Optional[ORMAdapter], 

1169 **kwargs: Any, 

1170 ) -> None: 

1171 loader = self._get_context_loader(context, path) 

1172 if loader and loader.strategy: 

1173 strat = self._get_strategy(loader.strategy) 

1174 else: 

1175 strat = self.strategy 

1176 strat.setup_query( 

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

1178 ) 

1179 

1180 def create_row_processor( 

1181 self, 

1182 context: _ORMCompileState, 

1183 query_entity: _MapperEntity, 

1184 path: _AbstractEntityRegistry, 

1185 mapper: Mapper[Any], 

1186 result: Result[Unpack[TupleAny]], 

1187 adapter: Optional[ORMAdapter], 

1188 populators: _PopulatorDict, 

1189 ) -> None: 

1190 loader = self._get_context_loader(context, path) 

1191 if loader and loader.strategy: 

1192 strat = self._get_strategy(loader.strategy) 

1193 else: 

1194 strat = self.strategy 

1195 strat.create_row_processor( 

1196 context, 

1197 query_entity, 

1198 path, 

1199 loader, 

1200 mapper, 

1201 result, 

1202 adapter, 

1203 populators, 

1204 ) 

1205 

1206 def do_init(self) -> None: 

1207 self._strategies = {} 

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

1209 

1210 def post_instrument_class(self, mapper: Mapper[Any]) -> None: 

1211 if not mapper.class_manager._attr_has_impl(self.key): 

1212 self.strategy.init_class_attribute(mapper) 

1213 

1214 _all_strategies: collections.defaultdict[ 

1215 Type[MapperProperty[Any]], Dict[_StrategyKey, Type[LoaderStrategy]] 

1216 ] = collections.defaultdict(dict) 

1217 

1218 @classmethod 

1219 def strategy_for(cls, **kw: Any) -> Callable[[_TLS], _TLS]: 

1220 def decorate(dec_cls: _TLS) -> _TLS: 

1221 # ensure each subclass of the strategy has its 

1222 # own _strategy_keys collection 

1223 if "_strategy_keys" not in dec_cls.__dict__: 

1224 dec_cls._strategy_keys = [] 

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

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

1227 dec_cls._strategy_keys.append(key) 

1228 return dec_cls 

1229 

1230 return decorate 

1231 

1232 @classmethod 

1233 def _strategy_lookup( 

1234 cls, requesting_property: MapperProperty[Any], *key: Any 

1235 ) -> Type[LoaderStrategy]: 

1236 requesting_property.parent._with_polymorphic_mappers 

1237 

1238 for prop_cls in cls.__mro__: 

1239 if prop_cls in cls._all_strategies: 

1240 if TYPE_CHECKING: 

1241 assert issubclass(prop_cls, MapperProperty) 

1242 strategies = cls._all_strategies[prop_cls] 

1243 try: 

1244 return strategies[key] 

1245 except KeyError: 

1246 pass 

1247 

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

1249 if key in strats: 

1250 intended_property_type = property_type 

1251 actual_strategy = strats[key] 

1252 break 

1253 else: 

1254 intended_property_type = None 

1255 actual_strategy = None 

1256 

1257 raise orm_exc.LoaderStrategyException( 

1258 cls, 

1259 requesting_property, 

1260 intended_property_type, 

1261 actual_strategy, 

1262 key, 

1263 ) 

1264 

1265 

1266class ORMOption(ExecutableOption): 

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

1268 

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

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

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

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

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

1274 time. 

1275 

1276 .. versionadded:: 1.4 

1277 

1278 """ 

1279 

1280 __slots__ = () 

1281 

1282 _is_legacy_option = False 

1283 

1284 propagate_to_loaders = False 

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

1286 to "secondary" SELECT statements that occur for relationship 

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

1288 

1289 """ 

1290 

1291 _is_core = False 

1292 

1293 _is_user_defined = False 

1294 

1295 _is_compile_state = False 

1296 

1297 _is_criteria_option = False 

1298 

1299 _is_strategy_option = False 

1300 

1301 def _adapt_cached_option_to_uncached_option( 

1302 self, context: QueryContext, uncached_opt: ORMOption 

1303 ) -> ORMOption: 

1304 """adapt this option to the "uncached" version of itself in a 

1305 loader strategy context. 

1306 

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

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

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

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

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

1312 entities passed to us from the cached query. 

1313 

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

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

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

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

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

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

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

1321 

1322 This routine could in the future involve 

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

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

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

1326 

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

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

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

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

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

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

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

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

1335 like an md5 key. 

1336 

1337 .. versionadded:: 1.4.41 

1338 

1339 """ 

1340 if uncached_opt is not None: 

1341 return uncached_opt 

1342 else: 

1343 return self 

1344 

1345 

1346class CompileStateOption(HasCacheKey, ORMOption): 

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

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

1349 

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

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

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

1353 interact with ORM compilation internals or caching. 

1354 

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

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

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

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

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

1360 structures. 

1361 

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

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

1364 

1365 .. versionadded:: 1.4.28 

1366 

1367 

1368 """ 

1369 

1370 __slots__ = () 

1371 

1372 _is_compile_state = True 

1373 

1374 def process_compile_state(self, compile_state: _ORMCompileState) -> None: 

1375 """Apply a modification to a given :class:`.ORMCompileState`. 

1376 

1377 This method is part of the implementation of a particular 

1378 :class:`.CompileStateOption` and is only invoked internally 

1379 when an ORM query is compiled. 

1380 

1381 """ 

1382 

1383 def process_compile_state_replaced_entities( 

1384 self, 

1385 compile_state: _ORMCompileState, 

1386 mapper_entities: Sequence[_MapperEntity], 

1387 ) -> None: 

1388 """Apply a modification to a given :class:`.ORMCompileState`, 

1389 given entities that were replaced by with_only_columns() or 

1390 with_entities(). 

1391 

1392 This method is part of the implementation of a particular 

1393 :class:`.CompileStateOption` and is only invoked internally 

1394 when an ORM query is compiled. 

1395 

1396 .. versionadded:: 1.4.19 

1397 

1398 """ 

1399 

1400 

1401class LoaderOption(CompileStateOption): 

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

1403 

1404 .. versionadded:: 1.4 

1405 

1406 """ 

1407 

1408 __slots__ = () 

1409 

1410 def process_compile_state_replaced_entities( 

1411 self, 

1412 compile_state: _ORMCompileState, 

1413 mapper_entities: Sequence[_MapperEntity], 

1414 ) -> None: 

1415 self.process_compile_state(compile_state) 

1416 

1417 

1418class CriteriaOption(CompileStateOption): 

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

1420 compilation time. 

1421 

1422 .. versionadded:: 1.4 

1423 

1424 """ 

1425 

1426 __slots__ = () 

1427 

1428 _is_criteria_option = True 

1429 

1430 def get_global_criteria(self, attributes: Dict[str, Any]) -> None: 

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

1432 attributes dictionary. 

1433 

1434 """ 

1435 

1436 

1437class UserDefinedOption(ORMOption): 

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

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

1440 

1441 """ 

1442 

1443 __slots__ = ("payload",) 

1444 

1445 _is_legacy_option = False 

1446 

1447 _is_user_defined = True 

1448 

1449 propagate_to_loaders = False 

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

1451 to "secondary" Query objects produced during lazy loads 

1452 or refresh operations. 

1453 

1454 """ 

1455 

1456 def __init__(self, payload: Optional[Any] = None): 

1457 self.payload = payload 

1458 

1459 

1460@util.deprecated_cls( 

1461 "1.4", 

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

1463 "in a future release. For " 

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

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

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

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

1468 constructor=None, 

1469) 

1470class MapperOption(ORMOption): 

1471 """Describe a modification to a Query""" 

1472 

1473 __slots__ = () 

1474 

1475 _is_legacy_option = True 

1476 

1477 propagate_to_loaders = False 

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

1479 to "secondary" Query objects produced during lazy loads 

1480 or refresh operations. 

1481 

1482 """ 

1483 

1484 def process_query(self, query: Query[Any]) -> None: 

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

1486 

1487 def process_query_conditionally(self, query: Query[Any]) -> None: 

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

1489 apply to the given query. 

1490 

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

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

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

1494 specify propagate_to_loaders=True. 

1495 

1496 """ 

1497 

1498 self.process_query(query) 

1499 

1500 

1501class LoaderStrategy: 

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

1503 

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

1505 ways: 

1506 

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

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

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

1510 off a select operation when the attribute is first accessed 

1511 (i.e. a lazy load) 

1512 

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

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

1515 For example, simple column attributes will add their represented 

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

1517 may establish join clauses to add to the statement. 

1518 

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

1520 These "row processor" functions populate a particular attribute 

1521 on a particular mapped instance. 

1522 

1523 """ 

1524 

1525 __slots__ = ( 

1526 "parent_property", 

1527 "is_class_level", 

1528 "parent", 

1529 "key", 

1530 "strategy_key", 

1531 "strategy_opts", 

1532 ) 

1533 

1534 _strategy_keys: ClassVar[List[_StrategyKey]] 

1535 

1536 def __init__( 

1537 self, parent: MapperProperty[Any], strategy_key: _StrategyKey 

1538 ): 

1539 self.parent_property = parent 

1540 self.is_class_level = False 

1541 self.parent = self.parent_property.parent 

1542 self.key = self.parent_property.key 

1543 self.strategy_key = strategy_key 

1544 self.strategy_opts = dict(strategy_key) 

1545 

1546 def init_class_attribute(self, mapper: Mapper[Any]) -> None: 

1547 pass 

1548 

1549 def setup_query( 

1550 self, 

1551 compile_state: _ORMCompileState, 

1552 query_entity: _MapperEntity, 

1553 path: _AbstractEntityRegistry, 

1554 loadopt: Optional[_LoadElement], 

1555 adapter: Optional[ORMAdapter], 

1556 **kwargs: Any, 

1557 ) -> None: 

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

1559 

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

1561 

1562 StrategizedProperty delegates its setup() method 

1563 directly to this method. 

1564 

1565 """ 

1566 

1567 def create_row_processor( 

1568 self, 

1569 context: _ORMCompileState, 

1570 query_entity: _MapperEntity, 

1571 path: _AbstractEntityRegistry, 

1572 loadopt: Optional[_LoadElement], 

1573 mapper: Mapper[Any], 

1574 result: Result[Unpack[TupleAny]], 

1575 adapter: Optional[ORMAdapter], 

1576 populators: _PopulatorDict, 

1577 ) -> None: 

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

1579 

1580 This method fulfills the contract specified by 

1581 MapperProperty.create_row_processor(). 

1582 

1583 StrategizedProperty delegates its create_row_processor() method 

1584 directly to this method. 

1585 

1586 """ 

1587 

1588 def __str__(self) -> str: 

1589 return str(self.parent_property)