summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/mapper.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/orm/mapper.py')
-rw-r--r--lib/sqlalchemy/orm/mapper.py649
1 files changed, 417 insertions, 232 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index abe11cc68..b37c080ea 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -23,12 +23,23 @@ import sys
import threading
from typing import Any
from typing import Callable
+from typing import cast
+from typing import Collection
+from typing import Deque
+from typing import Dict
from typing import Generic
+from typing import Iterable
from typing import Iterator
+from typing import List
+from typing import Mapping
from typing import Optional
+from typing import Sequence
+from typing import Set
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
+from typing import TypeVar
+from typing import Union
import weakref
from . import attributes
@@ -39,8 +50,8 @@ from . import properties
from . import util as orm_util
from ._typing import _O
from .base import _class_to_mapper
+from .base import _parse_mapper_argument
from .base import _state_mapper
-from .base import class_mapper
from .base import PassiveFlag
from .base import state_str
from .interfaces import _MappedAttribute
@@ -58,6 +69,8 @@ from .. import log
from .. import schema
from .. import sql
from .. import util
+from ..event import dispatcher
+from ..event import EventTarget
from ..sql import base as sql_base
from ..sql import coercions
from ..sql import expression
@@ -65,26 +78,68 @@ from ..sql import operators
from ..sql import roles
from ..sql import util as sql_util
from ..sql import visitors
+from ..sql.cache_key import MemoizedHasCacheKey
+from ..sql.schema import Table
from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
from ..util import HasMemoized
+from ..util import HasMemoized_ro_memoized_attribute
+from ..util.typing import Literal
if TYPE_CHECKING:
from ._typing import _IdentityKeyType
from ._typing import _InstanceDict
+ from ._typing import _ORMColumnExprArgument
+ from ._typing import _RegistryType
+ from .decl_api import registry
+ from .dependency import DependencyProcessor
+ from .descriptor_props import Composite
+ from .descriptor_props import Synonym
+ from .events import MapperEvents
from .instrumentation import ClassManager
+ from .path_registry import AbstractEntityRegistry
+ from .path_registry import CachingEntityRegistry
+ from .properties import ColumnProperty
+ from .relationships import Relationship
from .state import InstanceState
+ from ..engine import Row
+ from ..engine import RowMapping
+ from ..sql._typing import _ColumnExpressionArgument
+ from ..sql._typing import _EquivalentColumnMap
+ from ..sql.base import ReadOnlyColumnCollection
+ from ..sql.elements import ColumnClause
from ..sql.elements import ColumnElement
from ..sql.schema import Column
+ from ..sql.schema import Table
+ from ..sql.selectable import FromClause
+ from ..sql.selectable import TableClause
+ from ..sql.util import ColumnAdapter
+ from ..util import OrderedSet
-_mapper_registries = weakref.WeakKeyDictionary()
+_T = TypeVar("_T", bound=Any)
+_MP = TypeVar("_MP", bound="MapperProperty[Any]")
-def _all_registries():
+_WithPolymorphicArg = Union[
+ Literal["*"],
+ Tuple[
+ Union[Literal["*"], Sequence[Union["Mapper[Any]", Type[Any]]]],
+ Optional["FromClause"],
+ ],
+ Sequence[Union["Mapper[Any]", Type[Any]]],
+]
+
+
+_mapper_registries: weakref.WeakKeyDictionary[
+ _RegistryType, bool
+] = weakref.WeakKeyDictionary()
+
+
+def _all_registries() -> Set[registry]:
with _CONFIGURE_MUTEX:
return set(_mapper_registries)
-def _unconfigured_mappers():
+def _unconfigured_mappers() -> Iterator[Mapper[Any]]:
for reg in _all_registries():
for mapper in reg._mappers_to_configure():
yield mapper
@@ -107,9 +162,11 @@ _CONFIGURE_MUTEX = threading.RLock()
class Mapper(
ORMFromClauseRole,
ORMEntityColumnsClauseRole,
- sql_base.MemoizedHasCacheKey,
+ MemoizedHasCacheKey,
InspectionAttr,
log.Identified,
+ inspection.Inspectable["Mapper[_O]"],
+ EventTarget,
Generic[_O],
):
"""Defines an association between a Python class and a database table or
@@ -123,18 +180,11 @@ class Mapper(
"""
+ dispatch: dispatcher[Mapper[_O]]
+
_dispose_called = False
_ready_for_configure = False
- class_: Type[_O]
- """The class to which this :class:`_orm.Mapper` is mapped."""
-
- _identity_class: Type[_O]
-
- always_refresh: bool
- allow_partial_pks: bool
- version_id_col: Optional[ColumnElement[Any]]
-
@util.deprecated_params(
non_primary=(
"1.3",
@@ -148,33 +198,39 @@ class Mapper(
def __init__(
self,
class_: Type[_O],
- local_table=None,
- properties=None,
- primary_key=None,
- non_primary=False,
- inherits=None,
- inherit_condition=None,
- inherit_foreign_keys=None,
- always_refresh=False,
- version_id_col=None,
- version_id_generator=None,
- polymorphic_on=None,
- _polymorphic_map=None,
- polymorphic_identity=None,
- concrete=False,
- with_polymorphic=None,
- polymorphic_load=None,
- allow_partial_pks=True,
- batch=True,
- column_prefix=None,
- include_properties=None,
- exclude_properties=None,
- passive_updates=True,
- passive_deletes=False,
- confirm_deleted_rows=True,
- eager_defaults=False,
- legacy_is_orphan=False,
- _compiled_cache_size=100,
+ local_table: Optional[FromClause] = None,
+ properties: Optional[Mapping[str, MapperProperty[Any]]] = None,
+ primary_key: Optional[Iterable[_ORMColumnExprArgument[Any]]] = None,
+ non_primary: bool = False,
+ inherits: Optional[Union[Mapper[Any], Type[Any]]] = None,
+ inherit_condition: Optional[_ColumnExpressionArgument[bool]] = None,
+ inherit_foreign_keys: Optional[
+ Sequence[_ORMColumnExprArgument[Any]]
+ ] = None,
+ always_refresh: bool = False,
+ version_id_col: Optional[_ORMColumnExprArgument[Any]] = None,
+ version_id_generator: Optional[
+ Union[Literal[False], Callable[[Any], Any]]
+ ] = None,
+ polymorphic_on: Optional[
+ Union[_ORMColumnExprArgument[Any], str, MapperProperty[Any]]
+ ] = None,
+ _polymorphic_map: Optional[Dict[Any, Mapper[Any]]] = None,
+ polymorphic_identity: Optional[Any] = None,
+ concrete: bool = False,
+ with_polymorphic: Optional[_WithPolymorphicArg] = None,
+ polymorphic_load: Optional[Literal["selectin", "inline"]] = None,
+ allow_partial_pks: bool = True,
+ batch: bool = True,
+ column_prefix: Optional[str] = None,
+ include_properties: Optional[Sequence[str]] = None,
+ exclude_properties: Optional[Sequence[str]] = None,
+ passive_updates: bool = True,
+ passive_deletes: bool = False,
+ confirm_deleted_rows: bool = True,
+ eager_defaults: bool = False,
+ legacy_is_orphan: bool = False,
+ _compiled_cache_size: int = 100,
):
r"""Direct constructor for a new :class:`_orm.Mapper` object.
@@ -593,8 +649,6 @@ class Mapper(
self.class_.__name__,
)
- self.class_manager = None
-
self._primary_key_argument = util.to_list(primary_key)
self.non_primary = non_primary
@@ -623,17 +677,36 @@ class Mapper(
self.concrete = concrete
self.single = False
- self.inherits = inherits
+
+ if inherits is not None:
+ self.inherits = _parse_mapper_argument(inherits)
+ else:
+ self.inherits = None
+
if local_table is not None:
self.local_table = coercions.expect(
roles.StrictFromClauseRole, local_table
)
+ elif self.inherits:
+ # note this is a new flow as of 2.0 so that
+ # .local_table need not be Optional
+ self.local_table = self.inherits.local_table
+ self.single = True
else:
- self.local_table = None
+ raise sa_exc.ArgumentError(
+ f"Mapper[{self.class_.__name__}(None)] has None for a "
+ "primary table argument and does not specify 'inherits'"
+ )
+
+ if inherit_condition is not None:
+ self.inherit_condition = coercions.expect(
+ roles.OnClauseRole, inherit_condition
+ )
+ else:
+ self.inherit_condition = None
- self.inherit_condition = inherit_condition
self.inherit_foreign_keys = inherit_foreign_keys
- self._init_properties = properties or {}
+ self._init_properties = dict(properties) if properties else {}
self._delete_orphans = []
self.batch = batch
self.eager_defaults = eager_defaults
@@ -694,7 +767,10 @@ class Mapper(
# while a configure_mappers() is occurring (and defer a
# configure_mappers() until construction succeeds)
with _CONFIGURE_MUTEX:
- self.dispatch._events._new_mapper_instance(class_, self)
+
+ cast("MapperEvents", self.dispatch._events)._new_mapper_instance(
+ class_, self
+ )
self._configure_inheritance()
self._configure_class_instrumentation()
self._configure_properties()
@@ -704,16 +780,21 @@ class Mapper(
self._log("constructed")
self._expire_memoizations()
- # major attributes initialized at the classlevel so that
- # they can be Sphinx-documented.
+ def _gen_cache_key(self, anon_map, bindparams):
+ return (self,)
+
+ # ### BEGIN
+ # ATTRIBUTE DECLARATIONS START HERE
is_mapper = True
"""Part of the inspection API."""
represents_outer_join = False
+ registry: _RegistryType
+
@property
- def mapper(self):
+ def mapper(self) -> Mapper[_O]:
"""Part of the inspection API.
Returns self.
@@ -721,9 +802,6 @@ class Mapper(
"""
return self
- def _gen_cache_key(self, anon_map, bindparams):
- return (self,)
-
@property
def entity(self):
r"""Part of the inspection API.
@@ -733,49 +811,109 @@ class Mapper(
"""
return self.class_
- local_table = None
- """The :class:`_expression.Selectable` which this :class:`_orm.Mapper`
- manages.
+ class_: Type[_O]
+ """The class to which this :class:`_orm.Mapper` is mapped."""
+
+ _identity_class: Type[_O]
+
+ _delete_orphans: List[Tuple[str, Type[Any]]]
+ _dependency_processors: List[DependencyProcessor]
+ _memoized_values: Dict[Any, Callable[[], Any]]
+ _inheriting_mappers: util.WeakSequence[Mapper[Any]]
+ _all_tables: Set[Table]
+
+ _pks_by_table: Dict[FromClause, OrderedSet[ColumnClause[Any]]]
+ _cols_by_table: Dict[FromClause, OrderedSet[ColumnElement[Any]]]
+
+ _props: util.OrderedDict[str, MapperProperty[Any]]
+ _init_properties: Dict[str, MapperProperty[Any]]
+
+ _columntoproperty: _ColumnMapping
+
+ _set_polymorphic_identity: Optional[Callable[[InstanceState[_O]], None]]
+ _validate_polymorphic_identity: Optional[
+ Callable[[Mapper[_O], InstanceState[_O], _InstanceDict], None]
+ ]
+
+ tables: Sequence[Table]
+ """A sequence containing the collection of :class:`_schema.Table` objects
+ which this :class:`_orm.Mapper` is aware of.
+
+ If the mapper is mapped to a :class:`_expression.Join`, or an
+ :class:`_expression.Alias`
+ representing a :class:`_expression.Select`, the individual
+ :class:`_schema.Table`
+ objects that comprise the full construct will be represented here.
+
+ This is a *read only* attribute determined during mapper construction.
+ Behavior is undefined if directly modified.
+
+ """
+
+ validators: util.immutabledict[str, Tuple[str, Dict[str, Any]]]
+ """An immutable dictionary of attributes which have been decorated
+ using the :func:`_orm.validates` decorator.
+
+ The dictionary contains string attribute names as keys
+ mapped to the actual validation method.
+
+ """
+
+ always_refresh: bool
+ allow_partial_pks: bool
+ version_id_col: Optional[ColumnElement[Any]]
+
+ with_polymorphic: Optional[
+ Tuple[
+ Union[Literal["*"], Sequence[Union["Mapper[Any]", Type[Any]]]],
+ Optional["FromClause"],
+ ]
+ ]
+
+ version_id_generator: Optional[Union[Literal[False], Callable[[Any], Any]]]
+
+ local_table: FromClause
+ """The immediate :class:`_expression.FromClause` which this
+ :class:`_orm.Mapper` refers towards.
- Typically is an instance of :class:`_schema.Table` or
- :class:`_expression.Alias`.
- May also be ``None``.
+ Typically is an instance of :class:`_schema.Table`, may be any
+ :class:`.FromClause`.
The "local" table is the
selectable that the :class:`_orm.Mapper` is directly responsible for
managing from an attribute access and flush perspective. For
- non-inheriting mappers, the local table is the same as the
- "mapped" table. For joined-table inheritance mappers, local_table
- will be the particular sub-table of the overall "join" which
- this :class:`_orm.Mapper` represents. If this mapper is a
- single-table inheriting mapper, local_table will be ``None``.
+ non-inheriting mappers, :attr:`.Mapper.local_table` will be the same
+ as :attr:`.Mapper.persist_selectable`. For inheriting mappers,
+ :attr:`.Mapper.local_table` refers to the specific portion of
+ :attr:`.Mapper.persist_selectable` that includes the columns to which
+ this :class:`.Mapper` is loading/persisting, such as a particular
+ :class:`.Table` within a join.
.. seealso::
:attr:`_orm.Mapper.persist_selectable`.
+ :attr:`_orm.Mapper.selectable`.
+
"""
- persist_selectable = None
- """The :class:`_expression.Selectable` to which this :class:`_orm.Mapper`
+ persist_selectable: FromClause
+ """The :class:`_expression.FromClause` to which this :class:`_orm.Mapper`
is mapped.
- Typically an instance of :class:`_schema.Table`,
- :class:`_expression.Join`, or :class:`_expression.Alias`.
-
- The :attr:`_orm.Mapper.persist_selectable` is separate from
- :attr:`_orm.Mapper.selectable` in that the former represents columns
- that are mapped on this class or its superclasses, whereas the
- latter may be a "polymorphic" selectable that contains additional columns
- which are in fact mapped on subclasses only.
+ Typically is an instance of :class:`_schema.Table`, may be any
+ :class:`.FromClause`.
- "persist selectable" is the "thing the mapper writes to" and
- "selectable" is the "thing the mapper selects from".
-
- :attr:`_orm.Mapper.persist_selectable` is also separate from
- :attr:`_orm.Mapper.local_table`, which represents the set of columns that
- are locally mapped on this class directly.
+ The :attr:`_orm.Mapper.persist_selectable` is similar to
+ :attr:`.Mapper.local_table`, but represents the :class:`.FromClause` that
+ represents the inheriting class hierarchy overall in an inheritance
+ scenario.
+ :attr.`.Mapper.persist_selectable` is also separate from the
+ :attr:`.Mapper.selectable` attribute, the latter of which may be an
+ alternate subquery used for selecting columns.
+ :attr.`.Mapper.persist_selectable` is oriented towards columns that
+ will be written on a persist operation.
.. seealso::
@@ -785,16 +923,15 @@ class Mapper(
"""
- inherits = None
+ inherits: Optional[Mapper[Any]]
"""References the :class:`_orm.Mapper` which this :class:`_orm.Mapper`
inherits from, if any.
- This is a *read only* attribute determined during mapper construction.
- Behavior is undefined if directly modified.
-
"""
- configured = False
+ inherit_condition: Optional[ColumnElement[bool]]
+
+ configured: bool = False
"""Represent ``True`` if this :class:`_orm.Mapper` has been configured.
This is a *read only* attribute determined during mapper construction.
@@ -806,7 +943,7 @@ class Mapper(
"""
- concrete = None
+ concrete: bool
"""Represent ``True`` if this :class:`_orm.Mapper` is a concrete
inheritance mapper.
@@ -815,21 +952,6 @@ class Mapper(
"""
- tables = None
- """An iterable containing the collection of :class:`_schema.Table` objects
- which this :class:`_orm.Mapper` is aware of.
-
- If the mapper is mapped to a :class:`_expression.Join`, or an
- :class:`_expression.Alias`
- representing a :class:`_expression.Select`, the individual
- :class:`_schema.Table`
- objects that comprise the full construct will be represented here.
-
- This is a *read only* attribute determined during mapper construction.
- Behavior is undefined if directly modified.
-
- """
-
primary_key: Tuple[Column[Any], ...]
"""An iterable containing the collection of :class:`_schema.Column`
objects
@@ -854,14 +976,6 @@ class Mapper(
"""
- class_: Type[_O]
- """The Python class which this :class:`_orm.Mapper` maps.
-
- This is a *read only* attribute determined during mapper construction.
- Behavior is undefined if directly modified.
-
- """
-
class_manager: ClassManager[_O]
"""The :class:`.ClassManager` which maintains event listeners
and class-bound descriptors for this :class:`_orm.Mapper`.
@@ -871,7 +985,7 @@ class Mapper(
"""
- single = None
+ single: bool
"""Represent ``True`` if this :class:`_orm.Mapper` is a single table
inheritance mapper.
@@ -882,7 +996,7 @@ class Mapper(
"""
- non_primary = None
+ non_primary: bool
"""Represent ``True`` if this :class:`_orm.Mapper` is a "non-primary"
mapper, e.g. a mapper that is used only to select rows but not for
persistence management.
@@ -892,7 +1006,7 @@ class Mapper(
"""
- polymorphic_on = None
+ polymorphic_on: Optional[ColumnElement[Any]]
"""The :class:`_schema.Column` or SQL expression specified as the
``polymorphic_on`` argument
for this :class:`_orm.Mapper`, within an inheritance scenario.
@@ -906,7 +1020,7 @@ class Mapper(
"""
- polymorphic_map = None
+ polymorphic_map: Dict[Any, Mapper[Any]]
"""A mapping of "polymorphic identity" identifiers mapped to
:class:`_orm.Mapper` instances, within an inheritance scenario.
@@ -922,7 +1036,7 @@ class Mapper(
"""
- polymorphic_identity = None
+ polymorphic_identity: Optional[Any]
"""Represent an identifier which is matched against the
:attr:`_orm.Mapper.polymorphic_on` column during result row loading.
@@ -935,7 +1049,7 @@ class Mapper(
"""
- base_mapper = None
+ base_mapper: Mapper[Any]
"""The base-most :class:`_orm.Mapper` in an inheritance chain.
In a non-inheriting scenario, this attribute will always be this
@@ -948,7 +1062,7 @@ class Mapper(
"""
- columns = None
+ columns: ReadOnlyColumnCollection[str, Column[Any]]
"""A collection of :class:`_schema.Column` or other scalar expression
objects maintained by this :class:`_orm.Mapper`.
@@ -965,25 +1079,16 @@ class Mapper(
"""
- validators = None
- """An immutable dictionary of attributes which have been decorated
- using the :func:`_orm.validates` decorator.
-
- The dictionary contains string attribute names as keys
- mapped to the actual validation method.
-
- """
-
- c = None
+ c: ReadOnlyColumnCollection[str, Column[Any]]
"""A synonym for :attr:`_orm.Mapper.columns`."""
- @property
+ @util.non_memoized_property
@util.deprecated("1.3", "Use .persist_selectable")
def mapped_table(self):
return self.persist_selectable
@util.memoized_property
- def _path_registry(self) -> PathRegistry:
+ def _path_registry(self) -> CachingEntityRegistry:
return PathRegistry.per_mapper(self)
def _configure_inheritance(self):
@@ -994,8 +1099,6 @@ class Mapper(
self._inheriting_mappers = util.WeakSequence()
if self.inherits:
- if isinstance(self.inherits, type):
- self.inherits = class_mapper(self.inherits, configure=False)
if not issubclass(self.class_, self.inherits.class_):
raise sa_exc.ArgumentError(
"Class '%s' does not inherit from '%s'"
@@ -1011,11 +1114,9 @@ class Mapper(
"only allowed from a %s mapper"
% (np, self.class_.__name__, np)
)
- # inherit_condition is optional.
- if self.local_table is None:
- self.local_table = self.inherits.local_table
+
+ if self.single:
self.persist_selectable = self.inherits.persist_selectable
- self.single = True
elif self.local_table is not self.inherits.local_table:
if self.concrete:
self.persist_selectable = self.local_table
@@ -1068,6 +1169,7 @@ class Mapper(
self.local_table.description,
)
) from afe
+ assert self.inherits.persist_selectable is not None
self.persist_selectable = sql.join(
self.inherits.persist_selectable,
self.local_table,
@@ -1149,6 +1251,7 @@ class Mapper(
else:
self._all_tables = set()
self.base_mapper = self
+ assert self.local_table is not None
self.persist_selectable = self.local_table
if self.polymorphic_identity is not None:
self.polymorphic_map[self.polymorphic_identity] = self
@@ -1160,21 +1263,34 @@ class Mapper(
% self
)
- def _set_with_polymorphic(self, with_polymorphic):
+ def _set_with_polymorphic(
+ self, with_polymorphic: Optional[_WithPolymorphicArg]
+ ) -> None:
if with_polymorphic == "*":
self.with_polymorphic = ("*", None)
elif isinstance(with_polymorphic, (tuple, list)):
if isinstance(with_polymorphic[0], (str, tuple, list)):
- self.with_polymorphic = with_polymorphic
+ self.with_polymorphic = cast(
+ """Tuple[
+ Union[
+ Literal["*"],
+ Sequence[Union["Mapper[Any]", Type[Any]]],
+ ],
+ Optional["FromClause"],
+ ]""",
+ with_polymorphic,
+ )
else:
self.with_polymorphic = (with_polymorphic, None)
elif with_polymorphic is not None:
- raise sa_exc.ArgumentError("Invalid setting for with_polymorphic")
+ raise sa_exc.ArgumentError(
+ f"Invalid setting for with_polymorphic: {with_polymorphic!r}"
+ )
else:
self.with_polymorphic = None
if self.with_polymorphic and self.with_polymorphic[1] is not None:
- self.with_polymorphic = (
+ self.with_polymorphic = ( # type: ignore
self.with_polymorphic[0],
coercions.expect(
roles.StrictFromClauseRole,
@@ -1191,6 +1307,7 @@ class Mapper(
if self.with_polymorphic is None:
self._set_with_polymorphic((subcl,))
elif self.with_polymorphic[0] != "*":
+ assert isinstance(self.with_polymorphic[0], tuple)
self._set_with_polymorphic(
(self.with_polymorphic[0] + (subcl,), self.with_polymorphic[1])
)
@@ -1241,7 +1358,7 @@ class Mapper(
# we expect that declarative has applied the class manager
# already and set up a registry. if this is None,
# this raises as of 2.0.
- manager = attributes.manager_of_class(self.class_)
+ manager = attributes.opt_manager_of_class(self.class_)
if self.non_primary:
if not manager or not manager.is_mapped:
@@ -1251,6 +1368,8 @@ class Mapper(
"Mapper." % self.class_
)
self.class_manager = manager
+
+ assert manager.registry is not None
self.registry = manager.registry
self._identity_class = manager.mapper._identity_class
manager.registry._add_non_primary_mapper(self)
@@ -1275,7 +1394,7 @@ class Mapper(
manager = instrumentation.register_class(
self.class_,
mapper=self,
- expired_attribute_loader=util.partial(
+ expired_attribute_loader=util.partial( # type: ignore
loading.load_scalar_attributes, self
),
# finalize flag means instrument the __init__ method
@@ -1284,6 +1403,8 @@ class Mapper(
)
self.class_manager = manager
+
+ assert manager.registry is not None
self.registry = manager.registry
# The remaining members can be added by any mapper,
@@ -1315,15 +1436,25 @@ class Mapper(
{name: (method, validation_opts)}
)
- def _set_dispose_flags(self):
+ def _set_dispose_flags(self) -> None:
self.configured = True
self._ready_for_configure = True
self._dispose_called = True
self.__dict__.pop("_configure_failed", None)
- def _configure_pks(self):
- self.tables = sql_util.find_tables(self.persist_selectable)
+ def _configure_pks(self) -> None:
+ self.tables = cast(
+ "List[Table]", sql_util.find_tables(self.persist_selectable)
+ )
+ for t in self.tables:
+ if not isinstance(t, Table):
+ raise sa_exc.ArgumentError(
+ f"ORM mappings can only be made against schema-level "
+ f"Table objects, not TableClause; got "
+ f"tableclause {t.name !r}"
+ )
+ self._all_tables.update(t for t in self.tables if isinstance(t, Table))
self._pks_by_table = {}
self._cols_by_table = {}
@@ -1335,16 +1466,16 @@ class Mapper(
pk_cols = util.column_set(c for c in all_cols if c.primary_key)
# identify primary key columns which are also mapped by this mapper.
- tables = set(self.tables + [self.persist_selectable])
- self._all_tables.update(tables)
- for t in tables:
- if t.primary_key and pk_cols.issuperset(t.primary_key):
+ for fc in set(self.tables).union([self.persist_selectable]):
+ if fc.primary_key and pk_cols.issuperset(fc.primary_key):
# ordering is important since it determines the ordering of
# mapper.primary_key (and therefore query.get())
- self._pks_by_table[t] = util.ordered_column_set(
- t.primary_key
- ).intersection(pk_cols)
- self._cols_by_table[t] = util.ordered_column_set(t.c).intersection(
+ self._pks_by_table[fc] = util.ordered_column_set( # type: ignore # noqa: E501
+ fc.primary_key
+ ).intersection(
+ pk_cols
+ )
+ self._cols_by_table[fc] = util.ordered_column_set(fc.c).intersection( # type: ignore # noqa: E501
all_cols
)
@@ -1386,10 +1517,15 @@ class Mapper(
self.primary_key = self.inherits.primary_key
else:
# determine primary key from argument or persist_selectable pks
+ primary_key: Collection[ColumnElement[Any]]
+
if self._primary_key_argument:
primary_key = [
- self.persist_selectable.corresponding_column(c)
- for c in self._primary_key_argument
+ cc if cc is not None else c
+ for cc, c in (
+ (self.persist_selectable.corresponding_column(c), c)
+ for c in self._primary_key_argument
+ )
]
else:
# if heuristically determined PKs, reduce to the minimal set
@@ -1413,7 +1549,7 @@ class Mapper(
# determine cols that aren't expressed within our tables; mark these
# as "read only" properties which are refreshed upon INSERT/UPDATE
- self._readonly_props = set(
+ self._readonly_props = {
self._columntoproperty[col]
for col in self._columntoproperty
if self._columntoproperty[col] not in self._identity_key_props
@@ -1421,12 +1557,12 @@ class Mapper(
not hasattr(col, "table")
or col.table not in self._cols_by_table
)
- )
+ }
- def _configure_properties(self):
+ def _configure_properties(self) -> None:
# TODO: consider using DedupeColumnCollection
- self.columns = self.c = sql_base.ColumnCollection()
+ self.columns = self.c = sql_base.ColumnCollection() # type: ignore
# object attribute names mapped to MapperProperty objects
self._props = util.OrderedDict()
@@ -1454,7 +1590,6 @@ class Mapper(
continue
column_key = (self.column_prefix or "") + column.key
-
if self._should_exclude(
column.key,
column_key,
@@ -1542,6 +1677,7 @@ class Mapper(
col = self.polymorphic_on
if isinstance(col, schema.Column) and (
self.with_polymorphic is None
+ or self.with_polymorphic[1] is None
or self.with_polymorphic[1].corresponding_column(col)
is None
):
@@ -1763,8 +1899,8 @@ class Mapper(
self.columns.add(col, key)
for col in prop.columns + prop._orig_columns:
- for col in col.proxy_set:
- self._columntoproperty[col] = prop
+ for proxy_col in col.proxy_set:
+ self._columntoproperty[proxy_col] = prop
prop.key = key
@@ -2033,7 +2169,9 @@ class Mapper(
self._check_configure()
return iter(self._props.values())
- def _mappers_from_spec(self, spec, selectable):
+ def _mappers_from_spec(
+ self, spec: Any, selectable: Optional[FromClause]
+ ) -> Sequence[Mapper[Any]]:
"""given a with_polymorphic() argument, return the set of mappers it
represents.
@@ -2044,7 +2182,7 @@ class Mapper(
if spec == "*":
mappers = list(self.self_and_descendants)
elif spec:
- mappers = set()
+ mapper_set = set()
for m in util.to_list(spec):
m = _class_to_mapper(m)
if not m.isa(self):
@@ -2053,10 +2191,10 @@ class Mapper(
)
if selectable is None:
- mappers.update(m.iterate_to_root())
+ mapper_set.update(m.iterate_to_root())
else:
- mappers.add(m)
- mappers = [m for m in self.self_and_descendants if m in mappers]
+ mapper_set.add(m)
+ mappers = [m for m in self.self_and_descendants if m in mapper_set]
else:
mappers = []
@@ -2067,7 +2205,9 @@ class Mapper(
mappers = [m for m in mappers if m.local_table in tables]
return mappers
- def _selectable_from_mappers(self, mappers, innerjoin):
+ def _selectable_from_mappers(
+ self, mappers: Iterable[Mapper[Any]], innerjoin: bool
+ ) -> FromClause:
"""given a list of mappers (assumed to be within this mapper's
inheritance hierarchy), construct an outerjoin amongst those mapper's
mapped tables.
@@ -2098,13 +2238,13 @@ class Mapper(
def _single_table_criterion(self):
if self.single and self.inherits and self.polymorphic_on is not None:
return self.polymorphic_on._annotate({"parentmapper": self}).in_(
- m.polymorphic_identity for m in self.self_and_descendants
+ [m.polymorphic_identity for m in self.self_and_descendants]
)
else:
return None
@HasMemoized.memoized_attribute
- def _with_polymorphic_mappers(self):
+ def _with_polymorphic_mappers(self) -> Sequence[Mapper[Any]]:
self._check_configure()
if not self.with_polymorphic:
@@ -2124,8 +2264,8 @@ class Mapper(
"""
self._check_configure()
- @HasMemoized.memoized_attribute
- def _with_polymorphic_selectable(self):
+ @HasMemoized_ro_memoized_attribute
+ def _with_polymorphic_selectable(self) -> FromClause:
if not self.with_polymorphic:
return self.persist_selectable
@@ -2143,7 +2283,7 @@ class Mapper(
"""
- @HasMemoized.memoized_attribute
+ @HasMemoized_ro_memoized_attribute
def _insert_cols_evaluating_none(self):
return dict(
(
@@ -2250,7 +2390,7 @@ class Mapper(
@HasMemoized.memoized_instancemethod
def __clause_element__(self):
- annotations = {
+ annotations: Dict[str, Any] = {
"entity_namespace": self,
"parententity": self,
"parentmapper": self,
@@ -2290,7 +2430,7 @@ class Mapper(
)
@property
- def selectable(self):
+ def selectable(self) -> FromClause:
"""The :class:`_schema.FromClause` construct this
:class:`_orm.Mapper` selects from by default.
@@ -2302,8 +2442,11 @@ class Mapper(
return self._with_polymorphic_selectable
def _with_polymorphic_args(
- self, spec=None, selectable=False, innerjoin=False
- ):
+ self,
+ spec: Any = None,
+ selectable: Union[Literal[False, None], FromClause] = False,
+ innerjoin: bool = False,
+ ) -> Tuple[Sequence[Mapper[Any]], FromClause]:
if selectable not in (None, False):
selectable = coercions.expect(
roles.StrictFromClauseRole, selectable, allow_select=True
@@ -2357,7 +2500,7 @@ class Mapper(
]
@HasMemoized.memoized_attribute
- def _polymorphic_adapter(self):
+ def _polymorphic_adapter(self) -> Optional[sql_util.ColumnAdapter]:
if self.with_polymorphic:
return sql_util.ColumnAdapter(
self.selectable, equivalents=self._equivalent_columns
@@ -2394,7 +2537,7 @@ class Mapper(
yield c
@HasMemoized.memoized_attribute
- def attrs(self) -> util.ReadOnlyProperties["MapperProperty"]:
+ def attrs(self) -> util.ReadOnlyProperties[MapperProperty[Any]]:
"""A namespace of all :class:`.MapperProperty` objects
associated this mapper.
@@ -2432,7 +2575,7 @@ class Mapper(
return util.ReadOnlyProperties(self._props)
@HasMemoized.memoized_attribute
- def all_orm_descriptors(self):
+ def all_orm_descriptors(self) -> util.ReadOnlyProperties[InspectionAttr]:
"""A namespace of all :class:`.InspectionAttr` attributes associated
with the mapped class.
@@ -2503,7 +2646,7 @@ class Mapper(
@HasMemoized.memoized_attribute
@util.preload_module("sqlalchemy.orm.descriptor_props")
- def synonyms(self):
+ def synonyms(self) -> util.ReadOnlyProperties[Synonym[Any]]:
"""Return a namespace of all :class:`.Synonym`
properties maintained by this :class:`_orm.Mapper`.
@@ -2523,7 +2666,7 @@ class Mapper(
return self.class_
@HasMemoized.memoized_attribute
- def column_attrs(self):
+ def column_attrs(self) -> util.ReadOnlyProperties[ColumnProperty[Any]]:
"""Return a namespace of all :class:`.ColumnProperty`
properties maintained by this :class:`_orm.Mapper`.
@@ -2536,9 +2679,9 @@ class Mapper(
"""
return self._filter_properties(properties.ColumnProperty)
- @util.preload_module("sqlalchemy.orm.relationships")
@HasMemoized.memoized_attribute
- def relationships(self):
+ @util.preload_module("sqlalchemy.orm.relationships")
+ def relationships(self) -> util.ReadOnlyProperties[Relationship[Any]]:
"""A namespace of all :class:`.Relationship` properties
maintained by this :class:`_orm.Mapper`.
@@ -2567,7 +2710,7 @@ class Mapper(
@HasMemoized.memoized_attribute
@util.preload_module("sqlalchemy.orm.descriptor_props")
- def composites(self):
+ def composites(self) -> util.ReadOnlyProperties[Composite[Any]]:
"""Return a namespace of all :class:`.Composite`
properties maintained by this :class:`_orm.Mapper`.
@@ -2582,7 +2725,9 @@ class Mapper(
util.preloaded.orm_descriptor_props.Composite
)
- def _filter_properties(self, type_):
+ def _filter_properties(
+ self, type_: Type[_MP]
+ ) -> util.ReadOnlyProperties[_MP]:
self._check_configure()
return util.ReadOnlyProperties(
util.OrderedDict(
@@ -2610,7 +2755,7 @@ class Mapper(
)
@HasMemoized.memoized_attribute
- def _equivalent_columns(self):
+ def _equivalent_columns(self) -> _EquivalentColumnMap:
"""Create a map of all equivalent columns, based on
the determination of column pairs that are equated to
one another based on inherit condition. This is designed
@@ -2630,18 +2775,18 @@ class Mapper(
}
"""
- result = util.column_dict()
+ result: _EquivalentColumnMap = {}
def visit_binary(binary):
if binary.operator == operators.eq:
if binary.left in result:
result[binary.left].add(binary.right)
else:
- result[binary.left] = util.column_set((binary.right,))
+ result[binary.left] = {binary.right}
if binary.right in result:
result[binary.right].add(binary.left)
else:
- result[binary.right] = util.column_set((binary.left,))
+ result[binary.right] = {binary.left}
for mapper in self.base_mapper.self_and_descendants:
if mapper.inherit_condition is not None:
@@ -2711,13 +2856,13 @@ class Mapper(
return False
- def common_parent(self, other):
+ def common_parent(self, other: Mapper[Any]) -> bool:
"""Return true if the given mapper shares a
common inherited parent as this mapper."""
return self.base_mapper is other.base_mapper
- def is_sibling(self, other):
+ def is_sibling(self, other: Mapper[Any]) -> bool:
"""return true if the other mapper is an inheriting sibling to this
one. common parent but different branch
@@ -2728,7 +2873,9 @@ class Mapper(
and not other.isa(self)
)
- def _canload(self, state, allow_subtypes):
+ def _canload(
+ self, state: InstanceState[Any], allow_subtypes: bool
+ ) -> bool:
s = self.primary_mapper()
if self.polymorphic_on is not None or allow_subtypes:
return _state_mapper(state).isa(s)
@@ -2738,19 +2885,19 @@ class Mapper(
def isa(self, other: Mapper[Any]) -> bool:
"""Return True if the this mapper inherits from the given mapper."""
- m = self
+ m: Optional[Mapper[Any]] = self
while m and m is not other:
m = m.inherits
return bool(m)
- def iterate_to_root(self):
- m = self
+ def iterate_to_root(self) -> Iterator[Mapper[Any]]:
+ m: Optional[Mapper[Any]] = self
while m:
yield m
m = m.inherits
@HasMemoized.memoized_attribute
- def self_and_descendants(self):
+ def self_and_descendants(self) -> Sequence[Mapper[Any]]:
"""The collection including this mapper and all descendant mappers.
This includes not just the immediately inheriting mappers but
@@ -2765,7 +2912,7 @@ class Mapper(
stack.extend(item._inheriting_mappers)
return util.WeakSequence(descendants)
- def polymorphic_iterator(self):
+ def polymorphic_iterator(self) -> Iterator[Mapper[Any]]:
"""Iterate through the collection including this mapper and
all descendant mappers.
@@ -2778,18 +2925,18 @@ class Mapper(
"""
return iter(self.self_and_descendants)
- def primary_mapper(self):
+ def primary_mapper(self) -> Mapper[Any]:
"""Return the primary mapper corresponding to this mapper's class key
(class)."""
return self.class_manager.mapper
@property
- def primary_base_mapper(self):
+ def primary_base_mapper(self) -> Mapper[Any]:
return self.class_manager.mapper.base_mapper
def _result_has_identity_key(self, result, adapter=None):
- pk_cols = self.primary_key
+ pk_cols: Sequence[ColumnClause[Any]] = self.primary_key
if adapter:
pk_cols = [adapter.columns[c] for c in pk_cols]
rk = result.keys()
@@ -2799,25 +2946,35 @@ class Mapper(
else:
return True
- def identity_key_from_row(self, row, identity_token=None, adapter=None):
+ def identity_key_from_row(
+ self,
+ row: Optional[Union[Row, RowMapping]],
+ identity_token: Optional[Any] = None,
+ adapter: Optional[ColumnAdapter] = None,
+ ) -> _IdentityKeyType[_O]:
"""Return an identity-map key for use in storing/retrieving an
item from the identity map.
- :param row: A :class:`.Row` instance. The columns which are
- mapped by this :class:`_orm.Mapper` should be locatable in the row,
- preferably via the :class:`_schema.Column`
- object directly (as is the case
- when a :func:`_expression.select` construct is executed), or
- via string names of the form ``<tablename>_<colname>``.
+ :param row: A :class:`.Row` or :class:`.RowMapping` produced from a
+ result set that selected from the ORM mapped primary key columns.
+
+ .. versionchanged:: 2.0
+ :class:`.Row` or :class:`.RowMapping` are accepted
+ for the "row" argument
"""
- pk_cols = self.primary_key
+ pk_cols: Sequence[ColumnClause[Any]] = self.primary_key
if adapter:
pk_cols = [adapter.columns[c] for c in pk_cols]
+ if hasattr(row, "_mapping"):
+ mapping = row._mapping # type: ignore
+ else:
+ mapping = cast("Mapping[Any, Any]", row)
+
return (
self._identity_class,
- tuple(row[column] for column in pk_cols),
+ tuple(mapping[column] for column in pk_cols), # type: ignore
identity_token,
)
@@ -2852,12 +3009,12 @@ class Mapper(
"""
state = attributes.instance_state(instance)
- return self._identity_key_from_state(state, attributes.PASSIVE_OFF)
+ return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF)
def _identity_key_from_state(
self,
state: InstanceState[_O],
- passive: PassiveFlag = attributes.PASSIVE_RETURN_NO_VALUE,
+ passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE,
) -> _IdentityKeyType[_O]:
dict_ = state.dict
manager = state.manager
@@ -2884,7 +3041,7 @@ class Mapper(
"""
state = attributes.instance_state(instance)
identity_key = self._identity_key_from_state(
- state, attributes.PASSIVE_OFF
+ state, PassiveFlag.PASSIVE_OFF
)
return identity_key[1]
@@ -2913,14 +3070,14 @@ class Mapper(
@HasMemoized.memoized_attribute
def _all_pk_cols(self):
- collection = set()
+ collection: Set[ColumnClause[Any]] = set()
for table in self.tables:
collection.update(self._pks_by_table[table])
return collection
@HasMemoized.memoized_attribute
def _should_undefer_in_wildcard(self):
- cols = set(self.primary_key)
+ cols: Set[ColumnElement[Any]] = set(self.primary_key)
if self.polymorphic_on is not None:
cols.add(self.polymorphic_on)
return cols
@@ -2951,11 +3108,11 @@ class Mapper(
state = attributes.instance_state(obj)
dict_ = attributes.instance_dict(obj)
return self._get_committed_state_attr_by_column(
- state, dict_, column, passive=attributes.PASSIVE_OFF
+ state, dict_, column, passive=PassiveFlag.PASSIVE_OFF
)
def _get_committed_state_attr_by_column(
- self, state, dict_, column, passive=attributes.PASSIVE_RETURN_NO_VALUE
+ self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE
):
prop = self._columntoproperty[column]
@@ -2978,7 +3135,7 @@ class Mapper(
col_attribute_names = set(attribute_names).intersection(
state.mapper.column_attrs.keys()
)
- tables = set(
+ tables: Set[FromClause] = set(
chain(
*[
sql_util.find_tables(c, check_columns=True)
@@ -3002,7 +3159,7 @@ class Mapper(
state,
state.dict,
leftcol,
- passive=attributes.PASSIVE_NO_INITIALIZE,
+ passive=PassiveFlag.PASSIVE_NO_INITIALIZE,
)
if leftval in orm_util._none_set:
raise _OptGetColumnsNotAvailable()
@@ -3014,7 +3171,7 @@ class Mapper(
state,
state.dict,
rightcol,
- passive=attributes.PASSIVE_NO_INITIALIZE,
+ passive=PassiveFlag.PASSIVE_NO_INITIALIZE,
)
if rightval in orm_util._none_set:
raise _OptGetColumnsNotAvailable()
@@ -3022,7 +3179,7 @@ class Mapper(
None, rightval, type_=binary.right.type
)
- allconds = []
+ allconds: List[ColumnElement[bool]] = []
start = False
@@ -3035,6 +3192,9 @@ class Mapper(
elif not isinstance(mapper.local_table, expression.TableClause):
return None
if start and not mapper.single:
+ assert mapper.inherits
+ assert not mapper.concrete
+ assert mapper.inherit_condition is not None
allconds.append(mapper.inherit_condition)
tables.add(mapper.local_table)
@@ -3043,11 +3203,13 @@ class Mapper(
# descendant-most class should all be present and joined to each
# other.
try:
- allconds[0] = visitors.cloned_traverse(
+ _traversed = visitors.cloned_traverse(
allconds[0], {}, {"binary": visit_binary}
)
except _OptGetColumnsNotAvailable:
return None
+ else:
+ allconds[0] = _traversed
cond = sql.and_(*allconds)
@@ -3145,6 +3307,8 @@ class Mapper(
for pk in self.primary_key
]
+ in_expr: ColumnElement[Any]
+
if len(primary_key) > 1:
in_expr = sql.tuple_(*primary_key)
else:
@@ -3209,11 +3373,22 @@ class Mapper(
traverse all objects without relying on cascades.
"""
- visited_states = set()
+ visited_states: Set[InstanceState[Any]] = set()
prp, mpp = object(), object()
assert state.mapper.isa(self)
+ # this is actually a recursive structure, fully typing it seems
+ # a little too difficult for what it's worth here
+ visitables: Deque[
+ Tuple[
+ Deque[Any],
+ object,
+ Optional[InstanceState[Any]],
+ Optional[_InstanceDict],
+ ]
+ ]
+
visitables = deque(
[(deque(state.mapper._props.values()), prp, state, state.dict)]
)
@@ -3226,8 +3401,10 @@ class Mapper(
if item_type is prp:
prop = iterator.popleft()
- if type_ not in prop.cascade:
+ if not prop.cascade or type_ not in prop.cascade:
continue
+ assert parent_state is not None
+ assert parent_dict is not None
queue = deque(
prop.cascade_iterator(
type_,
@@ -3267,7 +3444,7 @@ class Mapper(
@HasMemoized.memoized_attribute
def _sorted_tables(self):
- table_to_mapper = {}
+ table_to_mapper: Dict[Table, Mapper[Any]] = {}
for mapper in self.base_mapper.self_and_descendants:
for t in mapper.tables:
@@ -3316,9 +3493,9 @@ class Mapper(
ret[t] = table_to_mapper[t]
return ret
- def _memo(self, key, callable_):
+ def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T:
if key in self._memoized_values:
- return self._memoized_values[key]
+ return cast(_T, self._memoized_values[key])
else:
self._memoized_values[key] = value = callable_()
return value
@@ -3328,14 +3505,22 @@ class Mapper(
"""memoized map of tables to collections of columns to be
synchronized upwards to the base mapper."""
- result = util.defaultdict(list)
+ result: util.defaultdict[
+ Table,
+ List[
+ Tuple[
+ Mapper[Any],
+ List[Tuple[ColumnElement[Any], ColumnElement[Any]]],
+ ]
+ ],
+ ] = util.defaultdict(list)
for table in self._sorted_tables:
cols = set(table.c)
for m in self.iterate_to_root():
if m._inherits_equated_pairs and cols.intersection(
reduce(
- set.union,
+ set.union, # type: ignore
[l.proxy_set for l, r in m._inherits_equated_pairs],
)
):
@@ -3440,7 +3625,7 @@ def _configure_registries(registries, cascade):
else:
return
- Mapper.dispatch._for_class(Mapper).before_configured()
+ Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore # noqa: E501
# initialize properties on all mappers
# note that _mapper_registry is unordered, which
# may randomly conceal/reveal issues related to
@@ -3449,7 +3634,7 @@ def _configure_registries(registries, cascade):
_do_configure_registries(registries, cascade)
finally:
_already_compiling = False
- Mapper.dispatch._for_class(Mapper).after_configured()
+ Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore
@util.preload_module("sqlalchemy.orm.decl_api")
@@ -3480,7 +3665,7 @@ def _do_configure_registries(registries, cascade):
"Original exception was: %s"
% (mapper, mapper._configure_failed)
)
- e._configure_failed = mapper._configure_failed
+ e._configure_failed = mapper._configure_failed # type: ignore
raise e
if not mapper.configured:
@@ -3636,7 +3821,7 @@ def _event_on_init(state, args, kwargs):
instrumenting_mapper._set_polymorphic_identity(state)
-class _ColumnMapping(dict):
+class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]):
"""Error reporting helper for mapper._columntoproperty."""
__slots__ = ("mapper",)