1# orm/mapper.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# mypy: allow-untyped-defs, allow-untyped-calls 
    8 
    9"""Logic to map Python classes to and from selectables. 
    10 
    11Defines the :class:`~sqlalchemy.orm.mapper.Mapper` class, the central 
    12configurational unit which associates a class with a database table. 
    13 
    14This is a semi-private module; the main configurational API of the ORM is 
    15available in :class:`~sqlalchemy.orm.`. 
    16 
    17""" 
    18from __future__ import annotations 
    19 
    20from collections import deque 
    21from functools import reduce 
    22from itertools import chain 
    23import sys 
    24import threading 
    25from typing import Any 
    26from typing import Callable 
    27from typing import cast 
    28from typing import Collection 
    29from typing import Deque 
    30from typing import Dict 
    31from typing import FrozenSet 
    32from typing import Generic 
    33from typing import Iterable 
    34from typing import Iterator 
    35from typing import List 
    36from typing import Literal 
    37from typing import Mapping 
    38from typing import Optional 
    39from typing import Sequence 
    40from typing import Set 
    41from typing import Tuple 
    42from typing import Type 
    43from typing import TYPE_CHECKING 
    44from typing import TypeVar 
    45from typing import Union 
    46import weakref 
    47 
    48from . import attributes 
    49from . import exc as orm_exc 
    50from . import instrumentation 
    51from . import loading 
    52from . import properties 
    53from . import util as orm_util 
    54from ._typing import _O 
    55from .base import _class_to_mapper 
    56from .base import _parse_mapper_argument 
    57from .base import _state_mapper 
    58from .base import PassiveFlag 
    59from .base import state_str 
    60from .interfaces import _MappedAttribute 
    61from .interfaces import EXT_SKIP 
    62from .interfaces import InspectionAttr 
    63from .interfaces import MapperProperty 
    64from .interfaces import ORMEntityColumnsClauseRole 
    65from .interfaces import ORMFromClauseRole 
    66from .interfaces import StrategizedProperty 
    67from .path_registry import PathRegistry 
    68from .. import event 
    69from .. import exc as sa_exc 
    70from .. import inspection 
    71from .. import log 
    72from .. import schema 
    73from .. import sql 
    74from .. import util 
    75from ..event import dispatcher 
    76from ..event import EventTarget 
    77from ..sql import base as sql_base 
    78from ..sql import coercions 
    79from ..sql import expression 
    80from ..sql import operators 
    81from ..sql import roles 
    82from ..sql import TableClause 
    83from ..sql import util as sql_util 
    84from ..sql import visitors 
    85from ..sql.cache_key import MemoizedHasCacheKey 
    86from ..sql.elements import KeyedColumnElement 
    87from ..sql.schema import Column 
    88from ..sql.schema import Table 
    89from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 
    90from ..util import HasMemoized 
    91from ..util import HasMemoized_ro_memoized_attribute 
    92from ..util.typing import TupleAny 
    93from ..util.typing import Unpack 
    94 
    95if TYPE_CHECKING: 
    96    from ._typing import _IdentityKeyType 
    97    from ._typing import _InstanceDict 
    98    from ._typing import _ORMColumnExprArgument 
    99    from ._typing import _RegistryType 
    100    from .decl_api import registry 
    101    from .dependency import _DependencyProcessor 
    102    from .descriptor_props import CompositeProperty 
    103    from .descriptor_props import SynonymProperty 
    104    from .events import MapperEvents 
    105    from .instrumentation import ClassManager 
    106    from .path_registry import _CachingEntityRegistry 
    107    from .properties import ColumnProperty 
    108    from .relationships import RelationshipProperty 
    109    from .state import InstanceState 
    110    from .util import ORMAdapter 
    111    from ..engine import Row 
    112    from ..engine import RowMapping 
    113    from ..sql._typing import _ColumnExpressionArgument 
    114    from ..sql._typing import _EquivalentColumnMap 
    115    from ..sql.base import _EntityNamespace 
    116    from ..sql.base import ReadOnlyColumnCollection 
    117    from ..sql.elements import ColumnClause 
    118    from ..sql.elements import ColumnElement 
    119    from ..sql.selectable import FromClause 
    120    from ..util import OrderedSet 
    121 
    122 
    123_T = TypeVar("_T", bound=Any) 
    124_MP = TypeVar("_MP", bound="MapperProperty[Any]") 
    125_Fn = TypeVar("_Fn", bound="Callable[..., Any]") 
    126 
    127 
    128_WithPolymorphicArg = Union[ 
    129    Literal["*"], 
    130    Tuple[ 
    131        Union[Literal["*"], Sequence[Union["Mapper[Any]", Type[Any]]]], 
    132        Optional["FromClause"], 
    133    ], 
    134    Sequence[Union["Mapper[Any]", Type[Any]]], 
    135] 
    136 
    137 
    138_mapper_registries: weakref.WeakKeyDictionary[_RegistryType, bool] = ( 
    139    weakref.WeakKeyDictionary() 
    140) 
    141 
    142 
    143def _all_registries() -> Set[registry]: 
    144    with _CONFIGURE_MUTEX: 
    145        return set(_mapper_registries) 
    146 
    147 
    148def _unconfigured_mappers() -> Iterator[Mapper[Any]]: 
    149    for reg in _all_registries(): 
    150        yield from reg._mappers_to_configure() 
    151 
    152 
    153_already_compiling = False 
    154 
    155 
    156# a constant returned by _get_attr_by_column to indicate 
    157# this mapper is not handling an attribute for a particular 
    158# column 
    159NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 
    160 
    161# lock used to synchronize the "mapper configure" step 
    162_CONFIGURE_MUTEX = threading.RLock() 
    163 
    164 
    165@inspection._self_inspects 
    166@log.class_logger 
    167class Mapper( 
    168    ORMFromClauseRole, 
    169    ORMEntityColumnsClauseRole[_O], 
    170    MemoizedHasCacheKey, 
    171    InspectionAttr, 
    172    log.Identified, 
    173    inspection.Inspectable["Mapper[_O]"], 
    174    EventTarget, 
    175    Generic[_O], 
    176): 
    177    """Defines an association between a Python class and a database table or 
    178    other relational structure, so that ORM operations against the class may 
    179    proceed. 
    180 
    181    The :class:`_orm.Mapper` object is instantiated using mapping methods 
    182    present on the :class:`_orm.registry` object.  For information 
    183    about instantiating new :class:`_orm.Mapper` objects, see 
    184    :ref:`orm_mapping_classes_toplevel`. 
    185 
    186    """ 
    187 
    188    dispatch: dispatcher[Mapper[_O]] 
    189 
    190    _dispose_called = False 
    191    _configure_failed: Any = False 
    192    _ready_for_configure = False 
    193 
    194    def __init__( 
    195        self, 
    196        class_: Type[_O], 
    197        local_table: Optional[FromClause] = None, 
    198        properties: Optional[Mapping[str, MapperProperty[Any]]] = None, 
    199        primary_key: Optional[Iterable[_ORMColumnExprArgument[Any]]] = None, 
    200        inherits: Optional[Union[Mapper[Any], Type[Any]]] = None, 
    201        inherit_condition: Optional[_ColumnExpressionArgument[bool]] = None, 
    202        inherit_foreign_keys: Optional[ 
    203            Sequence[_ORMColumnExprArgument[Any]] 
    204        ] = None, 
    205        always_refresh: bool = False, 
    206        version_id_col: Optional[_ORMColumnExprArgument[Any]] = None, 
    207        version_id_generator: Optional[ 
    208            Union[Literal[False], Callable[[Any], Any]] 
    209        ] = None, 
    210        polymorphic_on: Optional[ 
    211            Union[_ORMColumnExprArgument[Any], str, MapperProperty[Any]] 
    212        ] = None, 
    213        _polymorphic_map: Optional[Dict[Any, Mapper[Any]]] = None, 
    214        polymorphic_identity: Optional[Any] = None, 
    215        concrete: bool = False, 
    216        with_polymorphic: Optional[_WithPolymorphicArg] = None, 
    217        polymorphic_abstract: bool = False, 
    218        polymorphic_load: Optional[Literal["selectin", "inline"]] = None, 
    219        allow_partial_pks: bool = True, 
    220        batch: bool = True, 
    221        column_prefix: Optional[str] = None, 
    222        include_properties: Optional[Sequence[str]] = None, 
    223        exclude_properties: Optional[Sequence[str]] = None, 
    224        passive_updates: bool = True, 
    225        passive_deletes: bool = False, 
    226        confirm_deleted_rows: bool = True, 
    227        eager_defaults: Literal[True, False, "auto"] = "auto", 
    228        legacy_is_orphan: bool = False, 
    229        _compiled_cache_size: int = 100, 
    230    ): 
    231        r"""Direct constructor for a new :class:`_orm.Mapper` object. 
    232 
    233        The :class:`_orm.Mapper` constructor is not called directly, and 
    234        is normally invoked through the 
    235        use of the :class:`_orm.registry` object through either the 
    236        :ref:`Declarative <orm_declarative_mapping>` or 
    237        :ref:`Imperative <orm_imperative_mapping>` mapping styles. 
    238 
    239        .. versionchanged:: 2.0 The public facing ``mapper()`` function is 
    240           removed; for a classical mapping configuration, use the 
    241           :meth:`_orm.registry.map_imperatively` method. 
    242 
    243        Parameters documented below may be passed to either the 
    244        :meth:`_orm.registry.map_imperatively` method, or may be passed in the 
    245        ``__mapper_args__`` declarative class attribute described at 
    246        :ref:`orm_declarative_mapper_options`. 
    247 
    248        :param class\_: The class to be mapped.  When using Declarative, 
    249          this argument is automatically passed as the declared class 
    250          itself. 
    251 
    252        :param local_table: The :class:`_schema.Table` or other 
    253           :class:`_sql.FromClause` (i.e. selectable) to which the class is 
    254           mapped. May be ``None`` if this mapper inherits from another mapper 
    255           using single-table inheritance. When using Declarative, this 
    256           argument is automatically passed by the extension, based on what is 
    257           configured via the :attr:`_orm.DeclarativeBase.__table__` attribute 
    258           or via the :class:`_schema.Table` produced as a result of 
    259           the :attr:`_orm.DeclarativeBase.__tablename__` attribute being 
    260           present. 
    261 
    262        :param polymorphic_abstract: Indicates this class will be mapped in a 
    263            polymorphic hierarchy, but not directly instantiated. The class is 
    264            mapped normally, except that it has no requirement for a 
    265            :paramref:`_orm.Mapper.polymorphic_identity` within an inheritance 
    266            hierarchy. The class however must be part of a polymorphic 
    267            inheritance scheme which uses 
    268            :paramref:`_orm.Mapper.polymorphic_on` at the base. 
    269 
    270            .. versionadded:: 2.0 
    271 
    272            .. seealso:: 
    273 
    274                :ref:`orm_inheritance_abstract_poly` 
    275 
    276        :param always_refresh: If True, all query operations for this mapped 
    277           class will overwrite all data within object instances that already 
    278           exist within the session, erasing any in-memory changes with 
    279           whatever information was loaded from the database. Usage of this 
    280           flag is highly discouraged; as an alternative, see the method 
    281           :meth:`_query.Query.populate_existing`. 
    282 
    283        :param allow_partial_pks: Defaults to True.  Indicates that a 
    284           composite primary key with some NULL values should be considered as 
    285           possibly existing within the database. This affects whether a 
    286           mapper will assign an incoming row to an existing identity, as well 
    287           as if :meth:`.Session.merge` will check the database first for a 
    288           particular primary key value. A "partial primary key" can occur if 
    289           one has mapped to an OUTER JOIN, for example. 
    290 
    291           The :paramref:`.orm.Mapper.allow_partial_pks` parameter also 
    292           indicates to the ORM relationship lazy loader, when loading a 
    293           many-to-one related object, if a composite primary key that has 
    294           partial NULL values should result in an attempt to load from the 
    295           database, or if a load attempt is not necessary. 
    296 
    297           .. versionadded:: 2.0.36 :paramref:`.orm.Mapper.allow_partial_pks` 
    298              is consulted by the relationship lazy loader strategy, such that 
    299              when set to False, a SELECT for a composite primary key that 
    300              has partial NULL values will not be emitted. 
    301 
    302        :param batch: Defaults to ``True``, indicating that save operations 
    303           of multiple entities can be batched together for efficiency. 
    304           Setting to False indicates 
    305           that an instance will be fully saved before saving the next 
    306           instance.  This is used in the extremely rare case that a 
    307           :class:`.MapperEvents` listener requires being called 
    308           in between individual row persistence operations. 
    309 
    310        :param column_prefix: A string which will be prepended 
    311           to the mapped attribute name when :class:`_schema.Column` 
    312           objects are automatically assigned as attributes to the 
    313           mapped class.  Does not affect :class:`.Column` objects that 
    314           are mapped explicitly in the :paramref:`.Mapper.properties` 
    315           dictionary. 
    316 
    317           This parameter is typically useful with imperative mappings 
    318           that keep the :class:`.Table` object separate.  Below, assuming 
    319           the ``user_table`` :class:`.Table` object has columns named 
    320           ``user_id``, ``user_name``, and ``password``:: 
    321 
    322                class User(Base): 
    323                    __table__ = user_table 
    324                    __mapper_args__ = {"column_prefix": "_"} 
    325 
    326           The above mapping will assign the ``user_id``, ``user_name``, and 
    327           ``password`` columns to attributes named ``_user_id``, 
    328           ``_user_name``, and ``_password`` on the mapped ``User`` class. 
    329 
    330           The :paramref:`.Mapper.column_prefix` parameter is uncommon in 
    331           modern use. For dealing with reflected tables, a more flexible 
    332           approach to automating a naming scheme is to intercept the 
    333           :class:`.Column` objects as they are reflected; see the section 
    334           :ref:`mapper_automated_reflection_schemes` for notes on this usage 
    335           pattern. 
    336 
    337        :param concrete: If True, indicates this mapper should use concrete 
    338           table inheritance with its parent mapper. 
    339 
    340           See the section :ref:`concrete_inheritance` for an example. 
    341 
    342        :param confirm_deleted_rows: defaults to True; when a DELETE occurs 
    343          of one more rows based on specific primary keys, a warning is 
    344          emitted when the number of rows matched does not equal the number 
    345          of rows expected.  This parameter may be set to False to handle the 
    346          case where database ON DELETE CASCADE rules may be deleting some of 
    347          those rows automatically.  The warning may be changed to an 
    348          exception in a future release. 
    349 
    350        :param eager_defaults: if True, the ORM will immediately fetch the 
    351          value of server-generated default values after an INSERT or UPDATE, 
    352          rather than leaving them as expired to be fetched on next access. 
    353          This can be used for event schemes where the server-generated values 
    354          are needed immediately before the flush completes. 
    355 
    356          The fetch of values occurs either by using ``RETURNING`` inline 
    357          with the ``INSERT`` or ``UPDATE`` statement, or by adding an 
    358          additional ``SELECT`` statement subsequent to the ``INSERT`` or 
    359          ``UPDATE``, if the backend does not support ``RETURNING``. 
    360 
    361          The use of ``RETURNING`` is extremely performant in particular for 
    362          ``INSERT`` statements where SQLAlchemy can take advantage of 
    363          :ref:`insertmanyvalues <engine_insertmanyvalues>`, whereas the use of 
    364          an additional ``SELECT`` is relatively poor performing, adding 
    365          additional SQL round trips which would be unnecessary if these new 
    366          attributes are not to be accessed in any case. 
    367 
    368          For this reason, :paramref:`.Mapper.eager_defaults` defaults to the 
    369          string value ``"auto"``, which indicates that server defaults for 
    370          INSERT should be fetched using ``RETURNING`` if the backing database 
    371          supports it and if the dialect in use supports "insertmanyreturning" 
    372          for an INSERT statement. If the backing database does not support 
    373          ``RETURNING`` or "insertmanyreturning" is not available, server 
    374          defaults will not be fetched. 
    375 
    376          .. versionchanged:: 2.0.0rc1 added the "auto" option for 
    377             :paramref:`.Mapper.eager_defaults` 
    378 
    379          .. seealso:: 
    380 
    381                :ref:`orm_server_defaults` 
    382 
    383          .. versionchanged:: 2.0.0  RETURNING now works with multiple rows 
    384             INSERTed at once using the 
    385             :ref:`insertmanyvalues <engine_insertmanyvalues>` feature, which 
    386             among other things allows the :paramref:`.Mapper.eager_defaults` 
    387             feature to be very performant on supporting backends. 
    388 
    389        :param exclude_properties: A list or set of string column names to 
    390          be excluded from mapping. 
    391 
    392          .. seealso:: 
    393 
    394            :ref:`include_exclude_cols` 
    395 
    396        :param include_properties: An inclusive list or set of string column 
    397          names to map. 
    398 
    399          .. seealso:: 
    400 
    401            :ref:`include_exclude_cols` 
    402 
    403        :param inherits: A mapped class or the corresponding 
    404          :class:`_orm.Mapper` 
    405          of one indicating a superclass to which this :class:`_orm.Mapper` 
    406          should *inherit* from.   The mapped class here must be a subclass 
    407          of the other mapper's class.   When using Declarative, this argument 
    408          is passed automatically as a result of the natural class 
    409          hierarchy of the declared classes. 
    410 
    411          .. seealso:: 
    412 
    413            :ref:`inheritance_toplevel` 
    414 
    415        :param inherit_condition: For joined table inheritance, a SQL 
    416           expression which will 
    417           define how the two tables are joined; defaults to a natural join 
    418           between the two tables. 
    419 
    420        :param inherit_foreign_keys: When ``inherit_condition`` is used and 
    421           the columns present are missing a :class:`_schema.ForeignKey` 
    422           configuration, this parameter can be used to specify which columns 
    423           are "foreign".  In most cases can be left as ``None``. 
    424 
    425        :param legacy_is_orphan: Boolean, defaults to ``False``. 
    426          When ``True``, specifies that "legacy" orphan consideration 
    427          is to be applied to objects mapped by this mapper, which means 
    428          that a pending (that is, not persistent) object is auto-expunged 
    429          from an owning :class:`.Session` only when it is de-associated 
    430          from *all* parents that specify a ``delete-orphan`` cascade towards 
    431          this mapper.  The new default behavior is that the object is 
    432          auto-expunged when it is de-associated with *any* of its parents 
    433          that specify ``delete-orphan`` cascade.  This behavior is more 
    434          consistent with that of a persistent object, and allows behavior to 
    435          be consistent in more scenarios independently of whether or not an 
    436          orphan object has been flushed yet or not. 
    437 
    438          See the change note and example at :ref:`legacy_is_orphan_addition` 
    439          for more detail on this change. 
    440 
    441        :param passive_deletes: Indicates DELETE behavior of foreign key 
    442           columns when a joined-table inheritance entity is being deleted. 
    443           Defaults to ``False`` for a base mapper; for an inheriting mapper, 
    444           defaults to ``False`` unless the value is set to ``True`` 
    445           on the superclass mapper. 
    446 
    447           When ``True``, it is assumed that ON DELETE CASCADE is configured 
    448           on the foreign key relationships that link this mapper's table 
    449           to its superclass table, so that when the unit of work attempts 
    450           to delete the entity, it need only emit a DELETE statement for the 
    451           superclass table, and not this table. 
    452 
    453           When ``False``, a DELETE statement is emitted for this mapper's 
    454           table individually.  If the primary key attributes local to this 
    455           table are unloaded, then a SELECT must be emitted in order to 
    456           validate these attributes; note that the primary key columns 
    457           of a joined-table subclass are not part of the "primary key" of 
    458           the object as a whole. 
    459 
    460           Note that a value of ``True`` is **always** forced onto the 
    461           subclass mappers; that is, it's not possible for a superclass 
    462           to specify passive_deletes without this taking effect for 
    463           all subclass mappers. 
    464 
    465           .. seealso:: 
    466 
    467               :ref:`passive_deletes` - description of similar feature as 
    468               used with :func:`_orm.relationship` 
    469 
    470               :paramref:`.mapper.passive_updates` - supporting ON UPDATE 
    471               CASCADE for joined-table inheritance mappers 
    472 
    473        :param passive_updates: Indicates UPDATE behavior of foreign key 
    474           columns when a primary key column changes on a joined-table 
    475           inheritance mapping.   Defaults to ``True``. 
    476 
    477           When True, it is assumed that ON UPDATE CASCADE is configured on 
    478           the foreign key in the database, and that the database will handle 
    479           propagation of an UPDATE from a source column to dependent columns 
    480           on joined-table rows. 
    481 
    482           When False, it is assumed that the database does not enforce 
    483           referential integrity and will not be issuing its own CASCADE 
    484           operation for an update.  The unit of work process will 
    485           emit an UPDATE statement for the dependent columns during a 
    486           primary key change. 
    487 
    488           .. seealso:: 
    489 
    490               :ref:`passive_updates` - description of a similar feature as 
    491               used with :func:`_orm.relationship` 
    492 
    493               :paramref:`.mapper.passive_deletes` - supporting ON DELETE 
    494               CASCADE for joined-table inheritance mappers 
    495 
    496        :param polymorphic_load: Specifies "polymorphic loading" behavior 
    497         for a subclass in an inheritance hierarchy (joined and single 
    498         table inheritance only).   Valid values are: 
    499 
    500          * "'inline'" - specifies this class should be part of 
    501            the "with_polymorphic" mappers, e.g. its columns will be included 
    502            in a SELECT query against the base. 
    503 
    504          * "'selectin'" - specifies that when instances of this class 
    505            are loaded, an additional SELECT will be emitted to retrieve 
    506            the columns specific to this subclass.  The SELECT uses 
    507            IN to fetch multiple subclasses at once. 
    508 
    509         .. seealso:: 
    510 
    511            :ref:`with_polymorphic_mapper_config` 
    512 
    513            :ref:`polymorphic_selectin` 
    514 
    515        :param polymorphic_on: Specifies the column, attribute, or 
    516          SQL expression used to determine the target class for an 
    517          incoming row, when inheriting classes are present. 
    518 
    519          May be specified as a string attribute name, or as a SQL 
    520          expression such as a :class:`_schema.Column` or in a Declarative 
    521          mapping a :func:`_orm.mapped_column` object.  It is typically 
    522          expected that the SQL expression corresponds to a column in the 
    523          base-most mapped :class:`.Table`:: 
    524 
    525            class Employee(Base): 
    526                __tablename__ = "employee" 
    527 
    528                id: Mapped[int] = mapped_column(primary_key=True) 
    529                discriminator: Mapped[str] = mapped_column(String(50)) 
    530 
    531                __mapper_args__ = { 
    532                    "polymorphic_on": discriminator, 
    533                    "polymorphic_identity": "employee", 
    534                } 
    535 
    536          It may also be specified 
    537          as a SQL expression, as in this example where we 
    538          use the :func:`.case` construct to provide a conditional 
    539          approach:: 
    540 
    541            class Employee(Base): 
    542                __tablename__ = "employee" 
    543 
    544                id: Mapped[int] = mapped_column(primary_key=True) 
    545                discriminator: Mapped[str] = mapped_column(String(50)) 
    546 
    547                __mapper_args__ = { 
    548                    "polymorphic_on": case( 
    549                        (discriminator == "EN", "engineer"), 
    550                        (discriminator == "MA", "manager"), 
    551                        else_="employee", 
    552                    ), 
    553                    "polymorphic_identity": "employee", 
    554                } 
    555 
    556          It may also refer to any attribute using its string name, 
    557          which is of particular use when using annotated column 
    558          configurations:: 
    559 
    560                class Employee(Base): 
    561                    __tablename__ = "employee" 
    562 
    563                    id: Mapped[int] = mapped_column(primary_key=True) 
    564                    discriminator: Mapped[str] 
    565 
    566                    __mapper_args__ = { 
    567                        "polymorphic_on": "discriminator", 
    568                        "polymorphic_identity": "employee", 
    569                    } 
    570 
    571          When setting ``polymorphic_on`` to reference an 
    572          attribute or expression that's not present in the 
    573          locally mapped :class:`_schema.Table`, yet the value 
    574          of the discriminator should be persisted to the database, 
    575          the value of the 
    576          discriminator is not automatically set on new 
    577          instances; this must be handled by the user, 
    578          either through manual means or via event listeners. 
    579          A typical approach to establishing such a listener 
    580          looks like:: 
    581 
    582                from sqlalchemy import event 
    583                from sqlalchemy.orm import object_mapper 
    584 
    585 
    586                @event.listens_for(Employee, "init", propagate=True) 
    587                def set_identity(instance, *arg, **kw): 
    588                    mapper = object_mapper(instance) 
    589                    instance.discriminator = mapper.polymorphic_identity 
    590 
    591          Where above, we assign the value of ``polymorphic_identity`` 
    592          for the mapped class to the ``discriminator`` attribute, 
    593          thus persisting the value to the ``discriminator`` column 
    594          in the database. 
    595 
    596          .. warning:: 
    597 
    598             Currently, **only one discriminator column may be set**, typically 
    599             on the base-most class in the hierarchy. "Cascading" polymorphic 
    600             columns are not yet supported. 
    601 
    602          .. seealso:: 
    603 
    604            :ref:`inheritance_toplevel` 
    605 
    606        :param polymorphic_identity: Specifies the value which 
    607          identifies this particular class as returned by the column expression 
    608          referred to by the :paramref:`_orm.Mapper.polymorphic_on` setting. As 
    609          rows are received, the value corresponding to the 
    610          :paramref:`_orm.Mapper.polymorphic_on` column expression is compared 
    611          to this value, indicating which subclass should be used for the newly 
    612          reconstructed object. 
    613 
    614          .. seealso:: 
    615 
    616            :ref:`inheritance_toplevel` 
    617 
    618        :param properties: A dictionary mapping the string names of object 
    619           attributes to :class:`.MapperProperty` instances, which define the 
    620           persistence behavior of that attribute.  Note that 
    621           :class:`_schema.Column` 
    622           objects present in 
    623           the mapped :class:`_schema.Table` are automatically placed into 
    624           ``ColumnProperty`` instances upon mapping, unless overridden. 
    625           When using Declarative, this argument is passed automatically, 
    626           based on all those :class:`.MapperProperty` instances declared 
    627           in the declared class body. 
    628 
    629           .. seealso:: 
    630 
    631               :ref:`orm_mapping_properties` - in the 
    632               :ref:`orm_mapping_classes_toplevel` 
    633 
    634        :param primary_key: A list of :class:`_schema.Column` 
    635           objects, or alternatively string names of attribute names which 
    636           refer to :class:`_schema.Column`, which define 
    637           the primary key to be used against this mapper's selectable unit. 
    638           This is normally simply the primary key of the ``local_table``, but 
    639           can be overridden here. 
    640 
    641           .. versionchanged:: 2.0.2 :paramref:`_orm.Mapper.primary_key` 
    642              arguments may be indicated as string attribute names as well. 
    643 
    644           .. seealso:: 
    645 
    646                :ref:`mapper_primary_key` - background and example use 
    647 
    648        :param version_id_col: A :class:`_schema.Column` 
    649           that will be used to keep a running version id of rows 
    650           in the table.  This is used to detect concurrent updates or 
    651           the presence of stale data in a flush.  The methodology is to 
    652           detect if an UPDATE statement does not match the last known 
    653           version id, a 
    654           :class:`~sqlalchemy.orm.exc.StaleDataError` exception is 
    655           thrown. 
    656           By default, the column must be of :class:`.Integer` type, 
    657           unless ``version_id_generator`` specifies an alternative version 
    658           generator. 
    659 
    660           .. seealso:: 
    661 
    662              :ref:`mapper_version_counter` - discussion of version counting 
    663              and rationale. 
    664 
    665        :param version_id_generator: Define how new version ids should 
    666          be generated.  Defaults to ``None``, which indicates that 
    667          a simple integer counting scheme be employed.  To provide a custom 
    668          versioning scheme, provide a callable function of the form:: 
    669 
    670              def generate_version(version): 
    671                  return next_version 
    672 
    673          Alternatively, server-side versioning functions such as triggers, 
    674          or programmatic versioning schemes outside of the version id 
    675          generator may be used, by specifying the value ``False``. 
    676          Please see :ref:`server_side_version_counter` for a discussion 
    677          of important points when using this option. 
    678 
    679          .. seealso:: 
    680 
    681             :ref:`custom_version_counter` 
    682 
    683             :ref:`server_side_version_counter` 
    684 
    685 
    686        :param with_polymorphic: A tuple in the form ``(<classes>, 
    687            <selectable>)`` indicating the default style of "polymorphic" 
    688            loading, that is, which tables are queried at once. <classes> is 
    689            any single or list of mappers and/or classes indicating the 
    690            inherited classes that should be loaded at once. The special value 
    691            ``'*'`` may be used to indicate all descending classes should be 
    692            loaded immediately. The second tuple argument <selectable> 
    693            indicates a selectable that will be used to query for multiple 
    694            classes. 
    695 
    696            The :paramref:`_orm.Mapper.polymorphic_load` parameter may be 
    697            preferable over the use of :paramref:`_orm.Mapper.with_polymorphic` 
    698            in modern mappings to indicate a per-subclass technique of 
    699            indicating polymorphic loading styles. 
    700 
    701            .. seealso:: 
    702 
    703                :ref:`with_polymorphic_mapper_config` 
    704 
    705        """ 
    706        self.class_ = util.assert_arg_type(class_, type, "class_") 
    707        self._sort_key = "%s.%s" % ( 
    708            self.class_.__module__, 
    709            self.class_.__name__, 
    710        ) 
    711 
    712        self._primary_key_argument = util.to_list(primary_key) 
    713 
    714        self.always_refresh = always_refresh 
    715 
    716        if isinstance(version_id_col, MapperProperty): 
    717            self.version_id_prop = version_id_col 
    718            self.version_id_col = None 
    719        else: 
    720            self.version_id_col = ( 
    721                coercions.expect( 
    722                    roles.ColumnArgumentOrKeyRole, 
    723                    version_id_col, 
    724                    argname="version_id_col", 
    725                ) 
    726                if version_id_col is not None 
    727                else None 
    728            ) 
    729 
    730        if version_id_generator is False: 
    731            self.version_id_generator = False 
    732        elif version_id_generator is None: 
    733            self.version_id_generator = lambda x: (x or 0) + 1 
    734        else: 
    735            self.version_id_generator = version_id_generator 
    736 
    737        self.concrete = concrete 
    738        self.single = False 
    739 
    740        if inherits is not None: 
    741            self.inherits = _parse_mapper_argument(inherits) 
    742        else: 
    743            self.inherits = None 
    744 
    745        if local_table is not None: 
    746            self.local_table = coercions.expect( 
    747                roles.FromClauseRole, 
    748                local_table, 
    749                disable_inspection=True, 
    750                argname="local_table", 
    751            ) 
    752        elif self.inherits: 
    753            # note this is a new flow as of 2.0 so that 
    754            # .local_table need not be Optional 
    755            self.local_table = self.inherits.local_table 
    756            self.single = True 
    757        else: 
    758            raise sa_exc.ArgumentError( 
    759                f"Mapper[{self.class_.__name__}(None)] has None for a " 
    760                "primary table argument and does not specify 'inherits'" 
    761            ) 
    762 
    763        if inherit_condition is not None: 
    764            self.inherit_condition = coercions.expect( 
    765                roles.OnClauseRole, inherit_condition 
    766            ) 
    767        else: 
    768            self.inherit_condition = None 
    769 
    770        self.inherit_foreign_keys = inherit_foreign_keys 
    771        self._init_properties = dict(properties) if properties else {} 
    772        self._delete_orphans = [] 
    773        self.batch = batch 
    774        self.eager_defaults = eager_defaults 
    775        self.column_prefix = column_prefix 
    776 
    777        # interim - polymorphic_on is further refined in 
    778        # _configure_polymorphic_setter 
    779        self.polymorphic_on = ( 
    780            coercions.expect(  # type: ignore 
    781                roles.ColumnArgumentOrKeyRole, 
    782                polymorphic_on, 
    783                argname="polymorphic_on", 
    784            ) 
    785            if polymorphic_on is not None 
    786            else None 
    787        ) 
    788        self.polymorphic_abstract = polymorphic_abstract 
    789        self._dependency_processors = [] 
    790        self.validators = util.EMPTY_DICT 
    791        self.passive_updates = passive_updates 
    792        self.passive_deletes = passive_deletes 
    793        self.legacy_is_orphan = legacy_is_orphan 
    794        self._clause_adapter = None 
    795        self._requires_row_aliasing = False 
    796        self._inherits_equated_pairs = None 
    797        self._memoized_values = {} 
    798        self._compiled_cache_size = _compiled_cache_size 
    799        self._reconstructor = None 
    800        self.allow_partial_pks = allow_partial_pks 
    801 
    802        if self.inherits and not self.concrete: 
    803            self.confirm_deleted_rows = False 
    804        else: 
    805            self.confirm_deleted_rows = confirm_deleted_rows 
    806 
    807        self._set_with_polymorphic(with_polymorphic) 
    808        self.polymorphic_load = polymorphic_load 
    809 
    810        # our 'polymorphic identity', a string name that when located in a 
    811        #  result set row indicates this Mapper should be used to construct 
    812        # the object instance for that row. 
    813        self.polymorphic_identity = polymorphic_identity 
    814 
    815        # a dictionary of 'polymorphic identity' names, associating those 
    816        # names with Mappers that will be used to construct object instances 
    817        # upon a select operation. 
    818        if _polymorphic_map is None: 
    819            self.polymorphic_map = {} 
    820        else: 
    821            self.polymorphic_map = _polymorphic_map 
    822 
    823        if include_properties is not None: 
    824            self.include_properties = util.to_set(include_properties) 
    825        else: 
    826            self.include_properties = None 
    827        if exclude_properties: 
    828            self.exclude_properties = util.to_set(exclude_properties) 
    829        else: 
    830            self.exclude_properties = None 
    831 
    832        # prevent this mapper from being constructed 
    833        # while a configure_mappers() is occurring (and defer a 
    834        # configure_mappers() until construction succeeds) 
    835        with _CONFIGURE_MUTEX: 
    836            cast("MapperEvents", self.dispatch._events)._new_mapper_instance( 
    837                class_, self 
    838            ) 
    839            self._configure_inheritance() 
    840            self._configure_class_instrumentation() 
    841            self._configure_properties() 
    842            self._configure_polymorphic_setter() 
    843            self._configure_pks() 
    844            self.registry._flag_new_mapper(self) 
    845            self._log("constructed") 
    846            self._expire_memoizations() 
    847 
    848        self.dispatch.after_mapper_constructed(self, self.class_) 
    849 
    850    def _prefer_eager_defaults(self, dialect, table): 
    851        if self.eager_defaults == "auto": 
    852            if not table.implicit_returning: 
    853                return False 
    854 
    855            return ( 
    856                table in self._server_default_col_keys 
    857                and dialect.insert_executemany_returning 
    858            ) 
    859        else: 
    860            return self.eager_defaults 
    861 
    862    def _gen_cache_key(self, anon_map, bindparams): 
    863        return (self,) 
    864 
    865    # ### BEGIN 
    866    # ATTRIBUTE DECLARATIONS START HERE 
    867 
    868    is_mapper = True 
    869    """Part of the inspection API.""" 
    870 
    871    represents_outer_join = False 
    872 
    873    registry: _RegistryType 
    874 
    875    @property 
    876    def mapper(self) -> Mapper[_O]: 
    877        """Part of the inspection API. 
    878 
    879        Returns self. 
    880 
    881        """ 
    882        return self 
    883 
    884    @property 
    885    def entity(self): 
    886        r"""Part of the inspection API. 
    887 
    888        Returns self.class\_. 
    889 
    890        """ 
    891        return self.class_ 
    892 
    893    class_: Type[_O] 
    894    """The class to which this :class:`_orm.Mapper` is mapped.""" 
    895 
    896    _identity_class: Type[_O] 
    897 
    898    _delete_orphans: List[Tuple[str, Type[Any]]] 
    899    _dependency_processors: List[_DependencyProcessor] 
    900    _memoized_values: Dict[Any, Callable[[], Any]] 
    901    _inheriting_mappers: util.WeakSequence[Mapper[Any]] 
    902    _all_tables: Set[TableClause] 
    903    _polymorphic_attr_key: Optional[str] 
    904 
    905    _pks_by_table: Dict[FromClause, OrderedSet[ColumnClause[Any]]] 
    906    _cols_by_table: Dict[FromClause, OrderedSet[ColumnElement[Any]]] 
    907 
    908    _props: util.OrderedDict[str, MapperProperty[Any]] 
    909    _init_properties: Dict[str, MapperProperty[Any]] 
    910 
    911    _columntoproperty: _ColumnMapping 
    912 
    913    _set_polymorphic_identity: Optional[Callable[[InstanceState[_O]], None]] 
    914    _validate_polymorphic_identity: Optional[ 
    915        Callable[[Mapper[_O], InstanceState[_O], _InstanceDict], None] 
    916    ] 
    917 
    918    tables: Sequence[TableClause] 
    919    """A sequence containing the collection of :class:`_schema.Table` 
    920    or :class:`_schema.TableClause` objects which this :class:`_orm.Mapper` 
    921    is aware of. 
    922 
    923    If the mapper is mapped to a :class:`_expression.Join`, or an 
    924    :class:`_expression.Alias` 
    925    representing a :class:`_expression.Select`, the individual 
    926    :class:`_schema.Table` 
    927    objects that comprise the full construct will be represented here. 
    928 
    929    This is a *read only* attribute determined during mapper construction. 
    930    Behavior is undefined if directly modified. 
    931 
    932    """ 
    933 
    934    validators: util.immutabledict[str, Tuple[str, Dict[str, Any]]] 
    935    """An immutable dictionary of attributes which have been decorated 
    936    using the :func:`_orm.validates` decorator. 
    937 
    938    The dictionary contains string attribute names as keys 
    939    mapped to the actual validation method. 
    940 
    941    """ 
    942 
    943    always_refresh: bool 
    944    allow_partial_pks: bool 
    945    version_id_col: Optional[ColumnElement[Any]] 
    946 
    947    with_polymorphic: Optional[ 
    948        Tuple[ 
    949            Union[Literal["*"], Sequence[Union[Mapper[Any], Type[Any]]]], 
    950            Optional[FromClause], 
    951        ] 
    952    ] 
    953 
    954    version_id_generator: Optional[Union[Literal[False], Callable[[Any], Any]]] 
    955 
    956    local_table: FromClause 
    957    """The immediate :class:`_expression.FromClause` to which this 
    958    :class:`_orm.Mapper` refers. 
    959 
    960    Typically is an instance of :class:`_schema.Table`, may be any 
    961    :class:`.FromClause`. 
    962 
    963    The "local" table is the 
    964    selectable that the :class:`_orm.Mapper` is directly responsible for 
    965    managing from an attribute access and flush perspective.   For 
    966    non-inheriting mappers, :attr:`.Mapper.local_table` will be the same 
    967    as :attr:`.Mapper.persist_selectable`.  For inheriting mappers, 
    968    :attr:`.Mapper.local_table` refers to the specific portion of 
    969    :attr:`.Mapper.persist_selectable` that includes the columns to which 
    970    this :class:`.Mapper` is loading/persisting, such as a particular 
    971    :class:`.Table` within a join. 
    972 
    973    .. seealso:: 
    974 
    975        :attr:`_orm.Mapper.persist_selectable`. 
    976 
    977        :attr:`_orm.Mapper.selectable`. 
    978 
    979    """ 
    980 
    981    persist_selectable: FromClause 
    982    """The :class:`_expression.FromClause` to which this :class:`_orm.Mapper` 
    983    is mapped. 
    984 
    985    Typically is an instance of :class:`_schema.Table`, may be any 
    986    :class:`.FromClause`. 
    987 
    988    The :attr:`_orm.Mapper.persist_selectable` is similar to 
    989    :attr:`.Mapper.local_table`, but represents the :class:`.FromClause` that 
    990    represents the inheriting class hierarchy overall in an inheritance 
    991    scenario. 
    992 
    993    :attr.`.Mapper.persist_selectable` is also separate from the 
    994    :attr:`.Mapper.selectable` attribute, the latter of which may be an 
    995    alternate subquery used for selecting columns. 
    996    :attr.`.Mapper.persist_selectable` is oriented towards columns that 
    997    will be written on a persist operation. 
    998 
    999    .. seealso:: 
    1000 
    1001        :attr:`_orm.Mapper.selectable`. 
    1002 
    1003        :attr:`_orm.Mapper.local_table`. 
    1004 
    1005    """ 
    1006 
    1007    inherits: Optional[Mapper[Any]] 
    1008    """References the :class:`_orm.Mapper` which this :class:`_orm.Mapper` 
    1009    inherits from, if any. 
    1010 
    1011    """ 
    1012 
    1013    inherit_condition: Optional[ColumnElement[bool]] 
    1014 
    1015    configured: bool = False 
    1016    """Represent ``True`` if this :class:`_orm.Mapper` has been configured. 
    1017 
    1018    This is a *read only* attribute determined during mapper construction. 
    1019    Behavior is undefined if directly modified. 
    1020 
    1021    .. seealso:: 
    1022 
    1023        :func:`.configure_mappers`. 
    1024 
    1025    """ 
    1026 
    1027    concrete: bool 
    1028    """Represent ``True`` if this :class:`_orm.Mapper` is a concrete 
    1029    inheritance mapper. 
    1030 
    1031    This is a *read only* attribute determined during mapper construction. 
    1032    Behavior is undefined if directly modified. 
    1033 
    1034    """ 
    1035 
    1036    primary_key: Tuple[ColumnElement[Any], ...] 
    1037    """An iterable containing the collection of :class:`_schema.Column` 
    1038    objects 
    1039    which comprise the 'primary key' of the mapped table, from the 
    1040    perspective of this :class:`_orm.Mapper`. 
    1041 
    1042    This list is against the selectable in 
    1043    :attr:`_orm.Mapper.persist_selectable`. 
    1044    In the case of inheriting mappers, some columns may be managed by a 
    1045    superclass mapper.  For example, in the case of a 
    1046    :class:`_expression.Join`, the 
    1047    primary key is determined by all of the primary key columns across all 
    1048    tables referenced by the :class:`_expression.Join`. 
    1049 
    1050    The list is also not necessarily the same as the primary key column 
    1051    collection associated with the underlying tables; the :class:`_orm.Mapper` 
    1052    features a ``primary_key`` argument that can override what the 
    1053    :class:`_orm.Mapper` considers as primary key columns. 
    1054 
    1055    This is a *read only* attribute determined during mapper construction. 
    1056    Behavior is undefined if directly modified. 
    1057 
    1058    """ 
    1059 
    1060    class_manager: ClassManager[_O] 
    1061    """The :class:`.ClassManager` which maintains event listeners 
    1062    and class-bound descriptors for this :class:`_orm.Mapper`. 
    1063 
    1064    This is a *read only* attribute determined during mapper construction. 
    1065    Behavior is undefined if directly modified. 
    1066 
    1067    """ 
    1068 
    1069    single: bool 
    1070    """Represent ``True`` if this :class:`_orm.Mapper` is a single table 
    1071    inheritance mapper. 
    1072 
    1073    :attr:`_orm.Mapper.local_table` will be ``None`` if this flag is set. 
    1074 
    1075    This is a *read only* attribute determined during mapper construction. 
    1076    Behavior is undefined if directly modified. 
    1077 
    1078    """ 
    1079 
    1080    polymorphic_on: Optional[KeyedColumnElement[Any]] 
    1081    """The :class:`_schema.Column` or SQL expression specified as the 
    1082    ``polymorphic_on`` argument 
    1083    for this :class:`_orm.Mapper`, within an inheritance scenario. 
    1084 
    1085    This attribute is normally a :class:`_schema.Column` instance but 
    1086    may also be an expression, such as one derived from 
    1087    :func:`.cast`. 
    1088 
    1089    This is a *read only* attribute determined during mapper construction. 
    1090    Behavior is undefined if directly modified. 
    1091 
    1092    """ 
    1093 
    1094    polymorphic_map: Dict[Any, Mapper[Any]] 
    1095    """A mapping of "polymorphic identity" identifiers mapped to 
    1096    :class:`_orm.Mapper` instances, within an inheritance scenario. 
    1097 
    1098    The identifiers can be of any type which is comparable to the 
    1099    type of column represented by :attr:`_orm.Mapper.polymorphic_on`. 
    1100 
    1101    An inheritance chain of mappers will all reference the same 
    1102    polymorphic map object.  The object is used to correlate incoming 
    1103    result rows to target mappers. 
    1104 
    1105    This is a *read only* attribute determined during mapper construction. 
    1106    Behavior is undefined if directly modified. 
    1107 
    1108    """ 
    1109 
    1110    polymorphic_identity: Optional[Any] 
    1111    """Represent an identifier which is matched against the 
    1112    :attr:`_orm.Mapper.polymorphic_on` column during result row loading. 
    1113 
    1114    Used only with inheritance, this object can be of any type which is 
    1115    comparable to the type of column represented by 
    1116    :attr:`_orm.Mapper.polymorphic_on`. 
    1117 
    1118    This is a *read only* attribute determined during mapper construction. 
    1119    Behavior is undefined if directly modified. 
    1120 
    1121    """ 
    1122 
    1123    base_mapper: Mapper[Any] 
    1124    """The base-most :class:`_orm.Mapper` in an inheritance chain. 
    1125 
    1126    In a non-inheriting scenario, this attribute will always be this 
    1127    :class:`_orm.Mapper`.   In an inheritance scenario, it references 
    1128    the :class:`_orm.Mapper` which is parent to all other :class:`_orm.Mapper` 
    1129    objects in the inheritance chain. 
    1130 
    1131    This is a *read only* attribute determined during mapper construction. 
    1132    Behavior is undefined if directly modified. 
    1133 
    1134    """ 
    1135 
    1136    columns: ReadOnlyColumnCollection[str, Column[Any]] 
    1137    """A collection of :class:`_schema.Column` or other scalar expression 
    1138    objects maintained by this :class:`_orm.Mapper`. 
    1139 
    1140    The collection behaves the same as that of the ``c`` attribute on 
    1141    any :class:`_schema.Table` object, 
    1142    except that only those columns included in 
    1143    this mapping are present, and are keyed based on the attribute name 
    1144    defined in the mapping, not necessarily the ``key`` attribute of the 
    1145    :class:`_schema.Column` itself.   Additionally, scalar expressions mapped 
    1146    by :func:`.column_property` are also present here. 
    1147 
    1148    This is a *read only* attribute determined during mapper construction. 
    1149    Behavior is undefined if directly modified. 
    1150 
    1151    """ 
    1152 
    1153    c: ReadOnlyColumnCollection[str, Column[Any]] 
    1154    """A synonym for :attr:`_orm.Mapper.columns`.""" 
    1155 
    1156    @util.memoized_property 
    1157    def _path_registry(self) -> _CachingEntityRegistry: 
    1158        return PathRegistry.per_mapper(self) 
    1159 
    1160    def _configure_inheritance(self): 
    1161        """Configure settings related to inheriting and/or inherited mappers 
    1162        being present.""" 
    1163 
    1164        # a set of all mappers which inherit from this one. 
    1165        self._inheriting_mappers = util.WeakSequence() 
    1166 
    1167        if self.inherits: 
    1168            if not issubclass(self.class_, self.inherits.class_): 
    1169                raise sa_exc.ArgumentError( 
    1170                    "Class '%s' does not inherit from '%s'" 
    1171                    % (self.class_.__name__, self.inherits.class_.__name__) 
    1172                ) 
    1173 
    1174            self.dispatch._update(self.inherits.dispatch) 
    1175 
    1176            if self.single: 
    1177                self.persist_selectable = self.inherits.persist_selectable 
    1178            elif self.local_table is not self.inherits.local_table: 
    1179                if self.concrete: 
    1180                    self.persist_selectable = self.local_table 
    1181                    for mapper in self.iterate_to_root(): 
    1182                        if mapper.polymorphic_on is not None: 
    1183                            mapper._requires_row_aliasing = True 
    1184                else: 
    1185                    if self.inherit_condition is None: 
    1186                        # figure out inherit condition from our table to the 
    1187                        # immediate table of the inherited mapper, not its 
    1188                        # full table which could pull in other stuff we don't 
    1189                        # want (allows test/inheritance.InheritTest4 to pass) 
    1190                        try: 
    1191                            self.inherit_condition = sql_util.join_condition( 
    1192                                self.inherits.local_table, self.local_table 
    1193                            ) 
    1194                        except sa_exc.NoForeignKeysError as nfe: 
    1195                            assert self.inherits.local_table is not None 
    1196                            assert self.local_table is not None 
    1197                            raise sa_exc.NoForeignKeysError( 
    1198                                "Can't determine the inherit condition " 
    1199                                "between inherited table '%s' and " 
    1200                                "inheriting " 
    1201                                "table '%s'; tables have no " 
    1202                                "foreign key relationships established.  " 
    1203                                "Please ensure the inheriting table has " 
    1204                                "a foreign key relationship to the " 
    1205                                "inherited " 
    1206                                "table, or provide an " 
    1207                                "'on clause' using " 
    1208                                "the 'inherit_condition' mapper argument." 
    1209                                % ( 
    1210                                    self.inherits.local_table.description, 
    1211                                    self.local_table.description, 
    1212                                ) 
    1213                            ) from nfe 
    1214                        except sa_exc.AmbiguousForeignKeysError as afe: 
    1215                            assert self.inherits.local_table is not None 
    1216                            assert self.local_table is not None 
    1217                            raise sa_exc.AmbiguousForeignKeysError( 
    1218                                "Can't determine the inherit condition " 
    1219                                "between inherited table '%s' and " 
    1220                                "inheriting " 
    1221                                "table '%s'; tables have more than one " 
    1222                                "foreign key relationship established.  " 
    1223                                "Please specify the 'on clause' using " 
    1224                                "the 'inherit_condition' mapper argument." 
    1225                                % ( 
    1226                                    self.inherits.local_table.description, 
    1227                                    self.local_table.description, 
    1228                                ) 
    1229                            ) from afe 
    1230                    assert self.inherits.persist_selectable is not None 
    1231                    self.persist_selectable = sql.join( 
    1232                        self.inherits.persist_selectable, 
    1233                        self.local_table, 
    1234                        self.inherit_condition, 
    1235                    ) 
    1236 
    1237                    fks = util.to_set(self.inherit_foreign_keys) 
    1238                    self._inherits_equated_pairs = sql_util.criterion_as_pairs( 
    1239                        self.persist_selectable.onclause, 
    1240                        consider_as_foreign_keys=fks, 
    1241                    ) 
    1242            else: 
    1243                self.persist_selectable = self.local_table 
    1244 
    1245            if self.polymorphic_identity is None: 
    1246                self._identity_class = self.class_ 
    1247 
    1248                if ( 
    1249                    not self.polymorphic_abstract 
    1250                    and self.inherits.base_mapper.polymorphic_on is not None 
    1251                ): 
    1252                    util.warn( 
    1253                        f"{self} does not indicate a 'polymorphic_identity', " 
    1254                        "yet is part of an inheritance hierarchy that has a " 
    1255                        f"'polymorphic_on' column of " 
    1256                        f"'{self.inherits.base_mapper.polymorphic_on}'. " 
    1257                        "If this is an intermediary class that should not be " 
    1258                        "instantiated, the class may either be left unmapped, " 
    1259                        "or may include the 'polymorphic_abstract=True' " 
    1260                        "parameter in its Mapper arguments. To leave the " 
    1261                        "class unmapped when using Declarative, set the " 
    1262                        "'__abstract__ = True' attribute on the class." 
    1263                    ) 
    1264            elif self.concrete: 
    1265                self._identity_class = self.class_ 
    1266            else: 
    1267                self._identity_class = self.inherits._identity_class 
    1268 
    1269            if self.version_id_col is None: 
    1270                self.version_id_col = self.inherits.version_id_col 
    1271                self.version_id_generator = self.inherits.version_id_generator 
    1272            elif ( 
    1273                self.inherits.version_id_col is not None 
    1274                and self.version_id_col is not self.inherits.version_id_col 
    1275            ): 
    1276                util.warn( 
    1277                    "Inheriting version_id_col '%s' does not match inherited " 
    1278                    "version_id_col '%s' and will not automatically populate " 
    1279                    "the inherited versioning column. " 
    1280                    "version_id_col should only be specified on " 
    1281                    "the base-most mapper that includes versioning." 
    1282                    % ( 
    1283                        self.version_id_col.description, 
    1284                        self.inherits.version_id_col.description, 
    1285                    ) 
    1286                ) 
    1287 
    1288            self.polymorphic_map = self.inherits.polymorphic_map 
    1289            self.batch = self.inherits.batch 
    1290            self.inherits._inheriting_mappers.append(self) 
    1291            self.base_mapper = self.inherits.base_mapper 
    1292            self.passive_updates = self.inherits.passive_updates 
    1293            self.passive_deletes = ( 
    1294                self.inherits.passive_deletes or self.passive_deletes 
    1295            ) 
    1296            self._all_tables = self.inherits._all_tables 
    1297 
    1298            if self.polymorphic_identity is not None: 
    1299                if self.polymorphic_identity in self.polymorphic_map: 
    1300                    util.warn( 
    1301                        "Reassigning polymorphic association for identity %r " 
    1302                        "from %r to %r: Check for duplicate use of %r as " 
    1303                        "value for polymorphic_identity." 
    1304                        % ( 
    1305                            self.polymorphic_identity, 
    1306                            self.polymorphic_map[self.polymorphic_identity], 
    1307                            self, 
    1308                            self.polymorphic_identity, 
    1309                        ) 
    1310                    ) 
    1311                self.polymorphic_map[self.polymorphic_identity] = self 
    1312 
    1313            if self.polymorphic_load and self.concrete: 
    1314                raise sa_exc.ArgumentError( 
    1315                    "polymorphic_load is not currently supported " 
    1316                    "with concrete table inheritance" 
    1317                ) 
    1318            if self.polymorphic_load == "inline": 
    1319                self.inherits._add_with_polymorphic_subclass(self) 
    1320            elif self.polymorphic_load == "selectin": 
    1321                pass 
    1322            elif self.polymorphic_load is not None: 
    1323                raise sa_exc.ArgumentError( 
    1324                    "unknown argument for polymorphic_load: %r" 
    1325                    % self.polymorphic_load 
    1326                ) 
    1327 
    1328        else: 
    1329            self._all_tables = set() 
    1330            self.base_mapper = self 
    1331            assert self.local_table is not None 
    1332            self.persist_selectable = self.local_table 
    1333            if self.polymorphic_identity is not None: 
    1334                self.polymorphic_map[self.polymorphic_identity] = self 
    1335            self._identity_class = self.class_ 
    1336 
    1337        if self.persist_selectable is None: 
    1338            raise sa_exc.ArgumentError( 
    1339                "Mapper '%s' does not have a persist_selectable specified." 
    1340                % self 
    1341            ) 
    1342 
    1343    def _set_with_polymorphic( 
    1344        self, with_polymorphic: Optional[_WithPolymorphicArg] 
    1345    ) -> None: 
    1346        if with_polymorphic == "*": 
    1347            self.with_polymorphic = ("*", None) 
    1348        elif isinstance(with_polymorphic, (tuple, list)): 
    1349            if isinstance(with_polymorphic[0], (str, tuple, list)): 
    1350                self.with_polymorphic = cast( 
    1351                    """Tuple[ 
    1352                        Union[ 
    1353                            Literal["*"], 
    1354                            Sequence[Union["Mapper[Any]", Type[Any]]], 
    1355                        ], 
    1356                        Optional["FromClause"], 
    1357                    ]""", 
    1358                    with_polymorphic, 
    1359                ) 
    1360            else: 
    1361                self.with_polymorphic = (with_polymorphic, None) 
    1362        elif with_polymorphic is not None: 
    1363            raise sa_exc.ArgumentError( 
    1364                f"Invalid setting for with_polymorphic: {with_polymorphic!r}" 
    1365            ) 
    1366        else: 
    1367            self.with_polymorphic = None 
    1368 
    1369        if self.with_polymorphic and self.with_polymorphic[1] is not None: 
    1370            self.with_polymorphic = ( 
    1371                self.with_polymorphic[0], 
    1372                coercions.expect( 
    1373                    roles.FromClauseRole, 
    1374                    self.with_polymorphic[1], 
    1375                ), 
    1376            ) 
    1377 
    1378        if self.configured: 
    1379            self._expire_memoizations() 
    1380 
    1381    def _add_with_polymorphic_subclass(self, mapper): 
    1382        subcl = mapper.class_ 
    1383        if self.with_polymorphic is None: 
    1384            self._set_with_polymorphic((subcl,)) 
    1385        elif self.with_polymorphic[0] != "*": 
    1386            assert isinstance(self.with_polymorphic[0], tuple) 
    1387            self._set_with_polymorphic( 
    1388                (self.with_polymorphic[0] + (subcl,), self.with_polymorphic[1]) 
    1389            ) 
    1390 
    1391    def _set_concrete_base(self, mapper): 
    1392        """Set the given :class:`_orm.Mapper` as the 'inherits' for this 
    1393        :class:`_orm.Mapper`, assuming this :class:`_orm.Mapper` is concrete 
    1394        and does not already have an inherits.""" 
    1395 
    1396        assert self.concrete 
    1397        assert not self.inherits 
    1398        assert isinstance(mapper, Mapper) 
    1399        self.inherits = mapper 
    1400        self.inherits.polymorphic_map.update(self.polymorphic_map) 
    1401        self.polymorphic_map = self.inherits.polymorphic_map 
    1402        for mapper in self.iterate_to_root(): 
    1403            if mapper.polymorphic_on is not None: 
    1404                mapper._requires_row_aliasing = True 
    1405        self.batch = self.inherits.batch 
    1406        for mp in self.self_and_descendants: 
    1407            mp.base_mapper = self.inherits.base_mapper 
    1408        self.inherits._inheriting_mappers.append(self) 
    1409        self.passive_updates = self.inherits.passive_updates 
    1410        self._all_tables = self.inherits._all_tables 
    1411 
    1412        for key, prop in mapper._props.items(): 
    1413            if key not in self._props and not self._should_exclude( 
    1414                key, key, local=False, column=None 
    1415            ): 
    1416                self._adapt_inherited_property(key, prop, False) 
    1417 
    1418    def _set_polymorphic_on(self, polymorphic_on): 
    1419        self.polymorphic_on = polymorphic_on 
    1420        self._configure_polymorphic_setter(True) 
    1421 
    1422    def _configure_class_instrumentation(self): 
    1423        """Associate this Mapper with the 
    1424        given class and entity name. 
    1425 
    1426        Subsequent calls to ``class_mapper()`` for the ``class_`` / ``entity`` 
    1427        name combination will return this mapper.  Also decorate the 
    1428        `__init__` method on the mapped class to include optional 
    1429        auto-session attachment logic. 
    1430 
    1431        """ 
    1432 
    1433        # we expect that declarative has applied the class manager 
    1434        # already and set up a registry.  if this is None, 
    1435        # this raises as of 2.0. 
    1436        manager = attributes.opt_manager_of_class(self.class_) 
    1437 
    1438        if manager is None or not manager.registry: 
    1439            raise sa_exc.InvalidRequestError( 
    1440                "The _mapper() function and Mapper() constructor may not be " 
    1441                "invoked directly outside of a declarative registry." 
    1442                " Please use the sqlalchemy.orm.registry.map_imperatively() " 
    1443                "function for a classical mapping." 
    1444            ) 
    1445 
    1446        self.dispatch.instrument_class(self, self.class_) 
    1447 
    1448        # this invokes the class_instrument event and sets up 
    1449        # the __init__ method.  documented behavior is that this must 
    1450        # occur after the instrument_class event above. 
    1451        # yes two events with the same two words reversed and different APIs. 
    1452        # :( 
    1453 
    1454        manager = instrumentation.register_class( 
    1455            self.class_, 
    1456            mapper=self, 
    1457            expired_attribute_loader=util.partial( 
    1458                loading._load_scalar_attributes, self 
    1459            ), 
    1460            # finalize flag means instrument the __init__ method 
    1461            # and call the class_instrument event 
    1462            finalize=True, 
    1463        ) 
    1464 
    1465        self.class_manager = manager 
    1466 
    1467        assert manager.registry is not None 
    1468        self.registry = manager.registry 
    1469 
    1470        # The remaining members can be added by any mapper, 
    1471        # e_name None or not. 
    1472        if manager.mapper is None: 
    1473            return 
    1474 
    1475        event.listen(manager, "init", _event_on_init, raw=True) 
    1476 
    1477        for key, method in util.iterate_attributes(self.class_): 
    1478            if key == "__init__" and hasattr(method, "_sa_original_init"): 
    1479                method = method._sa_original_init 
    1480                if hasattr(method, "__func__"): 
    1481                    method = method.__func__ 
    1482            if callable(method): 
    1483                if hasattr(method, "__sa_reconstructor__"): 
    1484                    self._reconstructor = method 
    1485                    event.listen(manager, "load", _event_on_load, raw=True) 
    1486                elif hasattr(method, "__sa_validators__"): 
    1487                    validation_opts = method.__sa_validation_opts__ 
    1488                    for name in method.__sa_validators__: 
    1489                        if name in self.validators: 
    1490                            raise sa_exc.InvalidRequestError( 
    1491                                "A validation function for mapped " 
    1492                                "attribute %r on mapper %s already exists." 
    1493                                % (name, self) 
    1494                            ) 
    1495                        self.validators = self.validators.union( 
    1496                            {name: (method, validation_opts)} 
    1497                        ) 
    1498 
    1499    def _set_dispose_flags(self) -> None: 
    1500        self.configured = True 
    1501        self._ready_for_configure = True 
    1502        self._dispose_called = True 
    1503 
    1504        self.__dict__.pop("_configure_failed", None) 
    1505 
    1506    def _str_arg_to_mapped_col(self, argname: str, key: str) -> Column[Any]: 
    1507        try: 
    1508            prop = self._props[key] 
    1509        except KeyError as err: 
    1510            raise sa_exc.ArgumentError( 
    1511                f"Can't determine {argname} column '{key}' - " 
    1512                "no attribute is mapped to this name." 
    1513            ) from err 
    1514        try: 
    1515            expr = prop.expression 
    1516        except AttributeError as ae: 
    1517            raise sa_exc.ArgumentError( 
    1518                f"Can't determine {argname} column '{key}'; " 
    1519                "property does not refer to a single mapped Column" 
    1520            ) from ae 
    1521        if not isinstance(expr, Column): 
    1522            raise sa_exc.ArgumentError( 
    1523                f"Can't determine {argname} column '{key}'; " 
    1524                "property does not refer to a single " 
    1525                "mapped Column" 
    1526            ) 
    1527        return expr 
    1528 
    1529    def _configure_pks(self) -> None: 
    1530        self.tables = sql_util.find_tables(self.persist_selectable) 
    1531 
    1532        self._all_tables.update(t for t in self.tables) 
    1533 
    1534        self._pks_by_table = {} 
    1535        self._cols_by_table = {} 
    1536 
    1537        all_cols = util.column_set( 
    1538            chain(*[col.proxy_set for col in self._columntoproperty]) 
    1539        ) 
    1540 
    1541        pk_cols = util.column_set(c for c in all_cols if c.primary_key) 
    1542 
    1543        # identify primary key columns which are also mapped by this mapper. 
    1544        for fc in set(self.tables).union([self.persist_selectable]): 
    1545            if fc.primary_key and pk_cols.issuperset(fc.primary_key): 
    1546                # ordering is important since it determines the ordering of 
    1547                # mapper.primary_key (and therefore query.get()) 
    1548                self._pks_by_table[fc] = util.ordered_column_set(  # type: ignore  # noqa: E501 
    1549                    fc.primary_key 
    1550                ).intersection( 
    1551                    pk_cols 
    1552                ) 
    1553            self._cols_by_table[fc] = util.ordered_column_set(fc.c).intersection(  # type: ignore  # noqa: E501 
    1554                all_cols 
    1555            ) 
    1556 
    1557        if self._primary_key_argument: 
    1558            coerced_pk_arg = [ 
    1559                ( 
    1560                    self._str_arg_to_mapped_col("primary_key", c) 
    1561                    if isinstance(c, str) 
    1562                    else c 
    1563                ) 
    1564                for c in ( 
    1565                    coercions.expect( 
    1566                        roles.DDLConstraintColumnRole, 
    1567                        coerce_pk, 
    1568                        argname="primary_key", 
    1569                    ) 
    1570                    for coerce_pk in self._primary_key_argument 
    1571                ) 
    1572            ] 
    1573        else: 
    1574            coerced_pk_arg = None 
    1575 
    1576        # if explicit PK argument sent, add those columns to the 
    1577        # primary key mappings 
    1578        if coerced_pk_arg: 
    1579            for k in coerced_pk_arg: 
    1580                if k.table not in self._pks_by_table: 
    1581                    self._pks_by_table[k.table] = util.OrderedSet() 
    1582                self._pks_by_table[k.table].add(k) 
    1583 
    1584        # otherwise, see that we got a full PK for the mapped table 
    1585        elif ( 
    1586            self.persist_selectable not in self._pks_by_table 
    1587            or len(self._pks_by_table[self.persist_selectable]) == 0 
    1588        ): 
    1589            raise sa_exc.ArgumentError( 
    1590                "Mapper %s could not assemble any primary " 
    1591                "key columns for mapped table '%s'" 
    1592                % (self, self.persist_selectable.description) 
    1593            ) 
    1594        elif self.local_table not in self._pks_by_table and isinstance( 
    1595            self.local_table, schema.Table 
    1596        ): 
    1597            util.warn( 
    1598                "Could not assemble any primary " 
    1599                "keys for locally mapped table '%s' - " 
    1600                "no rows will be persisted in this Table." 
    1601                % self.local_table.description 
    1602            ) 
    1603 
    1604        if ( 
    1605            self.inherits 
    1606            and not self.concrete 
    1607            and not self._primary_key_argument 
    1608        ): 
    1609            # if inheriting, the "primary key" for this mapper is 
    1610            # that of the inheriting (unless concrete or explicit) 
    1611            self.primary_key = self.inherits.primary_key 
    1612        else: 
    1613            # determine primary key from argument or persist_selectable pks 
    1614            primary_key: Collection[ColumnElement[Any]] 
    1615 
    1616            if coerced_pk_arg: 
    1617                primary_key = [ 
    1618                    cc if cc is not None else c 
    1619                    for cc, c in ( 
    1620                        (self.persist_selectable.corresponding_column(c), c) 
    1621                        for c in coerced_pk_arg 
    1622                    ) 
    1623                ] 
    1624            else: 
    1625                # if heuristically determined PKs, reduce to the minimal set 
    1626                # of columns by eliminating FK->PK pairs for a multi-table 
    1627                # expression.   May over-reduce for some kinds of UNIONs 
    1628                # / CTEs; use explicit PK argument for these special cases 
    1629                primary_key = sql_util.reduce_columns( 
    1630                    self._pks_by_table[self.persist_selectable], 
    1631                    ignore_nonexistent_tables=True, 
    1632                ) 
    1633 
    1634            if len(primary_key) == 0: 
    1635                raise sa_exc.ArgumentError( 
    1636                    "Mapper %s could not assemble any primary " 
    1637                    "key columns for mapped table '%s'" 
    1638                    % (self, self.persist_selectable.description) 
    1639                ) 
    1640 
    1641            self.primary_key = tuple(primary_key) 
    1642            self._log("Identified primary key columns: %s", primary_key) 
    1643 
    1644        # determine cols that aren't expressed within our tables; mark these 
    1645        # as "read only" properties which are refreshed upon INSERT/UPDATE 
    1646        self._readonly_props = { 
    1647            self._columntoproperty[col] 
    1648            for col in self._columntoproperty 
    1649            if self._columntoproperty[col] not in self._identity_key_props 
    1650            and ( 
    1651                not hasattr(col, "table") 
    1652                or col.table not in self._cols_by_table 
    1653            ) 
    1654        } 
    1655 
    1656    def _configure_properties(self) -> None: 
    1657        self.columns = self.c = sql_base.ColumnCollection()  # type: ignore 
    1658 
    1659        # object attribute names mapped to MapperProperty objects 
    1660        self._props = util.OrderedDict() 
    1661 
    1662        # table columns mapped to MapperProperty 
    1663        self._columntoproperty = _ColumnMapping(self) 
    1664 
    1665        explicit_col_props_by_column: Dict[ 
    1666            KeyedColumnElement[Any], Tuple[str, ColumnProperty[Any]] 
    1667        ] = {} 
    1668        explicit_col_props_by_key: Dict[str, ColumnProperty[Any]] = {} 
    1669 
    1670        # step 1: go through properties that were explicitly passed 
    1671        # in the properties dictionary.  For Columns that are local, put them 
    1672        # aside in a separate collection we will reconcile with the Table 
    1673        # that's given.  For other properties, set them up in _props now. 
    1674        if self._init_properties: 
    1675            for key, prop_arg in self._init_properties.items(): 
    1676                if not isinstance(prop_arg, MapperProperty): 
    1677                    possible_col_prop = self._make_prop_from_column( 
    1678                        key, prop_arg 
    1679                    ) 
    1680                else: 
    1681                    possible_col_prop = prop_arg 
    1682 
    1683                # issue #8705.  if the explicit property is actually a 
    1684                # Column that is local to the local Table, don't set it up 
    1685                # in ._props yet, integrate it into the order given within 
    1686                # the Table. 
    1687 
    1688                _map_as_property_now = True 
    1689                if isinstance(possible_col_prop, properties.ColumnProperty): 
    1690                    for given_col in possible_col_prop.columns: 
    1691                        if self.local_table.c.contains_column(given_col): 
    1692                            _map_as_property_now = False 
    1693                            explicit_col_props_by_key[key] = possible_col_prop 
    1694                            explicit_col_props_by_column[given_col] = ( 
    1695                                key, 
    1696                                possible_col_prop, 
    1697                            ) 
    1698 
    1699                if _map_as_property_now: 
    1700                    self._configure_property( 
    1701                        key, 
    1702                        possible_col_prop, 
    1703                        init=False, 
    1704                    ) 
    1705 
    1706        # step 2: pull properties from the inherited mapper.  reconcile 
    1707        # columns with those which are explicit above.  for properties that 
    1708        # are only in the inheriting mapper, set them up as local props 
    1709        if self.inherits: 
    1710            for key, inherited_prop in self.inherits._props.items(): 
    1711                if self._should_exclude(key, key, local=False, column=None): 
    1712                    continue 
    1713 
    1714                incoming_prop = explicit_col_props_by_key.get(key) 
    1715                if incoming_prop: 
    1716                    new_prop = self._reconcile_prop_with_incoming_columns( 
    1717                        key, 
    1718                        inherited_prop, 
    1719                        warn_only=False, 
    1720                        incoming_prop=incoming_prop, 
    1721                    ) 
    1722                    explicit_col_props_by_key[key] = new_prop 
    1723 
    1724                    for inc_col in incoming_prop.columns: 
    1725                        explicit_col_props_by_column[inc_col] = ( 
    1726                            key, 
    1727                            new_prop, 
    1728                        ) 
    1729                elif key not in self._props: 
    1730                    self._adapt_inherited_property(key, inherited_prop, False) 
    1731 
    1732        # step 3.  Iterate through all columns in the persist selectable. 
    1733        # this includes not only columns in the local table / fromclause, 
    1734        # but also those columns in the superclass table if we are joined 
    1735        # inh or single inh mapper.  map these columns as well. additional 
    1736        # reconciliation against inherited columns occurs here also. 
    1737 
    1738        for column in self.persist_selectable.columns: 
    1739            if column in explicit_col_props_by_column: 
    1740                # column was explicitly passed to properties; configure 
    1741                # it now in the order in which it corresponds to the 
    1742                # Table / selectable 
    1743                key, prop = explicit_col_props_by_column[column] 
    1744                self._configure_property(key, prop, init=False) 
    1745                continue 
    1746 
    1747            elif column in self._columntoproperty: 
    1748                continue 
    1749 
    1750            column_key = (self.column_prefix or "") + column.key 
    1751            if self._should_exclude( 
    1752                column.key, 
    1753                column_key, 
    1754                local=self.local_table.c.contains_column(column), 
    1755                column=column, 
    1756            ): 
    1757                continue 
    1758 
    1759            # adjust the "key" used for this column to that 
    1760            # of the inheriting mapper 
    1761            for mapper in self.iterate_to_root(): 
    1762                if column in mapper._columntoproperty: 
    1763                    column_key = mapper._columntoproperty[column].key 
    1764 
    1765            self._configure_property( 
    1766                column_key, 
    1767                column, 
    1768                init=False, 
    1769                setparent=True, 
    1770            ) 
    1771 
    1772    def _configure_polymorphic_setter(self, init=False): 
    1773        """Configure an attribute on the mapper representing the 
    1774        'polymorphic_on' column, if applicable, and not 
    1775        already generated by _configure_properties (which is typical). 
    1776 
    1777        Also create a setter function which will assign this 
    1778        attribute to the value of the 'polymorphic_identity' 
    1779        upon instance construction, also if applicable.  This 
    1780        routine will run when an instance is created. 
    1781 
    1782        """ 
    1783        setter = False 
    1784        polymorphic_key: Optional[str] = None 
    1785 
    1786        if self.polymorphic_on is not None: 
    1787            setter = True 
    1788 
    1789            if isinstance(self.polymorphic_on, str): 
    1790                # polymorphic_on specified as a string - link 
    1791                # it to mapped ColumnProperty 
    1792                try: 
    1793                    self.polymorphic_on = self._props[self.polymorphic_on] 
    1794                except KeyError as err: 
    1795                    raise sa_exc.ArgumentError( 
    1796                        "Can't determine polymorphic_on " 
    1797                        "value '%s' - no attribute is " 
    1798                        "mapped to this name." % self.polymorphic_on 
    1799                    ) from err 
    1800 
    1801            if self.polymorphic_on in self._columntoproperty: 
    1802                # polymorphic_on is a column that is already mapped 
    1803                # to a ColumnProperty 
    1804                prop = self._columntoproperty[self.polymorphic_on] 
    1805            elif isinstance(self.polymorphic_on, MapperProperty): 
    1806                # polymorphic_on is directly a MapperProperty, 
    1807                # ensure it's a ColumnProperty 
    1808                if not isinstance( 
    1809                    self.polymorphic_on, properties.ColumnProperty 
    1810                ): 
    1811                    raise sa_exc.ArgumentError( 
    1812                        "Only direct column-mapped " 
    1813                        "property or SQL expression " 
    1814                        "can be passed for polymorphic_on" 
    1815                    ) 
    1816                prop = self.polymorphic_on 
    1817            else: 
    1818                # polymorphic_on is a Column or SQL expression and 
    1819                # doesn't appear to be mapped. this means it can be 1. 
    1820                # only present in the with_polymorphic selectable or 
    1821                # 2. a totally standalone SQL expression which we'd 
    1822                # hope is compatible with this mapper's persist_selectable 
    1823                col = self.persist_selectable.corresponding_column( 
    1824                    self.polymorphic_on 
    1825                ) 
    1826                if col is None: 
    1827                    # polymorphic_on doesn't derive from any 
    1828                    # column/expression isn't present in the mapped 
    1829                    # table. we will make a "hidden" ColumnProperty 
    1830                    # for it. Just check that if it's directly a 
    1831                    # schema.Column and we have with_polymorphic, it's 
    1832                    # likely a user error if the schema.Column isn't 
    1833                    # represented somehow in either persist_selectable or 
    1834                    # with_polymorphic.   Otherwise as of 0.7.4 we 
    1835                    # just go with it and assume the user wants it 
    1836                    # that way (i.e. a CASE statement) 
    1837                    setter = False 
    1838                    instrument = False 
    1839                    col = self.polymorphic_on 
    1840                    if isinstance(col, schema.Column) and ( 
    1841                        self.with_polymorphic is None 
    1842                        or self.with_polymorphic[1] is None 
    1843                        or self.with_polymorphic[1].corresponding_column(col) 
    1844                        is None 
    1845                    ): 
    1846                        raise sa_exc.InvalidRequestError( 
    1847                            "Could not map polymorphic_on column " 
    1848                            "'%s' to the mapped table - polymorphic " 
    1849                            "loads will not function properly" 
    1850                            % col.description 
    1851                        ) 
    1852                else: 
    1853                    # column/expression that polymorphic_on derives from 
    1854                    # is present in our mapped table 
    1855                    # and is probably mapped, but polymorphic_on itself 
    1856                    # is not.  This happens when 
    1857                    # the polymorphic_on is only directly present in the 
    1858                    # with_polymorphic selectable, as when use 
    1859                    # polymorphic_union. 
    1860                    # we'll make a separate ColumnProperty for it. 
    1861                    instrument = True 
    1862                key = getattr(col, "key", None) 
    1863                if key: 
    1864                    if self._should_exclude(key, key, False, col): 
    1865                        raise sa_exc.InvalidRequestError( 
    1866                            "Cannot exclude or override the " 
    1867                            "discriminator column %r" % key 
    1868                        ) 
    1869                else: 
    1870                    self.polymorphic_on = col = col.label("_sa_polymorphic_on") 
    1871                    key = col.key 
    1872 
    1873                prop = properties.ColumnProperty(col, _instrument=instrument) 
    1874                self._configure_property(key, prop, init=init, setparent=True) 
    1875 
    1876            # the actual polymorphic_on should be the first public-facing 
    1877            # column in the property 
    1878            self.polymorphic_on = prop.columns[0] 
    1879            polymorphic_key = prop.key 
    1880        else: 
    1881            # no polymorphic_on was set. 
    1882            # check inheriting mappers for one. 
    1883            for mapper in self.iterate_to_root(): 
    1884                # determine if polymorphic_on of the parent 
    1885                # should be propagated here.   If the col 
    1886                # is present in our mapped table, or if our mapped 
    1887                # table is the same as the parent (i.e. single table 
    1888                # inheritance), we can use it 
    1889                if mapper.polymorphic_on is not None: 
    1890                    if self.persist_selectable is mapper.persist_selectable: 
    1891                        self.polymorphic_on = mapper.polymorphic_on 
    1892                    else: 
    1893                        self.polymorphic_on = ( 
    1894                            self.persist_selectable 
    1895                        ).corresponding_column(mapper.polymorphic_on) 
    1896                    # we can use the parent mapper's _set_polymorphic_identity 
    1897                    # directly; it ensures the polymorphic_identity of the 
    1898                    # instance's mapper is used so is portable to subclasses. 
    1899                    if self.polymorphic_on is not None: 
    1900                        self._set_polymorphic_identity = ( 
    1901                            mapper._set_polymorphic_identity 
    1902                        ) 
    1903                        self._polymorphic_attr_key = ( 
    1904                            mapper._polymorphic_attr_key 
    1905                        ) 
    1906                        self._validate_polymorphic_identity = ( 
    1907                            mapper._validate_polymorphic_identity 
    1908                        ) 
    1909                    else: 
    1910                        self._set_polymorphic_identity = None 
    1911                        self._polymorphic_attr_key = None 
    1912                    return 
    1913 
    1914        if self.polymorphic_abstract and self.polymorphic_on is None: 
    1915            raise sa_exc.InvalidRequestError( 
    1916                "The Mapper.polymorphic_abstract parameter may only be used " 
    1917                "on a mapper hierarchy which includes the " 
    1918                "Mapper.polymorphic_on parameter at the base of the hierarchy." 
    1919            ) 
    1920 
    1921        if setter: 
    1922 
    1923            def _set_polymorphic_identity(state): 
    1924                dict_ = state.dict 
    1925                # TODO: what happens if polymorphic_on column attribute name 
    1926                # does not match .key? 
    1927 
    1928                polymorphic_identity = ( 
    1929                    state.manager.mapper.polymorphic_identity 
    1930                ) 
    1931                if ( 
    1932                    polymorphic_identity is None 
    1933                    and state.manager.mapper.polymorphic_abstract 
    1934                ): 
    1935                    raise sa_exc.InvalidRequestError( 
    1936                        f"Can't instantiate class for {state.manager.mapper}; " 
    1937                        "mapper is marked polymorphic_abstract=True" 
    1938                    ) 
    1939 
    1940                state.get_impl(polymorphic_key).set( 
    1941                    state, 
    1942                    dict_, 
    1943                    polymorphic_identity, 
    1944                    None, 
    1945                ) 
    1946 
    1947            self._polymorphic_attr_key = polymorphic_key 
    1948 
    1949            def _validate_polymorphic_identity(mapper, state, dict_): 
    1950                if ( 
    1951                    polymorphic_key in dict_ 
    1952                    and dict_[polymorphic_key] 
    1953                    not in mapper._acceptable_polymorphic_identities 
    1954                ): 
    1955                    util.warn_limited( 
    1956                        "Flushing object %s with " 
    1957                        "incompatible polymorphic identity %r; the " 
    1958                        "object may not refresh and/or load correctly", 
    1959                        (state_str(state), dict_[polymorphic_key]), 
    1960                    ) 
    1961 
    1962            self._set_polymorphic_identity = _set_polymorphic_identity 
    1963            self._validate_polymorphic_identity = ( 
    1964                _validate_polymorphic_identity 
    1965            ) 
    1966        else: 
    1967            self._polymorphic_attr_key = None 
    1968            self._set_polymorphic_identity = None 
    1969 
    1970    _validate_polymorphic_identity = None 
    1971 
    1972    @HasMemoized.memoized_attribute 
    1973    def _version_id_prop(self): 
    1974        if self.version_id_col is not None: 
    1975            return self._columntoproperty[self.version_id_col] 
    1976        else: 
    1977            return None 
    1978 
    1979    @HasMemoized.memoized_attribute 
    1980    def _acceptable_polymorphic_identities(self): 
    1981        identities = set() 
    1982 
    1983        stack = deque([self]) 
    1984        while stack: 
    1985            item = stack.popleft() 
    1986            if item.persist_selectable is self.persist_selectable: 
    1987                identities.add(item.polymorphic_identity) 
    1988                stack.extend(item._inheriting_mappers) 
    1989 
    1990        return identities 
    1991 
    1992    @HasMemoized.memoized_attribute 
    1993    def _prop_set(self): 
    1994        return frozenset(self._props.values()) 
    1995 
    1996    @util.preload_module("sqlalchemy.orm.descriptor_props") 
    1997    def _adapt_inherited_property(self, key, prop, init): 
    1998        descriptor_props = util.preloaded.orm_descriptor_props 
    1999 
    2000        if not self.concrete: 
    2001            self._configure_property(key, prop, init=False, setparent=False) 
    2002        elif key not in self._props: 
    2003            # determine if the class implements this attribute; if not, 
    2004            # or if it is implemented by the attribute that is handling the 
    2005            # given superclass-mapped property, then we need to report that we 
    2006            # can't use this at the instance level since we are a concrete 
    2007            # mapper and we don't map this.  don't trip user-defined 
    2008            # descriptors that might have side effects when invoked. 
    2009            implementing_attribute = self.class_manager._get_class_attr_mro( 
    2010                key, prop 
    2011            ) 
    2012            if implementing_attribute is prop or ( 
    2013                isinstance( 
    2014                    implementing_attribute, attributes.InstrumentedAttribute 
    2015                ) 
    2016                and implementing_attribute._parententity is prop.parent 
    2017            ): 
    2018                self._configure_property( 
    2019                    key, 
    2020                    descriptor_props.ConcreteInheritedProperty(), 
    2021                    init=init, 
    2022                    setparent=True, 
    2023                ) 
    2024 
    2025    @util.preload_module("sqlalchemy.orm.descriptor_props") 
    2026    def _configure_property( 
    2027        self, 
    2028        key: str, 
    2029        prop_arg: Union[KeyedColumnElement[Any], MapperProperty[Any]], 
    2030        *, 
    2031        init: bool = True, 
    2032        setparent: bool = True, 
    2033        warn_for_existing: bool = False, 
    2034    ) -> MapperProperty[Any]: 
    2035        descriptor_props = util.preloaded.orm_descriptor_props 
    2036        self._log( 
    2037            "_configure_property(%s, %s)", key, prop_arg.__class__.__name__ 
    2038        ) 
    2039 
    2040        if not isinstance(prop_arg, MapperProperty): 
    2041            prop: MapperProperty[Any] = self._property_from_column( 
    2042                key, prop_arg 
    2043            ) 
    2044        else: 
    2045            prop = prop_arg 
    2046 
    2047        if isinstance(prop, properties.ColumnProperty): 
    2048            col = self.persist_selectable.corresponding_column(prop.columns[0]) 
    2049 
    2050            # if the column is not present in the mapped table, 
    2051            # test if a column has been added after the fact to the 
    2052            # parent table (or their parent, etc.) [ticket:1570] 
    2053            if col is None and self.inherits: 
    2054                path = [self] 
    2055                for m in self.inherits.iterate_to_root(): 
    2056                    col = m.local_table.corresponding_column(prop.columns[0]) 
    2057                    if col is not None: 
    2058                        for m2 in path: 
    2059                            m2.persist_selectable._refresh_for_new_column(col) 
    2060                        col = self.persist_selectable.corresponding_column( 
    2061                            prop.columns[0] 
    2062                        ) 
    2063                        break 
    2064                    path.append(m) 
    2065 
    2066            # subquery expression, column not present in the mapped 
    2067            # selectable. 
    2068            if col is None: 
    2069                col = prop.columns[0] 
    2070 
    2071                # column is coming in after _readonly_props was 
    2072                # initialized; check for 'readonly' 
    2073                if hasattr(self, "_readonly_props") and ( 
    2074                    not hasattr(col, "table") 
    2075                    or col.table not in self._cols_by_table 
    2076                ): 
    2077                    self._readonly_props.add(prop) 
    2078 
    2079            else: 
    2080                # if column is coming in after _cols_by_table was 
    2081                # initialized, ensure the col is in the right set 
    2082                if ( 
    2083                    hasattr(self, "_cols_by_table") 
    2084                    and col.table in self._cols_by_table 
    2085                    and col not in self._cols_by_table[col.table] 
    2086                ): 
    2087                    self._cols_by_table[col.table].add(col) 
    2088 
    2089            # if this properties.ColumnProperty represents the "polymorphic 
    2090            # discriminator" column, mark it.  We'll need this when rendering 
    2091            # columns in SELECT statements. 
    2092            if not hasattr(prop, "_is_polymorphic_discriminator"): 
    2093                prop._is_polymorphic_discriminator = ( 
    2094                    col is self.polymorphic_on 
    2095                    or prop.columns[0] is self.polymorphic_on 
    2096                ) 
    2097 
    2098            if isinstance(col, expression.Label): 
    2099                # new in 1.4, get column property against expressions 
    2100                # to be addressable in subqueries 
    2101                col.key = col._tq_key_label = key 
    2102 
    2103            self.columns.add(col, key) 
    2104 
    2105            for col in prop.columns: 
    2106                for proxy_col in col.proxy_set: 
    2107                    self._columntoproperty[proxy_col] = prop 
    2108 
    2109        if getattr(prop, "key", key) != key: 
    2110            util.warn( 
    2111                f"ORM mapped property {self.class_.__name__}.{prop.key} being " 
    2112                "assigned to attribute " 
    2113                f"{key!r} is already associated with " 
    2114                f"attribute {prop.key!r}. The attribute will be de-associated " 
    2115                f"from {prop.key!r}." 
    2116            ) 
    2117 
    2118        prop.key = key 
    2119 
    2120        if setparent: 
    2121            prop.set_parent(self, init) 
    2122 
    2123        if key in self._props and getattr( 
    2124            self._props[key], "_mapped_by_synonym", False 
    2125        ): 
    2126            syn = self._props[key]._mapped_by_synonym 
    2127            raise sa_exc.ArgumentError( 
    2128                "Can't call map_column=True for synonym %r=%r, " 
    2129                "a ColumnProperty already exists keyed to the name " 
    2130                "%r for column %r" % (syn, key, key, syn) 
    2131            ) 
    2132 
    2133        # replacement cases 
    2134 
    2135        # case one: prop is replacing a prop that we have mapped.  this is 
    2136        # independent of whatever might be in the actual class dictionary 
    2137        if ( 
    2138            key in self._props 
    2139            and not isinstance( 
    2140                self._props[key], descriptor_props.ConcreteInheritedProperty 
    2141            ) 
    2142            and not isinstance(prop, descriptor_props.SynonymProperty) 
    2143        ): 
    2144            if warn_for_existing: 
    2145                util.warn_deprecated( 
    2146                    f"User-placed attribute {self.class_.__name__}.{key} on " 
    2147                    f"{self} is replacing an existing ORM-mapped attribute.  " 
    2148                    "Behavior is not fully defined in this case.  This " 
    2149                    "use is deprecated and will raise an error in a future " 
    2150                    "release", 
    2151                    "2.0", 
    2152                ) 
    2153            oldprop = self._props[key] 
    2154            self._path_registry.pop(oldprop, None) 
    2155 
    2156        # case two: prop is replacing an attribute on the class of some kind. 
    2157        # we have to be more careful here since it's normal when using 
    2158        # Declarative that all the "declared attributes" on the class 
    2159        # get replaced. 
    2160        elif ( 
    2161            warn_for_existing 
    2162            and self.class_.__dict__.get(key, None) is not None 
    2163            and not isinstance(prop, descriptor_props.SynonymProperty) 
    2164            and not isinstance( 
    2165                self._props.get(key, None), 
    2166                descriptor_props.ConcreteInheritedProperty, 
    2167            ) 
    2168        ): 
    2169            util.warn_deprecated( 
    2170                f"User-placed attribute {self.class_.__name__}.{key} on " 
    2171                f"{self} is replacing an existing class-bound " 
    2172                "attribute of the same name.  " 
    2173                "Behavior is not fully defined in this case.  This " 
    2174                "use is deprecated and will raise an error in a future " 
    2175                "release", 
    2176                "2.0", 
    2177            ) 
    2178 
    2179        self._props[key] = prop 
    2180 
    2181        prop.instrument_class(self) 
    2182 
    2183        for mapper in self._inheriting_mappers: 
    2184            mapper._adapt_inherited_property(key, prop, init) 
    2185 
    2186        if init: 
    2187            prop.init() 
    2188            prop.post_instrument_class(self) 
    2189 
    2190        if self.configured: 
    2191            self._expire_memoizations() 
    2192 
    2193        return prop 
    2194 
    2195    def _make_prop_from_column( 
    2196        self, 
    2197        key: str, 
    2198        column: Union[ 
    2199            Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 
    2200        ], 
    2201    ) -> ColumnProperty[Any]: 
    2202        columns = util.to_list(column) 
    2203        mapped_column = [] 
    2204        for c in columns: 
    2205            mc = self.persist_selectable.corresponding_column(c) 
    2206            if mc is None: 
    2207                mc = self.local_table.corresponding_column(c) 
    2208                if mc is not None: 
    2209                    # if the column is in the local table but not the 
    2210                    # mapped table, this corresponds to adding a 
    2211                    # column after the fact to the local table. 
    2212                    # [ticket:1523] 
    2213                    self.persist_selectable._refresh_for_new_column(mc) 
    2214                mc = self.persist_selectable.corresponding_column(c) 
    2215                if mc is None: 
    2216                    raise sa_exc.ArgumentError( 
    2217                        "When configuring property '%s' on %s, " 
    2218                        "column '%s' is not represented in the mapper's " 
    2219                        "table. Use the `column_property()` function to " 
    2220                        "force this column to be mapped as a read-only " 
    2221                        "attribute." % (key, self, c) 
    2222                    ) 
    2223            mapped_column.append(mc) 
    2224        return properties.ColumnProperty(*mapped_column) 
    2225 
    2226    def _reconcile_prop_with_incoming_columns( 
    2227        self, 
    2228        key: str, 
    2229        existing_prop: MapperProperty[Any], 
    2230        warn_only: bool, 
    2231        incoming_prop: Optional[ColumnProperty[Any]] = None, 
    2232        single_column: Optional[KeyedColumnElement[Any]] = None, 
    2233    ) -> ColumnProperty[Any]: 
    2234        if incoming_prop and ( 
    2235            self.concrete 
    2236            or not isinstance(existing_prop, properties.ColumnProperty) 
    2237        ): 
    2238            return incoming_prop 
    2239 
    2240        existing_column = existing_prop.columns[0] 
    2241 
    2242        if incoming_prop and existing_column in incoming_prop.columns: 
    2243            return incoming_prop 
    2244 
    2245        if incoming_prop is None: 
    2246            assert single_column is not None 
    2247            incoming_column = single_column 
    2248            equated_pair_key = (existing_prop.columns[0], incoming_column) 
    2249        else: 
    2250            assert single_column is None 
    2251            incoming_column = incoming_prop.columns[0] 
    2252            equated_pair_key = (incoming_column, existing_prop.columns[0]) 
    2253 
    2254        if ( 
    2255            ( 
    2256                not self._inherits_equated_pairs 
    2257                or (equated_pair_key not in self._inherits_equated_pairs) 
    2258            ) 
    2259            and not existing_column.shares_lineage(incoming_column) 
    2260            and existing_column is not self.version_id_col 
    2261            and incoming_column is not self.version_id_col 
    2262        ): 
    2263            msg = ( 
    2264                "Implicitly combining column %s with column " 
    2265                "%s under attribute '%s'.  Please configure one " 
    2266                "or more attributes for these same-named columns " 
    2267                "explicitly." 
    2268                % ( 
    2269                    existing_prop.columns[-1], 
    2270                    incoming_column, 
    2271                    key, 
    2272                ) 
    2273            ) 
    2274            if warn_only: 
    2275                util.warn(msg) 
    2276            else: 
    2277                raise sa_exc.InvalidRequestError(msg) 
    2278 
    2279        # existing properties.ColumnProperty from an inheriting 
    2280        # mapper. make a copy and append our column to it 
    2281        new_prop = existing_prop.copy() 
    2282 
    2283        new_prop.columns.insert(0, incoming_column) 
    2284        self._log( 
    2285            "inserting column to existing list " 
    2286            "in properties.ColumnProperty %s", 
    2287            key, 
    2288        ) 
    2289        return new_prop  # type: ignore 
    2290 
    2291    @util.preload_module("sqlalchemy.orm.descriptor_props") 
    2292    def _property_from_column( 
    2293        self, 
    2294        key: str, 
    2295        column: KeyedColumnElement[Any], 
    2296    ) -> ColumnProperty[Any]: 
    2297        """generate/update a :class:`.ColumnProperty` given a 
    2298        :class:`_schema.Column` or other SQL expression object.""" 
    2299 
    2300        descriptor_props = util.preloaded.orm_descriptor_props 
    2301 
    2302        prop = self._props.get(key) 
    2303 
    2304        if isinstance(prop, properties.ColumnProperty): 
    2305            return self._reconcile_prop_with_incoming_columns( 
    2306                key, 
    2307                prop, 
    2308                single_column=column, 
    2309                warn_only=prop.parent is not self, 
    2310            ) 
    2311        elif prop is None or isinstance( 
    2312            prop, descriptor_props.ConcreteInheritedProperty 
    2313        ): 
    2314            return self._make_prop_from_column(key, column) 
    2315        else: 
    2316            raise sa_exc.ArgumentError( 
    2317                "WARNING: when configuring property '%s' on %s, " 
    2318                "column '%s' conflicts with property '%r'. " 
    2319                "To resolve this, map the column to the class under a " 
    2320                "different name in the 'properties' dictionary.  Or, " 
    2321                "to remove all awareness of the column entirely " 
    2322                "(including its availability as a foreign key), " 
    2323                "use the 'include_properties' or 'exclude_properties' " 
    2324                "mapper arguments to control specifically which table " 
    2325                "columns get mapped." % (key, self, column.key, prop) 
    2326            ) 
    2327 
    2328    @util.langhelpers.tag_method_for_warnings( 
    2329        "This warning originated from the `configure_mappers()` process, " 
    2330        "which was invoked automatically in response to a user-initiated " 
    2331        "operation.", 
    2332        sa_exc.SAWarning, 
    2333    ) 
    2334    def _check_configure(self) -> None: 
    2335        if self.registry._new_mappers: 
    2336            _configure_registries({self.registry}, cascade=True) 
    2337 
    2338    def _post_configure_properties(self) -> None: 
    2339        """Call the ``init()`` method on all ``MapperProperties`` 
    2340        attached to this mapper. 
    2341 
    2342        This is a deferred configuration step which is intended 
    2343        to execute once all mappers have been constructed. 
    2344 
    2345        """ 
    2346 
    2347        self._log("_post_configure_properties() started") 
    2348        l = [(key, prop) for key, prop in self._props.items()] 
    2349        for key, prop in l: 
    2350            self._log("initialize prop %s", key) 
    2351 
    2352            if prop.parent is self and not prop._configure_started: 
    2353                prop.init() 
    2354 
    2355            if prop._configure_finished: 
    2356                prop.post_instrument_class(self) 
    2357 
    2358        self._log("_post_configure_properties() complete") 
    2359        self.configured = True 
    2360 
    2361    def add_properties(self, dict_of_properties): 
    2362        """Add the given dictionary of properties to this mapper, 
    2363        using `add_property`. 
    2364 
    2365        """ 
    2366        for key, value in dict_of_properties.items(): 
    2367            self.add_property(key, value) 
    2368 
    2369    def add_property( 
    2370        self, key: str, prop: Union[Column[Any], MapperProperty[Any]] 
    2371    ) -> None: 
    2372        """Add an individual MapperProperty to this mapper. 
    2373 
    2374        If the mapper has not been configured yet, just adds the 
    2375        property to the initial properties dictionary sent to the 
    2376        constructor.  If this Mapper has already been configured, then 
    2377        the given MapperProperty is configured immediately. 
    2378 
    2379        """ 
    2380        prop = self._configure_property( 
    2381            key, prop, init=self.configured, warn_for_existing=True 
    2382        ) 
    2383        assert isinstance(prop, MapperProperty) 
    2384        self._init_properties[key] = prop 
    2385 
    2386    def _expire_memoizations(self) -> None: 
    2387        for mapper in self.iterate_to_root(): 
    2388            mapper._reset_memoizations() 
    2389 
    2390    @property 
    2391    def _log_desc(self) -> str: 
    2392        return ( 
    2393            "(" 
    2394            + self.class_.__name__ 
    2395            + "|" 
    2396            + ( 
    2397                self.local_table is not None 
    2398                and self.local_table.description 
    2399                or str(self.local_table) 
    2400            ) 
    2401            + ")" 
    2402        ) 
    2403 
    2404    def _log(self, msg: str, *args: Any) -> None: 
    2405        self.logger.info("%s " + msg, *((self._log_desc,) + args)) 
    2406 
    2407    def _log_debug(self, msg: str, *args: Any) -> None: 
    2408        self.logger.debug("%s " + msg, *((self._log_desc,) + args)) 
    2409 
    2410    def __repr__(self) -> str: 
    2411        return "<Mapper at 0x%x; %s>" % (id(self), self.class_.__name__) 
    2412 
    2413    def __str__(self) -> str: 
    2414        return "Mapper[%s(%s)]" % ( 
    2415            self.class_.__name__, 
    2416            ( 
    2417                self.local_table.description 
    2418                if self.local_table is not None 
    2419                else self.persist_selectable.description 
    2420            ), 
    2421        ) 
    2422 
    2423    def _is_orphan(self, state: InstanceState[_O]) -> bool: 
    2424        orphan_possible = False 
    2425        for mapper in self.iterate_to_root(): 
    2426            for key, cls in mapper._delete_orphans: 
    2427                orphan_possible = True 
    2428 
    2429                has_parent = attributes.manager_of_class(cls).has_parent( 
    2430                    state, key, optimistic=state.has_identity 
    2431                ) 
    2432 
    2433                if self.legacy_is_orphan and has_parent: 
    2434                    return False 
    2435                elif not self.legacy_is_orphan and not has_parent: 
    2436                    return True 
    2437 
    2438        if self.legacy_is_orphan: 
    2439            return orphan_possible 
    2440        else: 
    2441            return False 
    2442 
    2443    def has_property(self, key: str) -> bool: 
    2444        return key in self._props 
    2445 
    2446    def get_property( 
    2447        self, key: str, _configure_mappers: bool = False 
    2448    ) -> MapperProperty[Any]: 
    2449        """return a MapperProperty associated with the given key.""" 
    2450 
    2451        if _configure_mappers: 
    2452            self._check_configure() 
    2453 
    2454        try: 
    2455            return self._props[key] 
    2456        except KeyError as err: 
    2457            raise sa_exc.InvalidRequestError( 
    2458                f"Mapper '{self}' has no property '{key}'.  If this property " 
    2459                "was indicated from other mappers or configure events, ensure " 
    2460                "registry.configure() has been called." 
    2461            ) from err 
    2462 
    2463    def get_property_by_column( 
    2464        self, column: ColumnElement[_T] 
    2465    ) -> MapperProperty[_T]: 
    2466        """Given a :class:`_schema.Column` object, return the 
    2467        :class:`.MapperProperty` which maps this column.""" 
    2468 
    2469        return self._columntoproperty[column] 
    2470 
    2471    @property 
    2472    def iterate_properties(self): 
    2473        """return an iterator of all MapperProperty objects.""" 
    2474 
    2475        return iter(self._props.values()) 
    2476 
    2477    def _mappers_from_spec( 
    2478        self, spec: Any, selectable: Optional[FromClause] 
    2479    ) -> Sequence[Mapper[Any]]: 
    2480        """given a with_polymorphic() argument, return the set of mappers it 
    2481        represents. 
    2482 
    2483        Trims the list of mappers to just those represented within the given 
    2484        selectable, if present. This helps some more legacy-ish mappings. 
    2485 
    2486        """ 
    2487        if spec == "*": 
    2488            mappers = list(self.self_and_descendants) 
    2489        elif spec: 
    2490            mapper_set: Set[Mapper[Any]] = set() 
    2491            for m in util.to_list(spec): 
    2492                m = _class_to_mapper(m) 
    2493                if not m.isa(self): 
    2494                    raise sa_exc.InvalidRequestError( 
    2495                        "%r does not inherit from %r" % (m, self) 
    2496                    ) 
    2497 
    2498                if selectable is None: 
    2499                    mapper_set.update(m.iterate_to_root()) 
    2500                else: 
    2501                    mapper_set.add(m) 
    2502            mappers = [m for m in self.self_and_descendants if m in mapper_set] 
    2503        else: 
    2504            mappers = [] 
    2505 
    2506        if selectable is not None: 
    2507            tables = set( 
    2508                sql_util.find_tables(selectable, include_aliases=True) 
    2509            ) 
    2510            mappers = [m for m in mappers if m.local_table in tables] 
    2511        return mappers 
    2512 
    2513    def _selectable_from_mappers( 
    2514        self, mappers: Iterable[Mapper[Any]], innerjoin: bool 
    2515    ) -> FromClause: 
    2516        """given a list of mappers (assumed to be within this mapper's 
    2517        inheritance hierarchy), construct an outerjoin amongst those mapper's 
    2518        mapped tables. 
    2519 
    2520        """ 
    2521        from_obj = self.persist_selectable 
    2522        for m in mappers: 
    2523            if m is self: 
    2524                continue 
    2525            if m.concrete: 
    2526                raise sa_exc.InvalidRequestError( 
    2527                    "'with_polymorphic()' requires 'selectable' argument " 
    2528                    "when concrete-inheriting mappers are used." 
    2529                ) 
    2530            elif not m.single: 
    2531                if innerjoin: 
    2532                    from_obj = from_obj.join( 
    2533                        m.local_table, m.inherit_condition 
    2534                    ) 
    2535                else: 
    2536                    from_obj = from_obj.outerjoin( 
    2537                        m.local_table, m.inherit_condition 
    2538                    ) 
    2539 
    2540        return from_obj 
    2541 
    2542    @HasMemoized.memoized_attribute 
    2543    def _version_id_has_server_side_value(self) -> bool: 
    2544        vid_col = self.version_id_col 
    2545 
    2546        if vid_col is None: 
    2547            return False 
    2548 
    2549        elif not isinstance(vid_col, Column): 
    2550            return True 
    2551        else: 
    2552            return vid_col.server_default is not None or ( 
    2553                vid_col.default is not None 
    2554                and ( 
    2555                    not vid_col.default.is_scalar 
    2556                    and not vid_col.default.is_callable 
    2557                ) 
    2558            ) 
    2559 
    2560    @HasMemoized.memoized_attribute 
    2561    def _single_table_criteria_component(self): 
    2562        if self.single and self.inherits and self.polymorphic_on is not None: 
    2563 
    2564            hierarchy = tuple( 
    2565                m.polymorphic_identity 
    2566                for m in self.self_and_descendants 
    2567                if not m.polymorphic_abstract 
    2568            ) 
    2569 
    2570            return ( 
    2571                self.polymorphic_on._annotate( 
    2572                    {"parententity": self, "parentmapper": self} 
    2573                ), 
    2574                hierarchy, 
    2575            ) 
    2576        else: 
    2577            return None 
    2578 
    2579    @HasMemoized.memoized_attribute 
    2580    def _single_table_criterion(self): 
    2581        component = self._single_table_criteria_component 
    2582        if component is not None: 
    2583            return component[0].in_(component[1]) 
    2584        else: 
    2585            return None 
    2586 
    2587    @HasMemoized.memoized_attribute 
    2588    def _has_aliased_polymorphic_fromclause(self): 
    2589        """return True if with_polymorphic[1] is an aliased fromclause, 
    2590        like a subquery. 
    2591 
    2592        As of #8168, polymorphic adaption with ORMAdapter is used only 
    2593        if this is present. 
    2594 
    2595        """ 
    2596        return self.with_polymorphic and isinstance( 
    2597            self.with_polymorphic[1], 
    2598            expression.AliasedReturnsRows, 
    2599        ) 
    2600 
    2601    @HasMemoized.memoized_attribute 
    2602    def _should_select_with_poly_adapter(self): 
    2603        """determine if _MapperEntity or _ORMColumnEntity will need to use 
    2604        polymorphic adaption when setting up a SELECT as well as fetching 
    2605        rows for mapped classes and subclasses against this Mapper. 
    2606 
    2607        moved here from context.py for #8456 to generalize the ruleset 
    2608        for this condition. 
    2609 
    2610        """ 
    2611 
    2612        # this has been simplified as of #8456. 
    2613        # rule is: if we have a with_polymorphic or a concrete-style 
    2614        # polymorphic selectable, *or* if the base mapper has either of those, 
    2615        # we turn on the adaption thing.  if not, we do *no* adaption. 
    2616        # 
    2617        # (UPDATE for #8168: the above comment was not accurate, as we were 
    2618        # still saying "do polymorphic" if we were using an auto-generated 
    2619        # flattened JOIN for with_polymorphic.) 
    2620        # 
    2621        # this splits the behavior among the "regular" joined inheritance 
    2622        # and single inheritance mappers, vs. the "weird / difficult" 
    2623        # concrete and joined inh mappings that use a with_polymorphic of 
    2624        # some kind or polymorphic_union. 
    2625        # 
    2626        # note we have some tests in test_polymorphic_rel that query against 
    2627        # a subclass, then refer to the superclass that has a with_polymorphic 
    2628        # on it (such as test_join_from_polymorphic_explicit_aliased_three). 
    2629        # these tests actually adapt the polymorphic selectable (like, the 
    2630        # UNION or the SELECT subquery with JOIN in it) to be just the simple 
    2631        # subclass table.   Hence even if we are a "plain" inheriting mapper 
    2632        # but our base has a wpoly on it, we turn on adaption.  This is a 
    2633        # legacy case we should probably disable. 
    2634        # 
    2635        # 
    2636        # UPDATE: simplified way more as of #8168.   polymorphic adaption 
    2637        # is turned off even if with_polymorphic is set, as long as there 
    2638        # is no user-defined aliased selectable / subquery configured. 
    2639        # this scales back the use of polymorphic adaption in practice 
    2640        # to basically no cases except for concrete inheritance with a 
    2641        # polymorphic base class. 
    2642        # 
    2643        return ( 
    2644            self._has_aliased_polymorphic_fromclause 
    2645            or self._requires_row_aliasing 
    2646            or (self.base_mapper._has_aliased_polymorphic_fromclause) 
    2647            or self.base_mapper._requires_row_aliasing 
    2648        ) 
    2649 
    2650    @HasMemoized.memoized_attribute 
    2651    def _with_polymorphic_mappers(self) -> Sequence[Mapper[Any]]: 
    2652        self._check_configure() 
    2653 
    2654        if not self.with_polymorphic: 
    2655            return [] 
    2656        return self._mappers_from_spec(*self.with_polymorphic) 
    2657 
    2658    @HasMemoized.memoized_attribute 
    2659    def _post_inspect(self): 
    2660        """This hook is invoked by attribute inspection. 
    2661 
    2662        E.g. when Query calls: 
    2663 
    2664            coercions.expect(roles.ColumnsClauseRole, ent, keep_inspect=True) 
    2665 
    2666        This allows the inspection process run a configure mappers hook. 
    2667 
    2668        """ 
    2669        self._check_configure() 
    2670 
    2671    @HasMemoized_ro_memoized_attribute 
    2672    def _with_polymorphic_selectable(self) -> FromClause: 
    2673        if not self.with_polymorphic: 
    2674            return self.persist_selectable 
    2675 
    2676        spec, selectable = self.with_polymorphic 
    2677        if selectable is not None: 
    2678            return selectable 
    2679        else: 
    2680            return self._selectable_from_mappers( 
    2681                self._mappers_from_spec(spec, selectable), False 
    2682            ) 
    2683 
    2684    with_polymorphic_mappers = _with_polymorphic_mappers 
    2685    """The list of :class:`_orm.Mapper` objects included in the 
    2686    default "polymorphic" query. 
    2687 
    2688    """ 
    2689 
    2690    @HasMemoized_ro_memoized_attribute 
    2691    def _insert_cols_evaluating_none(self): 
    2692        return { 
    2693            table: frozenset( 
    2694                col for col in columns if col.type.should_evaluate_none 
    2695            ) 
    2696            for table, columns in self._cols_by_table.items() 
    2697        } 
    2698 
    2699    @HasMemoized.memoized_attribute 
    2700    def _insert_cols_as_none(self): 
    2701        return { 
    2702            table: frozenset( 
    2703                col.key 
    2704                for col in columns 
    2705                if not col.primary_key 
    2706                and not col.server_default 
    2707                and not col.default 
    2708                and not col.type.should_evaluate_none 
    2709            ) 
    2710            for table, columns in self._cols_by_table.items() 
    2711        } 
    2712 
    2713    @HasMemoized.memoized_attribute 
    2714    def _propkey_to_col(self): 
    2715        return { 
    2716            table: {self._columntoproperty[col].key: col for col in columns} 
    2717            for table, columns in self._cols_by_table.items() 
    2718        } 
    2719 
    2720    @HasMemoized.memoized_attribute 
    2721    def _pk_keys_by_table(self): 
    2722        return { 
    2723            table: frozenset([col.key for col in pks]) 
    2724            for table, pks in self._pks_by_table.items() 
    2725        } 
    2726 
    2727    @HasMemoized.memoized_attribute 
    2728    def _pk_attr_keys_by_table(self): 
    2729        return { 
    2730            table: frozenset([self._columntoproperty[col].key for col in pks]) 
    2731            for table, pks in self._pks_by_table.items() 
    2732        } 
    2733 
    2734    @HasMemoized.memoized_attribute 
    2735    def _server_default_cols( 
    2736        self, 
    2737    ) -> Mapping[FromClause, FrozenSet[Column[Any]]]: 
    2738        return { 
    2739            table: frozenset( 
    2740                [ 
    2741                    col 
    2742                    for col in cast("Iterable[Column[Any]]", columns) 
    2743                    if col.server_default is not None 
    2744                    or ( 
    2745                        col.default is not None 
    2746                        and col.default.is_clause_element 
    2747                    ) 
    2748                ] 
    2749            ) 
    2750            for table, columns in self._cols_by_table.items() 
    2751        } 
    2752 
    2753    @HasMemoized.memoized_attribute 
    2754    def _server_onupdate_default_cols( 
    2755        self, 
    2756    ) -> Mapping[FromClause, FrozenSet[Column[Any]]]: 
    2757        return { 
    2758            table: frozenset( 
    2759                [ 
    2760                    col 
    2761                    for col in cast("Iterable[Column[Any]]", columns) 
    2762                    if col.server_onupdate is not None 
    2763                    or ( 
    2764                        col.onupdate is not None 
    2765                        and col.onupdate.is_clause_element 
    2766                    ) 
    2767                ] 
    2768            ) 
    2769            for table, columns in self._cols_by_table.items() 
    2770        } 
    2771 
    2772    @HasMemoized.memoized_attribute 
    2773    def _server_default_col_keys(self) -> Mapping[FromClause, FrozenSet[str]]: 
    2774        return { 
    2775            table: frozenset(col.key for col in cols if col.key is not None) 
    2776            for table, cols in self._server_default_cols.items() 
    2777        } 
    2778 
    2779    @HasMemoized.memoized_attribute 
    2780    def _server_onupdate_default_col_keys( 
    2781        self, 
    2782    ) -> Mapping[FromClause, FrozenSet[str]]: 
    2783        return { 
    2784            table: frozenset(col.key for col in cols if col.key is not None) 
    2785            for table, cols in self._server_onupdate_default_cols.items() 
    2786        } 
    2787 
    2788    @HasMemoized.memoized_attribute 
    2789    def _server_default_plus_onupdate_propkeys(self) -> Set[str]: 
    2790        result: Set[str] = set() 
    2791 
    2792        col_to_property = self._columntoproperty 
    2793        for table, columns in self._server_default_cols.items(): 
    2794            result.update( 
    2795                col_to_property[col].key 
    2796                for col in columns.intersection(col_to_property) 
    2797            ) 
    2798        for table, columns in self._server_onupdate_default_cols.items(): 
    2799            result.update( 
    2800                col_to_property[col].key 
    2801                for col in columns.intersection(col_to_property) 
    2802            ) 
    2803        return result 
    2804 
    2805    @HasMemoized.memoized_instancemethod 
    2806    def __clause_element__(self): 
    2807        annotations: Dict[str, Any] = { 
    2808            "entity_namespace": self, 
    2809            "parententity": self, 
    2810            "parentmapper": self, 
    2811        } 
    2812        if self.persist_selectable is not self.local_table: 
    2813            # joined table inheritance, with polymorphic selectable, 
    2814            # etc. 
    2815            annotations["dml_table"] = self.local_table._annotate( 
    2816                { 
    2817                    "entity_namespace": self, 
    2818                    "parententity": self, 
    2819                    "parentmapper": self, 
    2820                } 
    2821            )._set_propagate_attrs( 
    2822                {"compile_state_plugin": "orm", "plugin_subject": self} 
    2823            ) 
    2824 
    2825        return self.selectable._annotate(annotations)._set_propagate_attrs( 
    2826            {"compile_state_plugin": "orm", "plugin_subject": self} 
    2827        ) 
    2828 
    2829    @util.memoized_property 
    2830    def select_identity_token(self): 
    2831        return ( 
    2832            expression.null() 
    2833            ._annotate( 
    2834                { 
    2835                    "entity_namespace": self, 
    2836                    "parententity": self, 
    2837                    "parentmapper": self, 
    2838                    "identity_token": True, 
    2839                } 
    2840            ) 
    2841            ._set_propagate_attrs( 
    2842                {"compile_state_plugin": "orm", "plugin_subject": self} 
    2843            ) 
    2844        ) 
    2845 
    2846    @property 
    2847    def selectable(self) -> FromClause: 
    2848        """The :class:`_schema.FromClause` construct this 
    2849        :class:`_orm.Mapper` selects from by default. 
    2850 
    2851        Normally, this is equivalent to :attr:`.persist_selectable`, unless 
    2852        the ``with_polymorphic`` feature is in use, in which case the 
    2853        full "polymorphic" selectable is returned. 
    2854 
    2855        """ 
    2856        return self._with_polymorphic_selectable 
    2857 
    2858    def _with_polymorphic_args( 
    2859        self, 
    2860        spec: Any = None, 
    2861        selectable: Union[Literal[False, None], FromClause] = False, 
    2862        innerjoin: bool = False, 
    2863    ) -> Tuple[Sequence[Mapper[Any]], FromClause]: 
    2864        if selectable not in (None, False): 
    2865            selectable = coercions.expect( 
    2866                roles.FromClauseRole, 
    2867                selectable, 
    2868            ) 
    2869 
    2870        if self.with_polymorphic: 
    2871            if not spec: 
    2872                spec = self.with_polymorphic[0] 
    2873            if selectable is False: 
    2874                selectable = self.with_polymorphic[1] 
    2875        elif selectable is False: 
    2876            selectable = None 
    2877        mappers = self._mappers_from_spec(spec, selectable) 
    2878        if selectable is not None: 
    2879            return mappers, selectable 
    2880        else: 
    2881            return mappers, self._selectable_from_mappers(mappers, innerjoin) 
    2882 
    2883    @HasMemoized.memoized_attribute 
    2884    def _polymorphic_properties(self): 
    2885        return list( 
    2886            self._iterate_polymorphic_properties( 
    2887                self._with_polymorphic_mappers 
    2888            ) 
    2889        ) 
    2890 
    2891    @property 
    2892    def _all_column_expressions(self): 
    2893        poly_properties = self._polymorphic_properties 
    2894        adapter = self._polymorphic_adapter 
    2895 
    2896        return [ 
    2897            adapter.columns[c] if adapter else c 
    2898            for prop in poly_properties 
    2899            if isinstance(prop, properties.ColumnProperty) 
    2900            and prop._renders_in_subqueries 
    2901            for c in prop.columns 
    2902        ] 
    2903 
    2904    def _columns_plus_keys(self, polymorphic_mappers=()): 
    2905        if polymorphic_mappers: 
    2906            poly_properties = self._iterate_polymorphic_properties( 
    2907                polymorphic_mappers 
    2908            ) 
    2909        else: 
    2910            poly_properties = self._polymorphic_properties 
    2911 
    2912        return [ 
    2913            (prop.key, prop.columns[0]) 
    2914            for prop in poly_properties 
    2915            if isinstance(prop, properties.ColumnProperty) 
    2916        ] 
    2917 
    2918    @HasMemoized.memoized_attribute 
    2919    def _polymorphic_adapter(self) -> Optional[orm_util.ORMAdapter]: 
    2920        if self._has_aliased_polymorphic_fromclause: 
    2921            return orm_util.ORMAdapter( 
    2922                orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 
    2923                self, 
    2924                selectable=self.selectable, 
    2925                equivalents=self._equivalent_columns, 
    2926                limit_on_entity=False, 
    2927            ) 
    2928        else: 
    2929            return None 
    2930 
    2931    def _iterate_polymorphic_properties(self, mappers=None): 
    2932        """Return an iterator of MapperProperty objects which will render into 
    2933        a SELECT.""" 
    2934        if mappers is None: 
    2935            mappers = self._with_polymorphic_mappers 
    2936 
    2937        if not mappers: 
    2938            for c in self.iterate_properties: 
    2939                yield c 
    2940        else: 
    2941            # in the polymorphic case, filter out discriminator columns 
    2942            # from other mappers, as these are sometimes dependent on that 
    2943            # mapper's polymorphic selectable (which we don't want rendered) 
    2944            for c in util.unique_list( 
    2945                chain( 
    2946                    *[ 
    2947                        list(mapper.iterate_properties) 
    2948                        for mapper in [self] + mappers 
    2949                    ] 
    2950                ) 
    2951            ): 
    2952                if getattr(c, "_is_polymorphic_discriminator", False) and ( 
    2953                    self.polymorphic_on is None 
    2954                    or c.columns[0] is not self.polymorphic_on 
    2955                ): 
    2956                    continue 
    2957                yield c 
    2958 
    2959    @HasMemoized.memoized_attribute 
    2960    def attrs(self) -> util.ReadOnlyProperties[MapperProperty[Any]]: 
    2961        """A namespace of all :class:`.MapperProperty` objects 
    2962        associated this mapper. 
    2963 
    2964        This is an object that provides each property based on 
    2965        its key name.  For instance, the mapper for a 
    2966        ``User`` class which has ``User.name`` attribute would 
    2967        provide ``mapper.attrs.name``, which would be the 
    2968        :class:`.ColumnProperty` representing the ``name`` 
    2969        column.   The namespace object can also be iterated, 
    2970        which would yield each :class:`.MapperProperty`. 
    2971 
    2972        :class:`_orm.Mapper` has several pre-filtered views 
    2973        of this attribute which limit the types of properties 
    2974        returned, including :attr:`.synonyms`, :attr:`.column_attrs`, 
    2975        :attr:`.relationships`, and :attr:`.composites`. 
    2976 
    2977        .. warning:: 
    2978 
    2979            The :attr:`_orm.Mapper.attrs` accessor namespace is an 
    2980            instance of :class:`.OrderedProperties`.  This is 
    2981            a dictionary-like object which includes a small number of 
    2982            named methods such as :meth:`.OrderedProperties.items` 
    2983            and :meth:`.OrderedProperties.values`.  When 
    2984            accessing attributes dynamically, favor using the dict-access 
    2985            scheme, e.g. ``mapper.attrs[somename]`` over 
    2986            ``getattr(mapper.attrs, somename)`` to avoid name collisions. 
    2987 
    2988        .. seealso:: 
    2989 
    2990            :attr:`_orm.Mapper.all_orm_descriptors` 
    2991 
    2992        """ 
    2993 
    2994        self._check_configure() 
    2995        return util.ReadOnlyProperties(self._props) 
    2996 
    2997    @HasMemoized.memoized_attribute 
    2998    def all_orm_descriptors(self) -> util.ReadOnlyProperties[InspectionAttr]: 
    2999        """A namespace of all :class:`.InspectionAttr` attributes associated 
    3000        with the mapped class. 
    3001 
    3002        These attributes are in all cases Python :term:`descriptors` 
    3003        associated with the mapped class or its superclasses. 
    3004 
    3005        This namespace includes attributes that are mapped to the class 
    3006        as well as attributes declared by extension modules. 
    3007        It includes any Python descriptor type that inherits from 
    3008        :class:`.InspectionAttr`.  This includes 
    3009        :class:`.QueryableAttribute`, as well as extension types such as 
    3010        :class:`.hybrid_property`, :class:`.hybrid_method` and 
    3011        :class:`.AssociationProxy`. 
    3012 
    3013        To distinguish between mapped attributes and extension attributes, 
    3014        the attribute :attr:`.InspectionAttr.extension_type` will refer 
    3015        to a constant that distinguishes between different extension types. 
    3016 
    3017        The sorting of the attributes is based on the following rules: 
    3018 
    3019        1. Iterate through the class and its superclasses in order from 
    3020           subclass to superclass (i.e. iterate through ``cls.__mro__``) 
    3021 
    3022        2. For each class, yield the attributes in the order in which they 
    3023           appear in ``__dict__``, with the exception of those in step 
    3024           3 below.  The order will be the 
    3025           same as that of the class' construction, with the exception 
    3026           of attributes that were added after the fact by the application 
    3027           or the mapper. 
    3028 
    3029        3. If a certain attribute key is also in the superclass ``__dict__``, 
    3030           then it's included in the iteration for that class, and not the 
    3031           class in which it first appeared. 
    3032 
    3033        The above process produces an ordering that is deterministic in terms 
    3034        of the order in which attributes were assigned to the class. 
    3035 
    3036        When dealing with a :class:`.QueryableAttribute`, the 
    3037        :attr:`.QueryableAttribute.property` attribute refers to the 
    3038        :class:`.MapperProperty` property, which is what you get when 
    3039        referring to the collection of mapped properties via 
    3040        :attr:`_orm.Mapper.attrs`. 
    3041 
    3042        .. warning:: 
    3043 
    3044            The :attr:`_orm.Mapper.all_orm_descriptors` 
    3045            accessor namespace is an 
    3046            instance of :class:`.OrderedProperties`.  This is 
    3047            a dictionary-like object which includes a small number of 
    3048            named methods such as :meth:`.OrderedProperties.items` 
    3049            and :meth:`.OrderedProperties.values`.  When 
    3050            accessing attributes dynamically, favor using the dict-access 
    3051            scheme, e.g. ``mapper.all_orm_descriptors[somename]`` over 
    3052            ``getattr(mapper.all_orm_descriptors, somename)`` to avoid name 
    3053            collisions. 
    3054 
    3055        .. seealso:: 
    3056 
    3057            :attr:`_orm.Mapper.attrs` 
    3058 
    3059        """ 
    3060        return util.ReadOnlyProperties( 
    3061            dict(self.class_manager._all_sqla_attributes()) 
    3062        ) 
    3063 
    3064    @HasMemoized.memoized_attribute 
    3065    @util.preload_module("sqlalchemy.orm.descriptor_props") 
    3066    def _pk_synonyms(self) -> Dict[str, str]: 
    3067        """return a dictionary of {syn_attribute_name: pk_attr_name} for 
    3068        all synonyms that refer to primary key columns 
    3069 
    3070        """ 
    3071        descriptor_props = util.preloaded.orm_descriptor_props 
    3072 
    3073        pk_keys = {prop.key for prop in self._identity_key_props} 
    3074 
    3075        return { 
    3076            syn.key: syn.name 
    3077            for k, syn in self._props.items() 
    3078            if isinstance(syn, descriptor_props.SynonymProperty) 
    3079            and syn.name in pk_keys 
    3080        } 
    3081 
    3082    @HasMemoized.memoized_attribute 
    3083    @util.preload_module("sqlalchemy.orm.descriptor_props") 
    3084    def synonyms(self) -> util.ReadOnlyProperties[SynonymProperty[Any]]: 
    3085        """Return a namespace of all :class:`.Synonym` 
    3086        properties maintained by this :class:`_orm.Mapper`. 
    3087 
    3088        .. seealso:: 
    3089 
    3090            :attr:`_orm.Mapper.attrs` - namespace of all 
    3091            :class:`.MapperProperty` 
    3092            objects. 
    3093 
    3094        """ 
    3095        descriptor_props = util.preloaded.orm_descriptor_props 
    3096 
    3097        return self._filter_properties(descriptor_props.SynonymProperty) 
    3098 
    3099    @util.ro_non_memoized_property 
    3100    def entity_namespace(self) -> _EntityNamespace: 
    3101        return self.class_  # type: ignore[return-value] 
    3102 
    3103    @HasMemoized.memoized_attribute 
    3104    def column_attrs(self) -> util.ReadOnlyProperties[ColumnProperty[Any]]: 
    3105        """Return a namespace of all :class:`.ColumnProperty` 
    3106        properties maintained by this :class:`_orm.Mapper`. 
    3107 
    3108        .. seealso:: 
    3109 
    3110            :attr:`_orm.Mapper.attrs` - namespace of all 
    3111            :class:`.MapperProperty` 
    3112            objects. 
    3113 
    3114        """ 
    3115        return self._filter_properties(properties.ColumnProperty) 
    3116 
    3117    @HasMemoized.memoized_attribute 
    3118    @util.preload_module("sqlalchemy.orm.relationships") 
    3119    def relationships( 
    3120        self, 
    3121    ) -> util.ReadOnlyProperties[RelationshipProperty[Any]]: 
    3122        """A namespace of all :class:`.Relationship` properties 
    3123        maintained by this :class:`_orm.Mapper`. 
    3124 
    3125        .. warning:: 
    3126 
    3127            the :attr:`_orm.Mapper.relationships` accessor namespace is an 
    3128            instance of :class:`.OrderedProperties`.  This is 
    3129            a dictionary-like object which includes a small number of 
    3130            named methods such as :meth:`.OrderedProperties.items` 
    3131            and :meth:`.OrderedProperties.values`.  When 
    3132            accessing attributes dynamically, favor using the dict-access 
    3133            scheme, e.g. ``mapper.relationships[somename]`` over 
    3134            ``getattr(mapper.relationships, somename)`` to avoid name 
    3135            collisions. 
    3136 
    3137        .. seealso:: 
    3138 
    3139            :attr:`_orm.Mapper.attrs` - namespace of all 
    3140            :class:`.MapperProperty` 
    3141            objects. 
    3142 
    3143        """ 
    3144        return self._filter_properties( 
    3145            util.preloaded.orm_relationships.RelationshipProperty 
    3146        ) 
    3147 
    3148    @HasMemoized.memoized_attribute 
    3149    @util.preload_module("sqlalchemy.orm.descriptor_props") 
    3150    def composites(self) -> util.ReadOnlyProperties[CompositeProperty[Any]]: 
    3151        """Return a namespace of all :class:`.Composite` 
    3152        properties maintained by this :class:`_orm.Mapper`. 
    3153 
    3154        .. seealso:: 
    3155 
    3156            :attr:`_orm.Mapper.attrs` - namespace of all 
    3157            :class:`.MapperProperty` 
    3158            objects. 
    3159 
    3160        """ 
    3161        return self._filter_properties( 
    3162            util.preloaded.orm_descriptor_props.CompositeProperty 
    3163        ) 
    3164 
    3165    def _filter_properties( 
    3166        self, type_: Type[_MP] 
    3167    ) -> util.ReadOnlyProperties[_MP]: 
    3168        self._check_configure() 
    3169        return util.ReadOnlyProperties( 
    3170            util.OrderedDict( 
    3171                (k, v) for k, v in self._props.items() if isinstance(v, type_) 
    3172            ) 
    3173        ) 
    3174 
    3175    @HasMemoized.memoized_attribute 
    3176    def _get_clause(self): 
    3177        """create a "get clause" based on the primary key.  this is used 
    3178        by query.get() and many-to-one lazyloads to load this item 
    3179        by primary key. 
    3180 
    3181        """ 
    3182        params = [ 
    3183            ( 
    3184                primary_key, 
    3185                sql.bindparam("pk_%d" % idx, type_=primary_key.type), 
    3186            ) 
    3187            for idx, primary_key in enumerate(self.primary_key, 1) 
    3188        ] 
    3189        return ( 
    3190            sql.and_(*[k == v for (k, v) in params]), 
    3191            util.column_dict(params), 
    3192        ) 
    3193 
    3194    @HasMemoized.memoized_attribute 
    3195    def _equivalent_columns(self) -> _EquivalentColumnMap: 
    3196        """Create a map of all equivalent columns, based on 
    3197        the determination of column pairs that are equated to 
    3198        one another based on inherit condition.  This is designed 
    3199        to work with the queries that util.polymorphic_union 
    3200        comes up with, which often don't include the columns from 
    3201        the base table directly (including the subclass table columns 
    3202        only). 
    3203 
    3204        The resulting structure is a dictionary of columns mapped 
    3205        to lists of equivalent columns, e.g.:: 
    3206 
    3207            {tablea.col1: {tableb.col1, tablec.col1}, tablea.col2: {tabled.col2}} 
    3208 
    3209        """  # noqa: E501 
    3210        result: _EquivalentColumnMap = {} 
    3211 
    3212        def visit_binary(binary): 
    3213            if binary.operator == operators.eq: 
    3214                if binary.left in result: 
    3215                    result[binary.left].add(binary.right) 
    3216                else: 
    3217                    result[binary.left] = {binary.right} 
    3218                if binary.right in result: 
    3219                    result[binary.right].add(binary.left) 
    3220                else: 
    3221                    result[binary.right] = {binary.left} 
    3222 
    3223        for mapper in self.base_mapper.self_and_descendants: 
    3224            if mapper.inherit_condition is not None: 
    3225                visitors.traverse( 
    3226                    mapper.inherit_condition, {}, {"binary": visit_binary} 
    3227                ) 
    3228 
    3229        return result 
    3230 
    3231    def _is_userland_descriptor(self, assigned_name: str, obj: Any) -> bool: 
    3232        if isinstance( 
    3233            obj, 
    3234            ( 
    3235                _MappedAttribute, 
    3236                instrumentation.ClassManager, 
    3237                expression.ColumnElement, 
    3238            ), 
    3239        ): 
    3240            return False 
    3241        else: 
    3242            return assigned_name not in self._dataclass_fields 
    3243 
    3244    @HasMemoized.memoized_attribute 
    3245    def _dataclass_fields(self): 
    3246        return [f.name for f in util.dataclass_fields(self.class_)] 
    3247 
    3248    def _should_exclude(self, name, assigned_name, local, column): 
    3249        """determine whether a particular property should be implicitly 
    3250        present on the class. 
    3251 
    3252        This occurs when properties are propagated from an inherited class, or 
    3253        are applied from the columns present in the mapped table. 
    3254 
    3255        """ 
    3256 
    3257        if column is not None and sql_base._never_select_column(column): 
    3258            return True 
    3259 
    3260        # check for class-bound attributes and/or descriptors, 
    3261        # either local or from an inherited class 
    3262        # ignore dataclass field default values 
    3263        if local: 
    3264            if self.class_.__dict__.get( 
    3265                assigned_name, None 
    3266            ) is not None and self._is_userland_descriptor( 
    3267                assigned_name, self.class_.__dict__[assigned_name] 
    3268            ): 
    3269                return True 
    3270        else: 
    3271            attr = self.class_manager._get_class_attr_mro(assigned_name, None) 
    3272            if attr is not None and self._is_userland_descriptor( 
    3273                assigned_name, attr 
    3274            ): 
    3275                return True 
    3276 
    3277        if ( 
    3278            self.include_properties is not None 
    3279            and name not in self.include_properties 
    3280            and (column is None or column not in self.include_properties) 
    3281        ): 
    3282            self._log("not including property %s" % (name)) 
    3283            return True 
    3284 
    3285        if self.exclude_properties is not None and ( 
    3286            name in self.exclude_properties 
    3287            or (column is not None and column in self.exclude_properties) 
    3288        ): 
    3289            self._log("excluding property %s" % (name)) 
    3290            return True 
    3291 
    3292        return False 
    3293 
    3294    def common_parent(self, other: Mapper[Any]) -> bool: 
    3295        """Return true if the given mapper shares a 
    3296        common inherited parent as this mapper.""" 
    3297 
    3298        return self.base_mapper is other.base_mapper 
    3299 
    3300    def is_sibling(self, other: Mapper[Any]) -> bool: 
    3301        """return true if the other mapper is an inheriting sibling to this 
    3302        one.  common parent but different branch 
    3303 
    3304        """ 
    3305        return ( 
    3306            self.base_mapper is other.base_mapper 
    3307            and not self.isa(other) 
    3308            and not other.isa(self) 
    3309        ) 
    3310 
    3311    def _canload( 
    3312        self, state: InstanceState[Any], allow_subtypes: bool 
    3313    ) -> bool: 
    3314        s = self.primary_mapper() 
    3315        if self.polymorphic_on is not None or allow_subtypes: 
    3316            return _state_mapper(state).isa(s) 
    3317        else: 
    3318            return _state_mapper(state) is s 
    3319 
    3320    def isa(self, other: Mapper[Any]) -> bool: 
    3321        """Return True if the this mapper inherits from the given mapper.""" 
    3322 
    3323        m: Optional[Mapper[Any]] = self 
    3324        while m and m is not other: 
    3325            m = m.inherits 
    3326        return bool(m) 
    3327 
    3328    def iterate_to_root(self) -> Iterator[Mapper[Any]]: 
    3329        m: Optional[Mapper[Any]] = self 
    3330        while m: 
    3331            yield m 
    3332            m = m.inherits 
    3333 
    3334    @HasMemoized.memoized_attribute 
    3335    def self_and_descendants(self) -> Sequence[Mapper[Any]]: 
    3336        """The collection including this mapper and all descendant mappers. 
    3337 
    3338        This includes not just the immediately inheriting mappers but 
    3339        all their inheriting mappers as well. 
    3340 
    3341        """ 
    3342        descendants = [] 
    3343        stack = deque([self]) 
    3344        while stack: 
    3345            item = stack.popleft() 
    3346            descendants.append(item) 
    3347            stack.extend(item._inheriting_mappers) 
    3348        return util.WeakSequence(descendants) 
    3349 
    3350    def polymorphic_iterator(self) -> Iterator[Mapper[Any]]: 
    3351        """Iterate through the collection including this mapper and 
    3352        all descendant mappers. 
    3353 
    3354        This includes not just the immediately inheriting mappers but 
    3355        all their inheriting mappers as well. 
    3356 
    3357        To iterate through an entire hierarchy, use 
    3358        ``mapper.base_mapper.polymorphic_iterator()``. 
    3359 
    3360        """ 
    3361        return iter(self.self_and_descendants) 
    3362 
    3363    def primary_mapper(self) -> Mapper[Any]: 
    3364        """Return the primary mapper corresponding to this mapper's class key 
    3365        (class).""" 
    3366 
    3367        return self.class_manager.mapper 
    3368 
    3369    @property 
    3370    def primary_base_mapper(self) -> Mapper[Any]: 
    3371        return self.class_manager.mapper.base_mapper 
    3372 
    3373    def _result_has_identity_key(self, result, adapter=None): 
    3374        pk_cols: Sequence[ColumnElement[Any]] 
    3375        if adapter is not None: 
    3376            pk_cols = [adapter.columns[c] for c in self.primary_key] 
    3377        else: 
    3378            pk_cols = self.primary_key 
    3379        rk = result.keys() 
    3380        for col in pk_cols: 
    3381            if col not in rk: 
    3382                return False 
    3383        else: 
    3384            return True 
    3385 
    3386    def identity_key_from_row( 
    3387        self, 
    3388        row: Union[Row[Unpack[TupleAny]], RowMapping], 
    3389        identity_token: Optional[Any] = None, 
    3390        adapter: Optional[ORMAdapter] = None, 
    3391    ) -> _IdentityKeyType[_O]: 
    3392        """Return an identity-map key for use in storing/retrieving an 
    3393        item from the identity map. 
    3394 
    3395        :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 
    3396         result set that selected from the ORM mapped primary key columns. 
    3397 
    3398         .. versionchanged:: 2.0 
    3399            :class:`.Row` or :class:`.RowMapping` are accepted 
    3400            for the "row" argument 
    3401 
    3402        """ 
    3403        pk_cols: Sequence[ColumnElement[Any]] 
    3404        if adapter is not None: 
    3405            pk_cols = [adapter.columns[c] for c in self.primary_key] 
    3406        else: 
    3407            pk_cols = self.primary_key 
    3408 
    3409        mapping: RowMapping 
    3410        if hasattr(row, "_mapping"): 
    3411            mapping = row._mapping 
    3412        else: 
    3413            mapping = row  # type: ignore[assignment] 
    3414 
    3415        return ( 
    3416            self._identity_class, 
    3417            tuple(mapping[column] for column in pk_cols), 
    3418            identity_token, 
    3419        ) 
    3420 
    3421    def identity_key_from_primary_key( 
    3422        self, 
    3423        primary_key: Tuple[Any, ...], 
    3424        identity_token: Optional[Any] = None, 
    3425    ) -> _IdentityKeyType[_O]: 
    3426        """Return an identity-map key for use in storing/retrieving an 
    3427        item from an identity map. 
    3428 
    3429        :param primary_key: A list of values indicating the identifier. 
    3430 
    3431        """ 
    3432        return ( 
    3433            self._identity_class, 
    3434            tuple(primary_key), 
    3435            identity_token, 
    3436        ) 
    3437 
    3438    def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 
    3439        """Return the identity key for the given instance, based on 
    3440        its primary key attributes. 
    3441 
    3442        If the instance's state is expired, calling this method 
    3443        will result in a database check to see if the object has been deleted. 
    3444        If the row no longer exists, 
    3445        :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 
    3446 
    3447        This value is typically also found on the instance state under the 
    3448        attribute name `key`. 
    3449 
    3450        """ 
    3451        state = attributes.instance_state(instance) 
    3452        return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 
    3453 
    3454    def _identity_key_from_state( 
    3455        self, 
    3456        state: InstanceState[_O], 
    3457        passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 
    3458    ) -> _IdentityKeyType[_O]: 
    3459        dict_ = state.dict 
    3460        manager = state.manager 
    3461        return ( 
    3462            self._identity_class, 
    3463            tuple( 
    3464                [ 
    3465                    manager[prop.key].impl.get(state, dict_, passive) 
    3466                    for prop in self._identity_key_props 
    3467                ] 
    3468            ), 
    3469            state.identity_token, 
    3470        ) 
    3471 
    3472    def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 
    3473        """Return the list of primary key values for the given 
    3474        instance. 
    3475 
    3476        If the instance's state is expired, calling this method 
    3477        will result in a database check to see if the object has been deleted. 
    3478        If the row no longer exists, 
    3479        :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 
    3480 
    3481        """ 
    3482        state = attributes.instance_state(instance) 
    3483        identity_key = self._identity_key_from_state( 
    3484            state, PassiveFlag.PASSIVE_OFF 
    3485        ) 
    3486        return identity_key[1] 
    3487 
    3488    @HasMemoized.memoized_attribute 
    3489    def _persistent_sortkey_fn(self): 
    3490        key_fns = [col.type.sort_key_function for col in self.primary_key] 
    3491 
    3492        if set(key_fns).difference([None]): 
    3493 
    3494            def key(state): 
    3495                return tuple( 
    3496                    key_fn(val) if key_fn is not None else val 
    3497                    for key_fn, val in zip(key_fns, state.key[1]) 
    3498                ) 
    3499 
    3500        else: 
    3501 
    3502            def key(state): 
    3503                return state.key[1] 
    3504 
    3505        return key 
    3506 
    3507    @HasMemoized.memoized_attribute 
    3508    def _identity_key_props(self): 
    3509        return [self._columntoproperty[col] for col in self.primary_key] 
    3510 
    3511    @HasMemoized.memoized_attribute 
    3512    def _all_pk_cols(self): 
    3513        collection: Set[ColumnClause[Any]] = set() 
    3514        for table in self.tables: 
    3515            collection.update(self._pks_by_table[table]) 
    3516        return collection 
    3517 
    3518    @HasMemoized.memoized_attribute 
    3519    def _should_undefer_in_wildcard(self): 
    3520        cols: Set[ColumnElement[Any]] = set(self.primary_key) 
    3521        if self.polymorphic_on is not None: 
    3522            cols.add(self.polymorphic_on) 
    3523        return cols 
    3524 
    3525    @HasMemoized.memoized_attribute 
    3526    def _primary_key_propkeys(self): 
    3527        return {self._columntoproperty[col].key for col in self._all_pk_cols} 
    3528 
    3529    def _get_state_attr_by_column( 
    3530        self, 
    3531        state: InstanceState[_O], 
    3532        dict_: _InstanceDict, 
    3533        column: ColumnElement[Any], 
    3534        passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 
    3535    ) -> Any: 
    3536        prop = self._columntoproperty[column] 
    3537        return state.manager[prop.key].impl.get(state, dict_, passive=passive) 
    3538 
    3539    def _set_committed_state_attr_by_column(self, state, dict_, column, value): 
    3540        prop = self._columntoproperty[column] 
    3541        state.manager[prop.key].impl.set_committed_value(state, dict_, value) 
    3542 
    3543    def _set_state_attr_by_column(self, state, dict_, column, value): 
    3544        prop = self._columntoproperty[column] 
    3545        state.manager[prop.key].impl.set(state, dict_, value, None) 
    3546 
    3547    def _get_committed_attr_by_column(self, obj, column): 
    3548        state = attributes.instance_state(obj) 
    3549        dict_ = attributes.instance_dict(obj) 
    3550        return self._get_committed_state_attr_by_column( 
    3551            state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 
    3552        ) 
    3553 
    3554    def _get_committed_state_attr_by_column( 
    3555        self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 
    3556    ): 
    3557        prop = self._columntoproperty[column] 
    3558        return state.manager[prop.key].impl.get_committed_value( 
    3559            state, dict_, passive=passive 
    3560        ) 
    3561 
    3562    def _optimized_get_statement(self, state, attribute_names): 
    3563        """assemble a WHERE clause which retrieves a given state by primary 
    3564        key, using a minimized set of tables. 
    3565 
    3566        Applies to a joined-table inheritance mapper where the 
    3567        requested attribute names are only present on joined tables, 
    3568        not the base table.  The WHERE clause attempts to include 
    3569        only those tables to minimize joins. 
    3570 
    3571        """ 
    3572        props = self._props 
    3573 
    3574        col_attribute_names = set(attribute_names).intersection( 
    3575            state.mapper.column_attrs.keys() 
    3576        ) 
    3577        tables: Set[FromClause] = set( 
    3578            chain( 
    3579                *[ 
    3580                    sql_util.find_tables(c, check_columns=True) 
    3581                    for key in col_attribute_names 
    3582                    for c in props[key].columns 
    3583                ] 
    3584            ) 
    3585        ) 
    3586 
    3587        if self.base_mapper.local_table in tables: 
    3588            return None 
    3589 
    3590        def visit_binary(binary): 
    3591            leftcol = binary.left 
    3592            rightcol = binary.right 
    3593            if leftcol is None or rightcol is None: 
    3594                return 
    3595 
    3596            if leftcol.table not in tables: 
    3597                leftval = self._get_committed_state_attr_by_column( 
    3598                    state, 
    3599                    state.dict, 
    3600                    leftcol, 
    3601                    passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 
    3602                ) 
    3603                if leftval in orm_util._none_set: 
    3604                    raise _OptGetColumnsNotAvailable() 
    3605                binary.left = sql.bindparam( 
    3606                    None, leftval, type_=binary.right.type 
    3607                ) 
    3608            elif rightcol.table not in tables: 
    3609                rightval = self._get_committed_state_attr_by_column( 
    3610                    state, 
    3611                    state.dict, 
    3612                    rightcol, 
    3613                    passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 
    3614                ) 
    3615                if rightval in orm_util._none_set: 
    3616                    raise _OptGetColumnsNotAvailable() 
    3617                binary.right = sql.bindparam( 
    3618                    None, rightval, type_=binary.right.type 
    3619                ) 
    3620 
    3621        allconds: List[ColumnElement[bool]] = [] 
    3622 
    3623        start = False 
    3624 
    3625        # as of #7507, from the lowest base table on upwards, 
    3626        # we include all intermediary tables. 
    3627 
    3628        for mapper in reversed(list(self.iterate_to_root())): 
    3629            if mapper.local_table in tables: 
    3630                start = True 
    3631            elif not isinstance(mapper.local_table, expression.TableClause): 
    3632                return None 
    3633            if start and not mapper.single: 
    3634                assert mapper.inherits 
    3635                assert not mapper.concrete 
    3636                assert mapper.inherit_condition is not None 
    3637                allconds.append(mapper.inherit_condition) 
    3638                tables.add(mapper.local_table) 
    3639 
    3640        # only the bottom table needs its criteria to be altered to fit 
    3641        # the primary key ident - the rest of the tables upwards to the 
    3642        # descendant-most class should all be present and joined to each 
    3643        # other. 
    3644        try: 
    3645            _traversed = visitors.cloned_traverse( 
    3646                allconds[0], {}, {"binary": visit_binary} 
    3647            ) 
    3648        except _OptGetColumnsNotAvailable: 
    3649            return None 
    3650        else: 
    3651            allconds[0] = _traversed 
    3652 
    3653        cond = sql.and_(*allconds) 
    3654 
    3655        cols = [] 
    3656        for key in col_attribute_names: 
    3657            cols.extend(props[key].columns) 
    3658        return ( 
    3659            sql.select(*cols) 
    3660            .where(cond) 
    3661            .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 
    3662        ) 
    3663 
    3664    def _iterate_to_target_viawpoly(self, mapper): 
    3665        if self.isa(mapper): 
    3666            prev = self 
    3667            for m in self.iterate_to_root(): 
    3668                yield m 
    3669 
    3670                if m is not prev and prev not in m._with_polymorphic_mappers: 
    3671                    break 
    3672 
    3673                prev = m 
    3674                if m is mapper: 
    3675                    break 
    3676 
    3677    @HasMemoized.memoized_attribute 
    3678    def _would_selectinload_combinations_cache(self): 
    3679        return {} 
    3680 
    3681    def _would_selectin_load_only_from_given_mapper(self, super_mapper): 
    3682        """return True if this mapper would "selectin" polymorphic load based 
    3683        on the given super mapper, and not from a setting from a subclass. 
    3684 
    3685        given:: 
    3686 
    3687            class A: ... 
    3688 
    3689 
    3690            class B(A): 
    3691                __mapper_args__ = {"polymorphic_load": "selectin"} 
    3692 
    3693 
    3694            class C(B): ... 
    3695 
    3696 
    3697            class D(B): 
    3698                __mapper_args__ = {"polymorphic_load": "selectin"} 
    3699 
    3700        ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 
    3701        returns True, because C does selectin loading because of B's setting. 
    3702 
    3703        OTOH, ``inspect(D) 
    3704        ._would_selectin_load_only_from_given_mapper(inspect(B))`` 
    3705        returns False, because D does selectin loading because of its own 
    3706        setting; when we are doing a selectin poly load from B, we want to 
    3707        filter out D because it would already have its own selectin poly load 
    3708        set up separately. 
    3709 
    3710        Added as part of #9373. 
    3711 
    3712        """ 
    3713        cache = self._would_selectinload_combinations_cache 
    3714 
    3715        try: 
    3716            return cache[super_mapper] 
    3717        except KeyError: 
    3718            pass 
    3719 
    3720        # assert that given object is a supermapper, meaning we already 
    3721        # strong reference it directly or indirectly.  this allows us 
    3722        # to not worry that we are creating new strongrefs to unrelated 
    3723        # mappers or other objects. 
    3724        assert self.isa(super_mapper) 
    3725 
    3726        mapper = super_mapper 
    3727        for m in self._iterate_to_target_viawpoly(mapper): 
    3728            if m.polymorphic_load == "selectin": 
    3729                retval = m is super_mapper 
    3730                break 
    3731        else: 
    3732            retval = False 
    3733 
    3734        cache[super_mapper] = retval 
    3735        return retval 
    3736 
    3737    def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 
    3738        if not enabled_via_opt: 
    3739            # common case, takes place for all polymorphic loads 
    3740            mapper = polymorphic_from 
    3741            for m in self._iterate_to_target_viawpoly(mapper): 
    3742                if m.polymorphic_load == "selectin": 
    3743                    return m 
    3744        else: 
    3745            # uncommon case, selectin load options were used 
    3746            enabled_via_opt = set(enabled_via_opt) 
    3747            enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 
    3748            for entity in enabled_via_opt.union([polymorphic_from]): 
    3749                mapper = entity.mapper 
    3750                for m in self._iterate_to_target_viawpoly(mapper): 
    3751                    if ( 
    3752                        m.polymorphic_load == "selectin" 
    3753                        or m in enabled_via_opt_mappers 
    3754                    ): 
    3755                        return enabled_via_opt_mappers.get(m, m) 
    3756 
    3757        return None 
    3758 
    3759    @util.preload_module("sqlalchemy.orm.strategy_options") 
    3760    def _subclass_load_via_in(self, entity, polymorphic_from): 
    3761        """Assemble a that can load the columns local to 
    3762        this subclass as a SELECT with IN. 
    3763 
    3764        """ 
    3765 
    3766        strategy_options = util.preloaded.orm_strategy_options 
    3767 
    3768        assert self.inherits 
    3769 
    3770        if self.polymorphic_on is not None: 
    3771            polymorphic_prop = self._columntoproperty[self.polymorphic_on] 
    3772            keep_props = set([polymorphic_prop] + self._identity_key_props) 
    3773        else: 
    3774            keep_props = set(self._identity_key_props) 
    3775 
    3776        disable_opt = strategy_options.Load(entity) 
    3777        enable_opt = strategy_options.Load(entity) 
    3778 
    3779        classes_to_include = {self} 
    3780        m: Optional[Mapper[Any]] = self.inherits 
    3781        while ( 
    3782            m is not None 
    3783            and m is not polymorphic_from 
    3784            and m.polymorphic_load == "selectin" 
    3785        ): 
    3786            classes_to_include.add(m) 
    3787            m = m.inherits 
    3788 
    3789        for prop in self.column_attrs + self.relationships: 
    3790            # skip prop keys that are not instrumented on the mapped class. 
    3791            # this is primarily the "_sa_polymorphic_on" property that gets 
    3792            # created for an ad-hoc polymorphic_on SQL expression, issue #8704 
    3793            if prop.key not in self.class_manager: 
    3794                continue 
    3795 
    3796            if prop.parent in classes_to_include or prop in keep_props: 
    3797                # "enable" options, to turn on the properties that we want to 
    3798                # load by default (subject to options from the query) 
    3799                if not isinstance(prop, StrategizedProperty): 
    3800                    continue 
    3801 
    3802                enable_opt = enable_opt._set_generic_strategy( 
    3803                    # convert string name to an attribute before passing 
    3804                    # to loader strategy.   note this must be in terms 
    3805                    # of given entity, such as AliasedClass, etc. 
    3806                    (getattr(entity.entity_namespace, prop.key),), 
    3807                    dict(prop.strategy_key), 
    3808                    _reconcile_to_other=True, 
    3809                ) 
    3810            else: 
    3811                # "disable" options, to turn off the properties from the 
    3812                # superclass that we *don't* want to load, applied after 
    3813                # the options from the query to override them 
    3814                disable_opt = disable_opt._set_generic_strategy( 
    3815                    # convert string name to an attribute before passing 
    3816                    # to loader strategy.   note this must be in terms 
    3817                    # of given entity, such as AliasedClass, etc. 
    3818                    (getattr(entity.entity_namespace, prop.key),), 
    3819                    {"do_nothing": True}, 
    3820                    _reconcile_to_other=False, 
    3821                ) 
    3822 
    3823        primary_key = list(self.primary_key) 
    3824 
    3825        in_expr: ColumnElement[Any] 
    3826 
    3827        if len(primary_key) > 1: 
    3828            in_expr = sql.tuple_(*primary_key) 
    3829        else: 
    3830            in_expr = primary_key[0] 
    3831 
    3832        if entity.is_aliased_class: 
    3833            assert entity.mapper is self 
    3834 
    3835            q = sql.select(entity).set_label_style( 
    3836                LABEL_STYLE_TABLENAME_PLUS_COL 
    3837            ) 
    3838 
    3839            in_expr = entity._adapter.traverse(in_expr) 
    3840            primary_key = [entity._adapter.traverse(k) for k in primary_key] 
    3841            q = q.where( 
    3842                in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 
    3843            ).order_by(*primary_key) 
    3844        else: 
    3845            q = sql.select(self).set_label_style( 
    3846                LABEL_STYLE_TABLENAME_PLUS_COL 
    3847            ) 
    3848            q = q.where( 
    3849                in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 
    3850            ).order_by(*primary_key) 
    3851 
    3852        return q, enable_opt, disable_opt 
    3853 
    3854    @HasMemoized.memoized_attribute 
    3855    def _subclass_load_via_in_mapper(self): 
    3856        # the default is loading this mapper against the basemost mapper 
    3857        return self._subclass_load_via_in(self, self.base_mapper) 
    3858 
    3859    def cascade_iterator( 
    3860        self, 
    3861        type_: str, 
    3862        state: InstanceState[_O], 
    3863        halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 
    3864    ) -> Iterator[ 
    3865        Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 
    3866    ]: 
    3867        r"""Iterate each element and its mapper in an object graph, 
    3868        for all relationships that meet the given cascade rule. 
    3869 
    3870        :param type\_: 
    3871          The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 
    3872          etc.). 
    3873 
    3874          .. note::  the ``"all"`` cascade is not accepted here.  For a generic 
    3875             object traversal function, see :ref:`faq_walk_objects`. 
    3876 
    3877        :param state: 
    3878          The lead InstanceState.  child items will be processed per 
    3879          the relationships defined for this object's mapper. 
    3880 
    3881        :return: the method yields individual object instances. 
    3882 
    3883        .. seealso:: 
    3884 
    3885            :ref:`unitofwork_cascades` 
    3886 
    3887            :ref:`faq_walk_objects` - illustrates a generic function to 
    3888            traverse all objects without relying on cascades. 
    3889 
    3890        """ 
    3891        visited_states: Set[InstanceState[Any]] = set() 
    3892        prp, mpp = object(), object() 
    3893 
    3894        assert state.mapper.isa(self) 
    3895 
    3896        # this is actually a recursive structure, fully typing it seems 
    3897        # a little too difficult for what it's worth here 
    3898        visitables: Deque[ 
    3899            Tuple[ 
    3900                Deque[Any], 
    3901                object, 
    3902                Optional[InstanceState[Any]], 
    3903                Optional[_InstanceDict], 
    3904            ] 
    3905        ] 
    3906 
    3907        visitables = deque( 
    3908            [(deque(state.mapper._props.values()), prp, state, state.dict)] 
    3909        ) 
    3910 
    3911        while visitables: 
    3912            iterator, item_type, parent_state, parent_dict = visitables[-1] 
    3913            if not iterator: 
    3914                visitables.pop() 
    3915                continue 
    3916 
    3917            if item_type is prp: 
    3918                prop = iterator.popleft() 
    3919                if not prop.cascade or type_ not in prop.cascade: 
    3920                    continue 
    3921                assert parent_state is not None 
    3922                assert parent_dict is not None 
    3923                queue = deque( 
    3924                    prop.cascade_iterator( 
    3925                        type_, 
    3926                        parent_state, 
    3927                        parent_dict, 
    3928                        visited_states, 
    3929                        halt_on, 
    3930                    ) 
    3931                ) 
    3932                if queue: 
    3933                    visitables.append((queue, mpp, None, None)) 
    3934            elif item_type is mpp: 
    3935                ( 
    3936                    instance, 
    3937                    instance_mapper, 
    3938                    corresponding_state, 
    3939                    corresponding_dict, 
    3940                ) = iterator.popleft() 
    3941                yield ( 
    3942                    instance, 
    3943                    instance_mapper, 
    3944                    corresponding_state, 
    3945                    corresponding_dict, 
    3946                ) 
    3947                visitables.append( 
    3948                    ( 
    3949                        deque(instance_mapper._props.values()), 
    3950                        prp, 
    3951                        corresponding_state, 
    3952                        corresponding_dict, 
    3953                    ) 
    3954                ) 
    3955 
    3956    @HasMemoized.memoized_attribute 
    3957    def _compiled_cache(self): 
    3958        return util.LRUCache(self._compiled_cache_size) 
    3959 
    3960    @HasMemoized.memoized_attribute 
    3961    def _multiple_persistence_tables(self): 
    3962        return len(self.tables) > 1 
    3963 
    3964    @HasMemoized.memoized_attribute 
    3965    def _sorted_tables(self): 
    3966        table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 
    3967 
    3968        for mapper in self.base_mapper.self_and_descendants: 
    3969            for t in mapper.tables: 
    3970                table_to_mapper.setdefault(t, mapper) 
    3971 
    3972        extra_dependencies = [] 
    3973        for table, mapper in table_to_mapper.items(): 
    3974            super_ = mapper.inherits 
    3975            if super_: 
    3976                extra_dependencies.extend( 
    3977                    [(super_table, table) for super_table in super_.tables] 
    3978                ) 
    3979 
    3980        def skip(fk): 
    3981            # attempt to skip dependencies that are not 
    3982            # significant to the inheritance chain 
    3983            # for two tables that are related by inheritance. 
    3984            # while that dependency may be important, it's technically 
    3985            # not what we mean to sort on here. 
    3986            parent = table_to_mapper.get(fk.parent.table) 
    3987            dep = table_to_mapper.get(fk.column.table) 
    3988            if ( 
    3989                parent is not None 
    3990                and dep is not None 
    3991                and dep is not parent 
    3992                and dep.inherit_condition is not None 
    3993            ): 
    3994                cols = set(sql_util._find_columns(dep.inherit_condition)) 
    3995                if parent.inherit_condition is not None: 
    3996                    cols = cols.union( 
    3997                        sql_util._find_columns(parent.inherit_condition) 
    3998                    ) 
    3999                    return fk.parent not in cols and fk.column not in cols 
    4000                else: 
    4001                    return fk.parent not in cols 
    4002            return False 
    4003 
    4004        sorted_ = sql_util.sort_tables( 
    4005            table_to_mapper, 
    4006            skip_fn=skip, 
    4007            extra_dependencies=extra_dependencies, 
    4008        ) 
    4009 
    4010        ret = util.OrderedDict() 
    4011        for t in sorted_: 
    4012            ret[t] = table_to_mapper[t] 
    4013        return ret 
    4014 
    4015    def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 
    4016        if key in self._memoized_values: 
    4017            return cast(_T, self._memoized_values[key]) 
    4018        else: 
    4019            self._memoized_values[key] = value = callable_() 
    4020            return value 
    4021 
    4022    @util.memoized_property 
    4023    def _table_to_equated(self): 
    4024        """memoized map of tables to collections of columns to be 
    4025        synchronized upwards to the base mapper.""" 
    4026 
    4027        result: util.defaultdict[ 
    4028            Table, 
    4029            List[ 
    4030                Tuple[ 
    4031                    Mapper[Any], 
    4032                    List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 
    4033                ] 
    4034            ], 
    4035        ] = util.defaultdict(list) 
    4036 
    4037        def set_union(x, y): 
    4038            return x.union(y) 
    4039 
    4040        for table in self._sorted_tables: 
    4041            cols = set(table.c) 
    4042 
    4043            for m in self.iterate_to_root(): 
    4044                if m._inherits_equated_pairs and cols.intersection( 
    4045                    reduce( 
    4046                        set_union, 
    4047                        [l.proxy_set for l, r in m._inherits_equated_pairs], 
    4048                    ) 
    4049                ): 
    4050                    result[table].append((m, m._inherits_equated_pairs)) 
    4051 
    4052        return result 
    4053 
    4054 
    4055class _OptGetColumnsNotAvailable(Exception): 
    4056    pass 
    4057 
    4058 
    4059def configure_mappers() -> None: 
    4060    """Initialize the inter-mapper relationships of all mappers that 
    4061    have been constructed thus far across all :class:`_orm.registry` 
    4062    collections. 
    4063 
    4064    The configure step is used to reconcile and initialize the 
    4065    :func:`_orm.relationship` linkages between mapped classes, as well as to 
    4066    invoke configuration events such as the 
    4067    :meth:`_orm.MapperEvents.before_configured` and 
    4068    :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 
    4069    extensions or user-defined extension hooks. 
    4070 
    4071    Mapper configuration is normally invoked automatically, the first time 
    4072    mappings from a particular :class:`_orm.registry` are used, as well as 
    4073    whenever mappings are used and additional not-yet-configured mappers have 
    4074    been constructed. The automatic configuration process however is local only 
    4075    to the :class:`_orm.registry` involving the target mapper and any related 
    4076    :class:`_orm.registry` objects which it may depend on; this is 
    4077    equivalent to invoking the :meth:`_orm.registry.configure` method 
    4078    on a particular :class:`_orm.registry`. 
    4079 
    4080    By contrast, the :func:`_orm.configure_mappers` function will invoke the 
    4081    configuration process on all :class:`_orm.registry` objects that 
    4082    exist in memory, and may be useful for scenarios where many individual 
    4083    :class:`_orm.registry` objects that are nonetheless interrelated are 
    4084    in use. 
    4085 
    4086    .. versionchanged:: 1.4 
    4087 
    4088        As of SQLAlchemy 1.4.0b2, this function works on a 
    4089        per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 
    4090        objects present and invoking the :meth:`_orm.registry.configure` method 
    4091        on each. The :meth:`_orm.registry.configure` method may be preferred to 
    4092        limit the configuration of mappers to those local to a particular 
    4093        :class:`_orm.registry` and/or declarative base class. 
    4094 
    4095    Points at which automatic configuration is invoked include when a mapped 
    4096    class is instantiated into an instance, as well as when ORM queries 
    4097    are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 
    4098    with an ORM-enabled statement. 
    4099 
    4100    The mapper configure process, whether invoked by 
    4101    :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 
    4102    provides several event hooks that can be used to augment the mapper 
    4103    configuration step. These hooks include: 
    4104 
    4105    * :meth:`.MapperEvents.before_configured` - called once before 
    4106      :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 
    4107      work; this can be used to establish additional options, properties, or 
    4108      related mappings before the operation proceeds. 
    4109 
    4110    * :meth:`.RegistryEvents.before_configured` - Like 
    4111      :meth:`.MapperEvents.before_configured`, but local to a specific 
    4112      :class:`_orm.registry`. 
    4113 
    4114      .. versionadded:: 2.1 - added :meth:`.RegistryEvents.before_configured` 
    4115 
    4116    * :meth:`.MapperEvents.mapper_configured` - called as each individual 
    4117      :class:`_orm.Mapper` is configured within the process; will include all 
    4118      mapper state except for backrefs set up by other mappers that are still 
    4119      to be configured. 
    4120 
    4121    * :meth:`.MapperEvents.after_configured` - called once after 
    4122      :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 
    4123      complete; at this stage, all :class:`_orm.Mapper` objects that fall 
    4124      within the scope of the configuration operation will be fully configured. 
    4125      Note that the calling application may still have other mappings that 
    4126      haven't been produced yet, such as if they are in modules as yet 
    4127      unimported, and may also have mappings that are still to be configured, 
    4128      if they are in other :class:`_orm.registry` collections not part of the 
    4129      current scope of configuration. 
    4130 
    4131    * :meth:`.RegistryEvents.after_configured` - Like 
    4132      :meth:`.MapperEvents.after_configured`, but local to a specific 
    4133      :class:`_orm.registry`. 
    4134 
    4135      .. versionadded:: 2.1 - added :meth:`.RegistryEvents.after_configured` 
    4136 
    4137    """ 
    4138 
    4139    _configure_registries(_all_registries(), cascade=True) 
    4140 
    4141 
    4142def _configure_registries( 
    4143    registries: Set[_RegistryType], cascade: bool 
    4144) -> None: 
    4145    for reg in registries: 
    4146        if reg._new_mappers: 
    4147            break 
    4148    else: 
    4149        return 
    4150 
    4151    with _CONFIGURE_MUTEX: 
    4152        global _already_compiling 
    4153        if _already_compiling: 
    4154            return 
    4155        _already_compiling = True 
    4156        try: 
    4157            # double-check inside mutex 
    4158            for reg in registries: 
    4159                if reg._new_mappers: 
    4160                    break 
    4161            else: 
    4162                return 
    4163 
    4164            Mapper.dispatch._for_class(Mapper).before_configured()  # type: ignore # noqa: E501 
    4165 
    4166            # initialize properties on all mappers 
    4167            # note that _mapper_registry is unordered, which 
    4168            # may randomly conceal/reveal issues related to 
    4169            # the order of mapper compilation 
    4170 
    4171            registries_configured = list( 
    4172                _do_configure_registries(registries, cascade) 
    4173            ) 
    4174 
    4175        finally: 
    4176            _already_compiling = False 
    4177    for reg in registries_configured: 
    4178        reg.dispatch.after_configured(reg) 
    4179    Mapper.dispatch._for_class(Mapper).after_configured()  # type: ignore 
    4180 
    4181 
    4182@util.preload_module("sqlalchemy.orm.decl_api") 
    4183def _do_configure_registries( 
    4184    registries: Set[_RegistryType], cascade: bool 
    4185) -> Iterator[registry]: 
    4186    registry = util.preloaded.orm_decl_api.registry 
    4187 
    4188    orig = set(registries) 
    4189 
    4190    for reg in registry._recurse_with_dependencies(registries): 
    4191        if reg._new_mappers: 
    4192            reg.dispatch.before_configured(reg) 
    4193 
    4194        has_skip = False 
    4195 
    4196        for mapper in reg._mappers_to_configure(): 
    4197            run_configure = None 
    4198 
    4199            for fn in mapper.dispatch.before_mapper_configured: 
    4200                run_configure = fn(mapper, mapper.class_) 
    4201                if run_configure is EXT_SKIP: 
    4202                    has_skip = True 
    4203                    break 
    4204            if run_configure is EXT_SKIP: 
    4205                continue 
    4206 
    4207            if getattr(mapper, "_configure_failed", False): 
    4208                e = sa_exc.InvalidRequestError( 
    4209                    "One or more mappers failed to initialize - " 
    4210                    "can't proceed with initialization of other " 
    4211                    "mappers. Triggering mapper: '%s'. " 
    4212                    "Original exception was: %s" 
    4213                    % (mapper, mapper._configure_failed) 
    4214                ) 
    4215                e._configure_failed = mapper._configure_failed  # type: ignore 
    4216                raise e 
    4217 
    4218            if not mapper.configured: 
    4219                try: 
    4220                    mapper._post_configure_properties() 
    4221                    mapper._expire_memoizations() 
    4222                    mapper.dispatch.mapper_configured(mapper, mapper.class_) 
    4223                except Exception: 
    4224                    exc = sys.exc_info()[1] 
    4225                    if not hasattr(exc, "_configure_failed"): 
    4226                        mapper._configure_failed = exc 
    4227                    raise 
    4228 
    4229        if reg._new_mappers: 
    4230            yield reg 
    4231        if not has_skip: 
    4232            reg._new_mappers = False 
    4233 
    4234        if not cascade and reg._dependencies.difference(orig): 
    4235            raise sa_exc.InvalidRequestError( 
    4236                "configure was called with cascade=False but " 
    4237                "additional registries remain" 
    4238            ) 
    4239 
    4240 
    4241@util.preload_module("sqlalchemy.orm.decl_api") 
    4242def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 
    4243    registry = util.preloaded.orm_decl_api.registry 
    4244 
    4245    orig = set(registries) 
    4246 
    4247    for reg in registry._recurse_with_dependents(registries): 
    4248        if not cascade and reg._dependents.difference(orig): 
    4249            raise sa_exc.InvalidRequestError( 
    4250                "Registry has dependent registries that are not disposed; " 
    4251                "pass cascade=True to clear these also" 
    4252            ) 
    4253 
    4254        while reg._managers: 
    4255            try: 
    4256                manager, _ = reg._managers.popitem() 
    4257            except KeyError: 
    4258                # guard against race between while and popitem 
    4259                pass 
    4260            else: 
    4261                reg._dispose_manager_and_mapper(manager) 
    4262 
    4263        reg._dependents.clear() 
    4264        for dep in reg._dependencies: 
    4265            dep._dependents.discard(reg) 
    4266        reg._dependencies.clear() 
    4267        # this wasn't done in the 1.3 clear_mappers() and in fact it 
    4268        # was a bug, as it could cause configure_mappers() to invoke 
    4269        # the "before_configured" event even though mappers had all been 
    4270        # disposed. 
    4271        reg._new_mappers = False 
    4272 
    4273 
    4274def reconstructor(fn: _Fn) -> _Fn: 
    4275    """Decorate a method as the 'reconstructor' hook. 
    4276 
    4277    Designates a single method as the "reconstructor", an ``__init__``-like 
    4278    method that will be called by the ORM after the instance has been 
    4279    loaded from the database or otherwise reconstituted. 
    4280 
    4281    .. tip:: 
    4282 
    4283        The :func:`_orm.reconstructor` decorator makes use of the 
    4284        :meth:`_orm.InstanceEvents.load` event hook, which can be 
    4285        used directly. 
    4286 
    4287    The reconstructor will be invoked with no arguments.  Scalar 
    4288    (non-collection) database-mapped attributes of the instance will 
    4289    be available for use within the function.  Eagerly-loaded 
    4290    collections are generally not yet available and will usually only 
    4291    contain the first element.  ORM state changes made to objects at 
    4292    this stage will not be recorded for the next flush() operation, so 
    4293    the activity within a reconstructor should be conservative. 
    4294 
    4295    .. seealso:: 
    4296 
    4297        :meth:`.InstanceEvents.load` 
    4298 
    4299    """ 
    4300    fn.__sa_reconstructor__ = True  # type: ignore[attr-defined] 
    4301    return fn 
    4302 
    4303 
    4304def validates( 
    4305    *names: str, include_removes: bool = False, include_backrefs: bool = True 
    4306) -> Callable[[_Fn], _Fn]: 
    4307    r"""Decorate a method as a 'validator' for one or more named properties. 
    4308 
    4309    Designates a method as a validator, a method which receives the 
    4310    name of the attribute as well as a value to be assigned, or in the 
    4311    case of a collection, the value to be added to the collection. 
    4312    The function can then raise validation exceptions to halt the 
    4313    process from continuing (where Python's built-in ``ValueError`` 
    4314    and ``AssertionError`` exceptions are reasonable choices), or can 
    4315    modify or replace the value before proceeding. The function should 
    4316    otherwise return the given value. 
    4317 
    4318    Note that a validator for a collection **cannot** issue a load of that 
    4319    collection within the validation routine - this usage raises 
    4320    an assertion to avoid recursion overflows.  This is a reentrant 
    4321    condition which is not supported. 
    4322 
    4323    :param \*names: list of attribute names to be validated. 
    4324    :param include_removes: if True, "remove" events will be 
    4325     sent as well - the validation function must accept an additional 
    4326     argument "is_remove" which will be a boolean. 
    4327 
    4328    :param include_backrefs: defaults to ``True``; if ``False``, the 
    4329     validation function will not emit if the originator is an attribute 
    4330     event related via a backref.  This can be used for bi-directional 
    4331     :func:`.validates` usage where only one validator should emit per 
    4332     attribute operation. 
    4333 
    4334     .. versionchanged:: 2.0.16 This paramter inadvertently defaulted to 
    4335        ``False`` for releases 2.0.0 through 2.0.15.  Its correct default 
    4336        of ``True`` is restored in 2.0.16. 
    4337 
    4338    .. seealso:: 
    4339 
    4340      :ref:`simple_validators` - usage examples for :func:`.validates` 
    4341 
    4342    """ 
    4343 
    4344    def wrap(fn: _Fn) -> _Fn: 
    4345        fn.__sa_validators__ = names  # type: ignore[attr-defined] 
    4346        fn.__sa_validation_opts__ = {  # type: ignore[attr-defined] 
    4347            "include_removes": include_removes, 
    4348            "include_backrefs": include_backrefs, 
    4349        } 
    4350        return fn 
    4351 
    4352    return wrap 
    4353 
    4354 
    4355def _event_on_load(state, ctx): 
    4356    instrumenting_mapper = state.manager.mapper 
    4357 
    4358    if instrumenting_mapper._reconstructor: 
    4359        instrumenting_mapper._reconstructor(state.obj()) 
    4360 
    4361 
    4362def _event_on_init(state, args, kwargs): 
    4363    """Run init_instance hooks. 
    4364 
    4365    This also includes mapper compilation, normally not needed 
    4366    here but helps with some piecemeal configuration 
    4367    scenarios (such as in the ORM tutorial). 
    4368 
    4369    """ 
    4370 
    4371    instrumenting_mapper = state.manager.mapper 
    4372    if instrumenting_mapper: 
    4373        instrumenting_mapper._check_configure() 
    4374        if instrumenting_mapper._set_polymorphic_identity: 
    4375            instrumenting_mapper._set_polymorphic_identity(state) 
    4376 
    4377 
    4378class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 
    4379    """Error reporting helper for mapper._columntoproperty.""" 
    4380 
    4381    __slots__ = ("mapper",) 
    4382 
    4383    def __init__(self, mapper): 
    4384        # TODO: weakref would be a good idea here 
    4385        self.mapper = mapper 
    4386 
    4387    def __missing__(self, column): 
    4388        prop = self.mapper._props.get(column) 
    4389        if prop: 
    4390            raise orm_exc.UnmappedColumnError( 
    4391                "Column '%s.%s' is not available, due to " 
    4392                "conflicting property '%s':%r" 
    4393                % (column.table.name, column.name, column.key, prop) 
    4394            ) 
    4395        raise orm_exc.UnmappedColumnError( 
    4396            "No column %s is configured on mapper %s..." 
    4397            % (column, self.mapper) 
    4398        )