1# orm/_typing.py
2# Copyright (C) 2022-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8from __future__ import annotations
9
10import operator
11from typing import Any
12from typing import Dict
13from typing import Mapping
14from typing import Optional
15from typing import Protocol
16from typing import Tuple
17from typing import Type
18from typing import TYPE_CHECKING
19from typing import TypeGuard
20from typing import TypeVar
21from typing import Union
22
23from ..engine.interfaces import _CoreKnownExecutionOptions
24from ..sql import roles
25from ..sql._orm_types import DMLStrategyArgument as DMLStrategyArgument
26from ..sql._orm_types import (
27 SynchronizeSessionArgument as SynchronizeSessionArgument,
28)
29from ..sql._typing import _HasClauseElement
30from ..sql.elements import ColumnElement
31
32if TYPE_CHECKING:
33 from .attributes import _AttributeImpl
34 from .attributes import _CollectionAttributeImpl
35 from .attributes import _HasCollectionAdapter
36 from .attributes import QueryableAttribute
37 from .base import PassiveFlag
38 from .decl_api import registry as _registry_type
39 from .interfaces import InspectionAttr
40 from .interfaces import MapperProperty
41 from .interfaces import ORMOption
42 from .interfaces import UserDefinedOption
43 from .mapper import Mapper
44 from .relationships import RelationshipProperty
45 from .state import InstanceState
46 from .util import AliasedClass
47 from .util import AliasedInsp
48 from ..sql._typing import _CE
49 from ..sql.base import ExecutableOption
50
51_T = TypeVar("_T", bound=Any)
52
53_O = TypeVar("_O", bound=object)
54"""The 'ORM mapped object' type.
55
56"""
57
58
59if TYPE_CHECKING:
60 _RegistryType = _registry_type
61
62_InternalEntityType = Union["Mapper[_T]", "AliasedInsp[_T]"]
63
64_ExternalEntityType = Union[Type[_T], "AliasedClass[_T]"]
65
66_EntityType = Union[
67 Type[_T], "AliasedClass[_T]", "Mapper[_T]", "AliasedInsp[_T]"
68]
69
70
71_ClassDict = Mapping[str, Any]
72_InstanceDict = Dict[str, Any]
73
74_IdentityKeyType = Tuple[Type[_T], Tuple[Any, ...], Optional[Any]]
75
76_ORMColumnExprArgument = Union[
77 ColumnElement[_T],
78 _HasClauseElement[_T],
79 roles.ExpressionElementRole[_T],
80]
81
82
83_ORMCOLEXPR = TypeVar("_ORMCOLEXPR", bound=ColumnElement[Any])
84
85
86class _OrmKnownExecutionOptions(_CoreKnownExecutionOptions, total=False):
87 populate_existing: bool
88 autoflush: bool
89 synchronize_session: SynchronizeSessionArgument
90 dml_strategy: DMLStrategyArgument
91 is_delete_using: bool
92 is_update_from: bool
93 render_nulls: bool
94
95
96OrmExecuteOptionsParameter = Union[
97 _OrmKnownExecutionOptions, Mapping[str, Any]
98]
99
100
101class _HasPathString(Protocol):
102 def path_string(self) -> str: ...
103
104
105class _ORMAdapterProto(Protocol):
106 """protocol for the :class:`.AliasedInsp._orm_adapt_element` method
107 which is a synonym for :class:`.AliasedInsp._adapt_element`.
108
109
110 """
111
112 def __call__(self, obj: _CE, key: Optional[str] = None) -> _CE: ...
113
114
115class _LoaderCallable(Protocol):
116 def __call__(
117 self, state: InstanceState[Any], passive: PassiveFlag
118 ) -> Any: ...
119
120
121def is_orm_option(
122 opt: ExecutableOption,
123) -> TypeGuard[ORMOption]:
124 return not opt._is_core
125
126
127def is_user_defined_option(
128 opt: ExecutableOption,
129) -> TypeGuard[UserDefinedOption]:
130 return not opt._is_core and opt._is_user_defined # type: ignore[attr-defined] # noqa: E501
131
132
133def is_composite_class(obj: Any) -> bool:
134 # inlining is_dataclass(obj)
135 return hasattr(obj, "__composite_values__") or hasattr(
136 obj, "__dataclass_fields__"
137 )
138
139
140if TYPE_CHECKING:
141
142 def insp_is_mapper_property(
143 obj: Any,
144 ) -> TypeGuard[MapperProperty[Any]]: ...
145
146 def insp_is_mapper(obj: Any) -> TypeGuard[Mapper[Any]]: ...
147
148 def insp_is_aliased_class(obj: Any) -> TypeGuard[AliasedInsp[Any]]: ...
149
150 def insp_is_attribute(
151 obj: InspectionAttr,
152 ) -> TypeGuard[QueryableAttribute[Any]]: ...
153
154 def attr_is_internal_proxy(
155 obj: InspectionAttr,
156 ) -> TypeGuard[QueryableAttribute[Any]]: ...
157
158 def prop_is_relationship(
159 prop: MapperProperty[Any],
160 ) -> TypeGuard[RelationshipProperty[Any]]: ...
161
162 def is_collection_impl(
163 impl: _AttributeImpl,
164 ) -> TypeGuard[_CollectionAttributeImpl]: ...
165
166 def is_has_collection_adapter(
167 impl: _AttributeImpl,
168 ) -> TypeGuard[_HasCollectionAdapter]: ...
169
170else:
171 insp_is_mapper_property = operator.attrgetter("is_property")
172 insp_is_mapper = operator.attrgetter("is_mapper")
173 insp_is_aliased_class = operator.attrgetter("is_aliased_class")
174 insp_is_attribute = operator.attrgetter("is_attribute")
175 attr_is_internal_proxy = operator.attrgetter("_is_internal_proxy")
176 is_collection_impl = operator.attrgetter("collection")
177 prop_is_relationship = operator.attrgetter("_is_relationship")
178 is_has_collection_adapter = operator.attrgetter(
179 "_is_has_collection_adapter"
180 )