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-2026 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 

80if typing.TYPE_CHECKING: 

81 from ._typing import _EntityType 

82 from ._typing import _IdentityKeyType 

83 from ._typing import _InstanceDict 

84 from ._typing import _InternalEntityType 

85 from ._typing import _ORMAdapterProto 

86 from .attributes import InstrumentedAttribute 

87 from .base import Mapped 

88 from .context import _MapperEntity 

89 from .context import _ORMCompileState 

90 from .context import QueryContext 

91 from .decl_api import RegistryType 

92 from .decl_base import _ClassScanAbstractConfig 

93 from .decl_base import _DeclarativeMapperConfig 

94 from .loading import _PopulatorDict 

95 from .mapper import Mapper 

96 from .path_registry import _AbstractEntityRegistry 

97 from .query import Query 

98 from .session import Session 

99 from .state import InstanceState 

100 from .strategy_options import _LoadElement 

101 from .util import AliasedInsp 

102 from .util import ORMAdapter 

103 from ..engine.result import Result 

104 from ..sql._typing import _ColumnExpressionArgument 

105 from ..sql._typing import _ColumnsClauseArgument 

106 from ..sql._typing import _DMLColumnArgument 

107 from ..sql._typing import _InfoType 

108 from ..sql.operators import OperatorType 

109 from ..sql.visitors import _TraverseInternalsType 

110 from ..util.typing import _AnnotationScanType 

111 

112_StrategyKey = Tuple[Any, ...] 

113 

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

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

116 

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

118 

119 

120class ORMStatementRole(roles.StatementRole): 

121 __slots__ = () 

122 _role_name = ( 

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

124 ) 

125 

126 

127class ORMColumnsClauseRole( 

128 roles.ColumnsClauseRole, roles.TypedColumnsClauseRole[_T] 

129): 

130 __slots__ = () 

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

132 

133 

134class ORMEntityColumnsClauseRole(ORMColumnsClauseRole[_T]): 

135 __slots__ = () 

136 _role_name = "ORM mapped or aliased entity" 

137 

138 

139class ORMFromClauseRole(roles.FromClauseRole): 

140 __slots__ = () 

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

142 

143 

144class ORMColumnDescription(TypedDict): 

145 name: str 

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

147 # into "type" is a bad idea 

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

149 aliased: bool 

150 expr: _ColumnsClauseArgument[Any] 

151 entity: Optional[_ColumnsClauseArgument[Any]] 

152 

153 

154class _IntrospectsAnnotations: 

155 __slots__ = () 

156 

157 @classmethod 

158 def _mapper_property_name(cls) -> str: 

159 return cls.__name__ 

160 

161 def found_in_pep593_annotated(self) -> Any: 

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

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

164 

165 raise NotImplementedError( 

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

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

168 ) 

169 

170 def declarative_scan( 

171 self, 

172 decl_scan: _DeclarativeMapperConfig, 

173 registry: RegistryType, 

174 cls: Type[Any], 

175 originating_module: Optional[str], 

176 key: str, 

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

178 annotation: Optional[_AnnotationScanType], 

179 extracted_mapped_annotation: Optional[_AnnotationScanType], 

180 is_dataclass_field: bool, 

181 ) -> None: 

182 """Perform class-specific initialization at early declarative scanning 

183 time. 

184 

185 .. versionadded:: 2.0 

186 

187 """ 

188 

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

190 raise sa_exc.ArgumentError( 

191 f"Python typing annotation is required for attribute " 

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

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

194 "construct are None or not present" 

195 ) 

196 

197 

198class _DataclassArguments(TypedDict): 

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

200 class definitions. 

201 

202 """ 

203 

204 init: Union[_NoArg, bool] 

205 repr: Union[_NoArg, bool] 

206 eq: Union[_NoArg, bool] 

207 order: Union[_NoArg, bool] 

208 unsafe_hash: Union[_NoArg, bool] 

209 match_args: Union[_NoArg, bool] 

210 kw_only: Union[_NoArg, bool] 

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

212 

213 

214class _AttributeOptions(NamedTuple): 

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

216 :class:`.MapperProperty` objects. 

217 

218 Currently this includes dataclass-generation arguments. 

219 

220 .. versionadded:: 2.0 

221 

222 """ 

223 

224 dataclasses_init: Union[_NoArg, bool] 

225 dataclasses_repr: Union[_NoArg, bool] 

226 dataclasses_default: Union[_NoArg, Any] 

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

228 dataclasses_compare: Union[_NoArg, bool] 

229 dataclasses_kw_only: Union[_NoArg, bool] 

230 dataclasses_hash: Union[_NoArg, bool, None] 

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

232 

233 def _as_dataclass_field( 

234 self, key: str, dataclass_setup_arguments: _DataclassArguments 

235 ) -> Any: 

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

237 

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

239 if self.dataclasses_default_factory is not _NoArg.NO_ARG: 

240 kw["default_factory"] = self.dataclasses_default_factory 

241 if self.dataclasses_default is not _NoArg.NO_ARG: 

242 kw["default"] = self.dataclasses_default 

243 if self.dataclasses_init is not _NoArg.NO_ARG: 

244 kw["init"] = self.dataclasses_init 

245 if self.dataclasses_repr is not _NoArg.NO_ARG: 

246 kw["repr"] = self.dataclasses_repr 

247 if self.dataclasses_compare is not _NoArg.NO_ARG: 

248 kw["compare"] = self.dataclasses_compare 

249 if self.dataclasses_kw_only is not _NoArg.NO_ARG: 

250 kw["kw_only"] = self.dataclasses_kw_only 

251 if self.dataclasses_hash is not _NoArg.NO_ARG: 

252 kw["hash"] = self.dataclasses_hash 

253 if self.dataclasses_dataclass_metadata is not _NoArg.NO_ARG: 

254 kw["metadata"] = self.dataclasses_dataclass_metadata 

255 

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

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

258 # insert_default or default_factory. #9936 

259 warn_deprecated( 

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

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

262 "ambiguous, " 

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

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

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

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

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

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

269 "``default_factory`` dataclasses parameter.", 

270 "2.0", 

271 ) 

272 

273 if ( 

274 "init" in kw 

275 and not kw["init"] 

276 and "default" in kw 

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

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

279 ): 

280 # fix for #9879 

281 default = kw.pop("default") 

282 kw["default_factory"] = lambda: default 

283 

284 return dataclasses.field(**kw) 

285 

286 @classmethod 

287 def _get_arguments_for_make_dataclass( 

288 cls, 

289 decl_scan: _ClassScanAbstractConfig, 

290 key: str, 

291 annotation: _AnnotationScanType, 

292 mapped_container: Optional[Any], 

293 elem: Any, 

294 dataclass_setup_arguments: _DataclassArguments, 

295 enable_descriptor_defaults: bool, 

296 ) -> Union[ 

297 Tuple[str, _AnnotationScanType], 

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

299 ]: 

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

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

302 for this attribute. 

303 

304 """ 

305 if isinstance(elem, _DCAttributeOptions): 

306 attribute_options = elem._get_dataclass_setup_options( 

307 decl_scan, 

308 key, 

309 dataclass_setup_arguments, 

310 enable_descriptor_defaults, 

311 ) 

312 dc_field = attribute_options._as_dataclass_field( 

313 key, dataclass_setup_arguments 

314 ) 

315 

316 return (key, annotation, dc_field) 

317 elif elem is not _NoArg.NO_ARG: 

318 # why is typing not erroring on this? 

319 return (key, annotation, elem) 

320 elif mapped_container is not None: 

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

322 # did not actually do anything for this field. 

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

324 # assertion here, because the mapper _scan_attributes always 

325 # generates a MappedColumn when one is not present 

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

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

328 # MappedColumn that gets thrown away anyway. 

329 return (key, annotation) 

330 

331 else: 

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

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

334 # problems... 

335 return (key, annotation) 

336 

337 

338_DEFAULT_ATTRIBUTE_OPTIONS = _AttributeOptions( 

339 _NoArg.NO_ARG, 

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) 

348 

349_DEFAULT_READONLY_ATTRIBUTE_OPTIONS = _AttributeOptions( 

350 False, 

351 _NoArg.NO_ARG, 

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) 

359 

360 

361class _DCAttributeOptions: 

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

363 field options. 

364 

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

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

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

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

369 

370 """ 

371 

372 __slots__ = () 

373 

374 _attribute_options: _AttributeOptions 

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

376 

377 .. versionadded:: 2.0 

378 

379 """ 

380 

381 _has_dataclass_arguments: bool 

382 

383 def _get_dataclass_setup_options( 

384 self, 

385 decl_scan: _ClassScanAbstractConfig, 

386 key: str, 

387 dataclass_setup_arguments: _DataclassArguments, 

388 enable_descriptor_defaults: bool, 

389 ) -> _AttributeOptions: 

390 return self._attribute_options 

391 

392 

393class _DataclassDefaultsDontSet(_DCAttributeOptions): 

394 __slots__ = () 

395 

396 _default_scalar_value: Any 

397 

398 _disable_dataclass_default_factory: bool = False 

399 

400 def _get_dataclass_setup_options( 

401 self, 

402 decl_scan: _ClassScanAbstractConfig, 

403 key: str, 

404 dataclass_setup_arguments: _DataclassArguments, 

405 enable_descriptor_defaults: bool, 

406 ) -> _AttributeOptions: 

407 

408 disable_descriptor_defaults = ( 

409 not enable_descriptor_defaults 

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

411 ) 

412 

413 if disable_descriptor_defaults: 

414 return self._attribute_options 

415 

416 dataclasses_default = self._attribute_options.dataclasses_default 

417 dataclasses_default_factory = ( 

418 self._attribute_options.dataclasses_default_factory 

419 ) 

420 

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

422 dataclasses_default 

423 ): 

424 self._default_scalar_value = ( 

425 self._attribute_options.dataclasses_default 

426 ) 

427 return self._attribute_options._replace( 

428 dataclasses_default=DONT_SET, 

429 ) 

430 elif ( 

431 self._disable_dataclass_default_factory 

432 and dataclasses_default_factory is not _NoArg.NO_ARG 

433 ): 

434 return self._attribute_options._replace( 

435 dataclasses_default=DONT_SET, 

436 dataclasses_default_factory=_NoArg.NO_ARG, 

437 ) 

438 return self._attribute_options 

439 

440 

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

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

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

444 """ 

445 

446 __slots__ = () 

447 

448 @property 

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

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

451 raise NotImplementedError() 

452 

453 @property 

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

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

456 new Table object. 

457 

458 """ 

459 raise NotImplementedError() 

460 

461 

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

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

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

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

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

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

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

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

470# by typing tools 

471@inspection._self_inspects 

472class MapperProperty( 

473 HasCacheKey, 

474 _DCAttributeOptions, 

475 _MappedAttribute[_T], 

476 InspectionAttrInfo, 

477 util.MemoizedSlots, 

478): 

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

480 

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

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

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

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

485 represented in the mapping as an instance of 

486 :class:`.Relationship`. 

487 

488 """ 

489 

490 __slots__ = ( 

491 "_configure_started", 

492 "_configure_finished", 

493 "_attribute_options", 

494 "_has_dataclass_arguments", 

495 "parent", 

496 "key", 

497 "info", 

498 "doc", 

499 ) 

500 

501 _cache_key_traversal: _TraverseInternalsType = [ 

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

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

504 ] 

505 

506 if not TYPE_CHECKING: 

507 cascade = None 

508 

509 is_property = True 

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

511 mapper property. 

512 

513 """ 

514 

515 comparator: PropComparator[_T] 

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

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

518 

519 key: str 

520 """name of class attribute""" 

521 

522 parent: Mapper[Any] 

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

524 

525 _is_relationship = False 

526 

527 _links_to_entity: bool 

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

529 

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

531 

532 """ 

533 

534 doc: Optional[str] 

535 """optional documentation string""" 

536 

537 info: _InfoType 

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

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

540 

541 The dictionary is generated when first accessed. Alternatively, 

542 it can be specified as a constructor argument to the 

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

544 functions. 

545 

546 .. seealso:: 

547 

548 :attr:`.QueryableAttribute.info` 

549 

550 :attr:`.SchemaItem.info` 

551 

552 """ 

553 

554 def _memoized_attr_info(self) -> _InfoType: 

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

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

557 

558 The dictionary is generated when first accessed. Alternatively, 

559 it can be specified as a constructor argument to the 

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

561 :func:`.composite` 

562 functions. 

563 

564 .. seealso:: 

565 

566 :attr:`.QueryableAttribute.info` 

567 

568 :attr:`.SchemaItem.info` 

569 

570 """ 

571 return {} 

572 

573 def setup( 

574 self, 

575 context: _ORMCompileState, 

576 query_entity: _MapperEntity, 

577 path: _AbstractEntityRegistry, 

578 adapter: Optional[ORMAdapter], 

579 **kwargs: Any, 

580 ) -> None: 

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

582 

583 Each MapperProperty associated with the target mapper processes the 

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

585 criterion as appropriate. 

586 

587 """ 

588 

589 def create_row_processor( 

590 self, 

591 context: _ORMCompileState, 

592 query_entity: _MapperEntity, 

593 path: _AbstractEntityRegistry, 

594 mapper: Mapper[Any], 

595 result: Result[Unpack[TupleAny]], 

596 adapter: Optional[ORMAdapter], 

597 populators: _PopulatorDict, 

598 ) -> None: 

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

600 set of populators lists. 

601 

602 """ 

603 

604 def cascade_iterator( 

605 self, 

606 type_: str, 

607 state: InstanceState[Any], 

608 dict_: _InstanceDict, 

609 visited_states: Set[InstanceState[Any]], 

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

611 ) -> Iterator[ 

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

613 ]: 

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

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

616 

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

618 

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

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

621 

622 This method typically only applies to Relationship. 

623 

624 """ 

625 

626 return iter(()) 

627 

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

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

630 

631 This method is overridden by some subclasses to perform extra 

632 setup when the mapper is first known. 

633 

634 """ 

635 self.parent = parent 

636 

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

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

639 instrumentation of the class attribute managed by this 

640 MapperProperty. 

641 

642 The MapperProperty here will typically call out to the 

643 attributes module to set up an InstrumentedAttribute. 

644 

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

646 and is called early in the mapper setup process. 

647 

648 The second step is typically the init_class_attribute step, 

649 called from StrategizedProperty via the post_instrument_class() 

650 hook. This step assigns additional state to the InstrumentedAttribute 

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

652 MapperProperty has determined what kind of persistence 

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

654 

655 """ 

656 

657 def __init__( 

658 self, 

659 attribute_options: Optional[_AttributeOptions] = None, 

660 _assume_readonly_dc_attributes: bool = False, 

661 ) -> None: 

662 self._configure_started = False 

663 self._configure_finished = False 

664 

665 if _assume_readonly_dc_attributes: 

666 default_attrs = _DEFAULT_READONLY_ATTRIBUTE_OPTIONS 

667 else: 

668 default_attrs = _DEFAULT_ATTRIBUTE_OPTIONS 

669 

670 if attribute_options and attribute_options != default_attrs: 

671 self._has_dataclass_arguments = True 

672 self._attribute_options = attribute_options 

673 else: 

674 self._has_dataclass_arguments = False 

675 self._attribute_options = default_attrs 

676 

677 def init(self) -> None: 

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

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

680 initialization steps. 

681 

682 

683 """ 

684 self._configure_started = True 

685 self.do_init() 

686 self._configure_finished = True 

687 

688 @property 

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

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

691 :class:`.MapperProperty`. 

692 

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

694 

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

696 

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

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

699 is possible:: 

700 

701 >>> from sqlalchemy import inspect 

702 >>> mapper = inspect(User) 

703 >>> addresses_property = mapper.attrs.addresses 

704 >>> addresses_property.class_attribute is User.addresses 

705 True 

706 >>> User.addresses.property is addresses_property 

707 True 

708 

709 

710 """ 

711 

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

713 

714 def do_init(self) -> None: 

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

716 steps. 

717 

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

719 object's init() method. 

720 

721 """ 

722 

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

724 """Perform instrumentation adjustments that need to occur 

725 after init() has completed. 

726 

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

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

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

730 self.parent. 

731 

732 This method is typically used by StrategizedProperty, which delegates 

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

734 on the class-bound InstrumentedAttribute. 

735 

736 """ 

737 

738 def merge( 

739 self, 

740 session: Session, 

741 source_state: InstanceState[Any], 

742 source_dict: _InstanceDict, 

743 dest_state: InstanceState[Any], 

744 dest_dict: _InstanceDict, 

745 load: bool, 

746 _recursive: Dict[Any, object], 

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

748 ) -> None: 

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

750 from source to destination object. 

751 

752 """ 

753 

754 def __repr__(self) -> str: 

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

756 self.__class__.__name__, 

757 id(self), 

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

759 ) 

760 

761 

762@inspection._self_inspects 

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

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

765 

766 SQLAlchemy allows for operators to 

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

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

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

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

771 

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

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

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

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

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

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

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

779 argument. In each case, 

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

781 

782 # definition of custom PropComparator subclasses 

783 

784 from sqlalchemy.orm.properties import ( 

785 ColumnProperty, 

786 Composite, 

787 Relationship, 

788 ) 

789 

790 

791 class MyColumnComparator(ColumnProperty.Comparator): 

792 def __eq__(self, other): 

793 return self.__clause_element__() == other 

794 

795 

796 class MyRelationshipComparator(Relationship.Comparator): 

797 def any(self, expression): 

798 "define the 'any' operation" 

799 # ... 

800 

801 

802 class MyCompositeComparator(Composite.Comparator): 

803 def __gt__(self, other): 

804 "redefine the 'greater than' operation" 

805 

806 return sql.and_( 

807 *[ 

808 a > b 

809 for a, b in zip( 

810 self.__clause_element__().clauses, 

811 other.__composite_values__(), 

812 ) 

813 ] 

814 ) 

815 

816 

817 # application of custom PropComparator subclasses 

818 

819 from sqlalchemy.orm import column_property, relationship, composite 

820 from sqlalchemy import Column, String 

821 

822 

823 class SomeMappedClass(Base): 

824 some_column = column_property( 

825 Column("some_column", String), 

826 comparator_factory=MyColumnComparator, 

827 ) 

828 

829 some_relationship = relationship( 

830 SomeOtherClass, comparator_factory=MyRelationshipComparator 

831 ) 

832 

833 some_composite = composite( 

834 Column("a", String), 

835 Column("b", String), 

836 comparator_factory=MyCompositeComparator, 

837 ) 

838 

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

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

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

842 :ref:`types_operators` for more detail. 

843 

844 .. seealso:: 

845 

846 :class:`.ColumnProperty.Comparator` 

847 

848 :class:`.Relationship.Comparator` 

849 

850 :class:`.Composite.Comparator` 

851 

852 :class:`.ColumnOperators` 

853 

854 :ref:`types_operators` 

855 

856 :attr:`.TypeEngine.comparator_factory` 

857 

858 """ 

859 

860 __slots__ = "prop", "_parententity", "_adapt_to_entity" 

861 

862 __visit_name__ = "orm_prop_comparator" 

863 

864 _parententity: _InternalEntityType[Any] 

865 _adapt_to_entity: Optional[AliasedInsp[Any]] 

866 prop: RODescriptorReference[MapperProperty[_T_co]] 

867 

868 def __init__( 

869 self, 

870 prop: MapperProperty[_T], 

871 parentmapper: _InternalEntityType[Any], 

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

873 ): 

874 self.prop = prop 

875 self._parententity = adapt_to_entity or parentmapper 

876 self._adapt_to_entity = adapt_to_entity 

877 

878 @util.non_memoized_property 

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

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

881 :class:`.PropComparator`. 

882 

883 

884 Return values here will commonly be instances of 

885 :class:`.ColumnProperty` or :class:`.Relationship`. 

886 

887 

888 """ 

889 return self.prop 

890 

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

892 raise NotImplementedError("%r" % self) 

893 

894 def _bulk_update_tuples( 

895 self, value: Any 

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

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

898 clause of an UPDATE statement. 

899 

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

901 construct. 

902 

903 """ 

904 

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

906 

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

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

909 

910 return None 

911 

912 def adapt_to_entity( 

913 self, adapt_to_entity: AliasedInsp[Any] 

914 ) -> PropComparator[_T_co]: 

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

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

917 """ 

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

919 

920 @util.ro_non_memoized_property 

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

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

923 compatible with QueryableAttribute.""" 

924 return self._parententity.mapper 

925 

926 def _criterion_exists( 

927 self, 

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

929 **kwargs: Any, 

930 ) -> ColumnElement[Any]: 

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

932 

933 @util.ro_non_memoized_property 

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

935 """Produce a callable that adapts column expressions 

936 to suit an aliased version of this comparator. 

937 

938 """ 

939 if self._adapt_to_entity is None: 

940 return None 

941 else: 

942 return self._adapt_to_entity._orm_adapt_element 

943 

944 @util.ro_non_memoized_property 

945 def info(self) -> _InfoType: 

946 return self.prop.info 

947 

948 @staticmethod 

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

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

951 

952 @staticmethod 

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

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

955 

956 @staticmethod 

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

958 return a.of_type(class_) 

959 

960 any_op = cast(operators.OperatorType, _any_op) 

961 has_op = cast(operators.OperatorType, _has_op) 

962 of_type_op = cast(operators.OperatorType, _of_type_op) 

963 

964 if typing.TYPE_CHECKING: 

965 

966 def operate( 

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

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

969 

970 def reverse_operate( 

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

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

973 

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

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

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

977 construct. 

978 

979 Returns a new PropComparator from which further criterion can be 

980 evaluated. 

981 

982 e.g.:: 

983 

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

985 Engineer.name == "foo" 

986 ) 

987 

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

989 against this specific subclass. 

990 

991 .. seealso:: 

992 

993 :ref:`orm_queryguide_joining_relationships_aliased` - in the 

994 :ref:`queryguide_toplevel` 

995 

996 :ref:`inheritance_of_type` 

997 

998 """ 

999 

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

1001 

1002 def and_( 

1003 self, *criteria: _ColumnExpressionArgument[bool] 

1004 ) -> PropComparator[bool]: 

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

1006 relationship attribute. 

1007 

1008 E.g.:: 

1009 

1010 

1011 stmt = select(User).join( 

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

1013 ) 

1014 

1015 stmt = select(User).options( 

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

1017 ) 

1018 

1019 .. versionadded:: 1.4 

1020 

1021 .. seealso:: 

1022 

1023 :ref:`orm_queryguide_join_on_augmented` 

1024 

1025 :ref:`loader_option_criteria` 

1026 

1027 :func:`.with_loader_criteria` 

1028 

1029 """ 

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

1031 

1032 def any( 

1033 self, 

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

1035 **kwargs: Any, 

1036 ) -> ColumnElement[bool]: 

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

1038 references a member which meets the given criterion. 

1039 

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

1041 :meth:`.Relationship.Comparator.any`. 

1042 

1043 :param criterion: an optional ClauseElement formulated against the 

1044 member class' table or attributes. 

1045 

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

1047 attribute names which will be compared via equality to the 

1048 corresponding values. 

1049 

1050 """ 

1051 

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

1053 

1054 def has( 

1055 self, 

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

1057 **kwargs: Any, 

1058 ) -> ColumnElement[bool]: 

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

1060 references a member which meets the given criterion. 

1061 

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

1063 :meth:`.Relationship.Comparator.has`. 

1064 

1065 :param criterion: an optional ClauseElement formulated against the 

1066 member class' table or attributes. 

1067 

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

1069 attribute names which will be compared via equality to the 

1070 corresponding values. 

1071 

1072 """ 

1073 

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

1075 

1076 

1077class StrategizedProperty(MapperProperty[_T]): 

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

1079 loading behavior. 

1080 

1081 There is a single strategy selected by default. Alternate 

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

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

1084 

1085 The mechanics of StrategizedProperty are used for every Query 

1086 invocation for every mapped attribute participating in that Query, 

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

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

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

1090 performance-critical. 

1091 

1092 """ 

1093 

1094 __slots__ = ( 

1095 "_strategies", 

1096 "strategy", 

1097 "_wildcard_token", 

1098 "_default_path_loader_key", 

1099 "strategy_key", 

1100 ) 

1101 inherit_cache = True 

1102 strategy_wildcard_key: ClassVar[str] 

1103 

1104 strategy_key: _StrategyKey 

1105 

1106 _strategies: Dict[_StrategyKey, LoaderStrategy] 

1107 

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

1109 return ( 

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

1111 ) 

1112 

1113 def _memoized_attr__default_path_loader_key( 

1114 self, 

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

1116 return ( 

1117 "loader", 

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

1119 ) 

1120 

1121 def _get_context_loader( 

1122 self, context: _ORMCompileState, path: _AbstractEntityRegistry 

1123 ) -> Optional[_LoadElement]: 

1124 load: Optional[_LoadElement] = None 

1125 

1126 search_path = path[self] 

1127 

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

1129 # if any. 

1130 for path_key in ( 

1131 search_path._loader_key, 

1132 search_path._wildcard_path_loader_key, 

1133 search_path._default_path_loader_key, 

1134 ): 

1135 if path_key in context.attributes: 

1136 load = context.attributes[path_key] 

1137 break 

1138 

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

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

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

1142 # option as below. 

1143 # if load.strategy or load.local_opts: 

1144 # break 

1145 

1146 return load 

1147 

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

1149 try: 

1150 return self._strategies[key] 

1151 except KeyError: 

1152 pass 

1153 

1154 # run outside to prevent transfer of exception context 

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

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

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

1158 # strategy has multiple keys at this point 

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

1160 return strategy 

1161 

1162 def setup( 

1163 self, 

1164 context: _ORMCompileState, 

1165 query_entity: _MapperEntity, 

1166 path: _AbstractEntityRegistry, 

1167 adapter: Optional[ORMAdapter], 

1168 **kwargs: Any, 

1169 ) -> None: 

1170 loader = self._get_context_loader(context, path) 

1171 if loader and loader.strategy: 

1172 strat = self._get_strategy(loader.strategy) 

1173 else: 

1174 strat = self.strategy 

1175 strat.setup_query( 

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

1177 ) 

1178 

1179 def create_row_processor( 

1180 self, 

1181 context: _ORMCompileState, 

1182 query_entity: _MapperEntity, 

1183 path: _AbstractEntityRegistry, 

1184 mapper: Mapper[Any], 

1185 result: Result[Unpack[TupleAny]], 

1186 adapter: Optional[ORMAdapter], 

1187 populators: _PopulatorDict, 

1188 ) -> None: 

1189 loader = self._get_context_loader(context, path) 

1190 if loader and loader.strategy: 

1191 strat = self._get_strategy(loader.strategy) 

1192 else: 

1193 strat = self.strategy 

1194 strat.create_row_processor( 

1195 context, 

1196 query_entity, 

1197 path, 

1198 loader, 

1199 mapper, 

1200 result, 

1201 adapter, 

1202 populators, 

1203 ) 

1204 

1205 def do_init(self) -> None: 

1206 self._strategies = {} 

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

1208 

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

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

1211 self.strategy.init_class_attribute(mapper) 

1212 

1213 _all_strategies: collections.defaultdict[ 

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

1215 ] = collections.defaultdict(dict) 

1216 

1217 @classmethod 

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

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

1220 # ensure each subclass of the strategy has its 

1221 # own _strategy_keys collection 

1222 if "_strategy_keys" not in dec_cls.__dict__: 

1223 dec_cls._strategy_keys = [] 

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

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

1226 dec_cls._strategy_keys.append(key) 

1227 return dec_cls 

1228 

1229 return decorate 

1230 

1231 @classmethod 

1232 def _strategy_lookup( 

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

1234 ) -> Type[LoaderStrategy]: 

1235 requesting_property.parent._with_polymorphic_mappers 

1236 

1237 for prop_cls in cls.__mro__: 

1238 if prop_cls in cls._all_strategies: 

1239 if TYPE_CHECKING: 

1240 assert issubclass(prop_cls, MapperProperty) 

1241 strategies = cls._all_strategies[prop_cls] 

1242 try: 

1243 return strategies[key] 

1244 except KeyError: 

1245 pass 

1246 

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

1248 if key in strats: 

1249 intended_property_type = property_type 

1250 actual_strategy = strats[key] 

1251 break 

1252 else: 

1253 intended_property_type = None 

1254 actual_strategy = None 

1255 

1256 raise orm_exc.LoaderStrategyException( 

1257 cls, 

1258 requesting_property, 

1259 intended_property_type, 

1260 actual_strategy, 

1261 key, 

1262 ) 

1263 

1264 

1265class ORMOption(ExecutableOption): 

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

1267 

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

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

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

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

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

1273 time. 

1274 

1275 .. versionadded:: 1.4 

1276 

1277 """ 

1278 

1279 __slots__ = () 

1280 

1281 _is_legacy_option = False 

1282 

1283 propagate_to_loaders = False 

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

1285 to "secondary" SELECT statements that occur for relationship 

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

1287 

1288 """ 

1289 

1290 _is_core = False 

1291 

1292 _is_user_defined = False 

1293 

1294 _is_compile_state = False 

1295 

1296 _is_criteria_option = False 

1297 

1298 _is_strategy_option = False 

1299 

1300 def _adapt_cached_option_to_uncached_option( 

1301 self, context: QueryContext, uncached_opt: ORMOption 

1302 ) -> ORMOption: 

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

1304 loader strategy context. 

1305 

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

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

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

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

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

1311 entities passed to us from the cached query. 

1312 

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

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

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

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

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

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

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

1320 

1321 This routine could in the future involve 

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

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

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

1325 

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

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

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

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

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

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

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

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

1334 like an md5 key. 

1335 

1336 .. versionadded:: 1.4.41 

1337 

1338 """ 

1339 if uncached_opt is not None: 

1340 return uncached_opt 

1341 else: 

1342 return self 

1343 

1344 

1345class CompileStateOption(HasCacheKey, ORMOption): 

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

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

1348 

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

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

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

1352 interact with ORM compilation internals or caching. 

1353 

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

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

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

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

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

1359 structures. 

1360 

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

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

1363 

1364 .. versionadded:: 1.4.28 

1365 

1366 

1367 """ 

1368 

1369 __slots__ = () 

1370 

1371 _is_compile_state = True 

1372 

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

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

1375 

1376 This method is part of the implementation of a particular 

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

1378 when an ORM query is compiled. 

1379 

1380 """ 

1381 

1382 def process_compile_state_replaced_entities( 

1383 self, 

1384 compile_state: _ORMCompileState, 

1385 mapper_entities: Sequence[_MapperEntity], 

1386 ) -> None: 

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

1388 given entities that were replaced by with_only_columns() or 

1389 with_entities(). 

1390 

1391 This method is part of the implementation of a particular 

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

1393 when an ORM query is compiled. 

1394 

1395 .. versionadded:: 1.4.19 

1396 

1397 """ 

1398 

1399 

1400class LoaderOption(CompileStateOption): 

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

1402 

1403 .. versionadded:: 1.4 

1404 

1405 """ 

1406 

1407 __slots__ = () 

1408 

1409 def process_compile_state_replaced_entities( 

1410 self, 

1411 compile_state: _ORMCompileState, 

1412 mapper_entities: Sequence[_MapperEntity], 

1413 ) -> None: 

1414 self.process_compile_state(compile_state) 

1415 

1416 

1417class CriteriaOption(CompileStateOption): 

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

1419 compilation time. 

1420 

1421 .. versionadded:: 1.4 

1422 

1423 """ 

1424 

1425 __slots__ = () 

1426 

1427 _is_criteria_option = True 

1428 

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

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

1431 attributes dictionary. 

1432 

1433 """ 

1434 

1435 

1436class UserDefinedOption(ORMOption): 

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

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

1439 

1440 """ 

1441 

1442 __slots__ = ("payload",) 

1443 

1444 _is_legacy_option = False 

1445 

1446 _is_user_defined = True 

1447 

1448 propagate_to_loaders = False 

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

1450 to "secondary" Query objects produced during lazy loads 

1451 or refresh operations. 

1452 

1453 """ 

1454 

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

1456 self.payload = payload 

1457 

1458 

1459@util.deprecated_cls( 

1460 "1.4", 

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

1462 "in a future release. For " 

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

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

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

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

1467 constructor=None, 

1468) 

1469class MapperOption(ORMOption): 

1470 """Describe a modification to a Query""" 

1471 

1472 __slots__ = () 

1473 

1474 _is_legacy_option = True 

1475 

1476 propagate_to_loaders = False 

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

1478 to "secondary" Query objects produced during lazy loads 

1479 or refresh operations. 

1480 

1481 """ 

1482 

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

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

1485 

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

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

1488 apply to the given query. 

1489 

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

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

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

1493 specify propagate_to_loaders=True. 

1494 

1495 """ 

1496 

1497 self.process_query(query) 

1498 

1499 

1500class LoaderStrategy: 

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

1502 

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

1504 ways: 

1505 

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

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

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

1509 off a select operation when the attribute is first accessed 

1510 (i.e. a lazy load) 

1511 

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

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

1514 For example, simple column attributes will add their represented 

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

1516 may establish join clauses to add to the statement. 

1517 

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

1519 These "row processor" functions populate a particular attribute 

1520 on a particular mapped instance. 

1521 

1522 """ 

1523 

1524 __slots__ = ( 

1525 "parent_property", 

1526 "is_class_level", 

1527 "parent", 

1528 "key", 

1529 "strategy_key", 

1530 "strategy_opts", 

1531 ) 

1532 

1533 _strategy_keys: ClassVar[List[_StrategyKey]] 

1534 

1535 def __init__( 

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

1537 ): 

1538 self.parent_property = parent 

1539 self.is_class_level = False 

1540 self.parent = self.parent_property.parent 

1541 self.key = self.parent_property.key 

1542 self.strategy_key = strategy_key 

1543 self.strategy_opts = dict(strategy_key) 

1544 

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

1546 pass 

1547 

1548 def setup_query( 

1549 self, 

1550 compile_state: _ORMCompileState, 

1551 query_entity: _MapperEntity, 

1552 path: _AbstractEntityRegistry, 

1553 loadopt: Optional[_LoadElement], 

1554 adapter: Optional[ORMAdapter], 

1555 **kwargs: Any, 

1556 ) -> None: 

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

1558 

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

1560 

1561 StrategizedProperty delegates its setup() method 

1562 directly to this method. 

1563 

1564 """ 

1565 

1566 def create_row_processor( 

1567 self, 

1568 context: _ORMCompileState, 

1569 query_entity: _MapperEntity, 

1570 path: _AbstractEntityRegistry, 

1571 loadopt: Optional[_LoadElement], 

1572 mapper: Mapper[Any], 

1573 result: Result[Unpack[TupleAny]], 

1574 adapter: Optional[ORMAdapter], 

1575 populators: _PopulatorDict, 

1576 ) -> None: 

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

1578 

1579 This method fulfills the contract specified by 

1580 MapperProperty.create_row_processor(). 

1581 

1582 StrategizedProperty delegates its create_row_processor() method 

1583 directly to this method. 

1584 

1585 """ 

1586 

1587 def __str__(self) -> str: 

1588 return str(self.parent_property)