SQLAlchemy 2.1.0rc1__cp315-cp315-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. sqlalchemy/__init__.py +299 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +171 -0
  4. sqlalchemy/connectors/asyncio.py +476 -0
  5. sqlalchemy/connectors/pyodbc.py +248 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +29 -0
  8. sqlalchemy/dialects/mssql/__init__.py +88 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4833 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +345 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +242 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +130 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +697 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +260 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +241 -0
  21. sqlalchemy/dialects/mysql/base.py +3896 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +107 -0
  23. sqlalchemy/dialects/mysql/dml.py +279 -0
  24. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  25. sqlalchemy/dialects/mysql/expression.py +146 -0
  26. sqlalchemy/dialects/mysql/json.py +92 -0
  27. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  28. sqlalchemy/dialects/mysql/mariadbconnector.py +314 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +318 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +188 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  34. sqlalchemy/dialects/mysql/reflection.py +724 -0
  35. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  36. sqlalchemy/dialects/mysql/types.py +845 -0
  37. sqlalchemy/dialects/oracle/__init__.py +85 -0
  38. sqlalchemy/dialects/oracle/base.py +3847 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +157 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +898 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +366 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +170 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
  50. sqlalchemy/dialects/postgresql/base.py +5935 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +599 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +422 -0
  55. sqlalchemy/dialects/postgresql/json.py +411 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +535 -0
  57. sqlalchemy/dialects/postgresql/operators.py +129 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +655 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
  60. sqlalchemy/dialects/postgresql/provision.py +202 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +800 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +59 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
  68. sqlalchemy/dialects/sqlite/base.py +3103 -0
  69. sqlalchemy/dialects/sqlite/dml.py +314 -0
  70. sqlalchemy/dialects/sqlite/json.py +134 -0
  71. sqlalchemy/dialects/sqlite/provision.py +237 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp315-win_amd64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win_amd64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win_amd64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win_amd64.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3357 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2425 -0
  88. sqlalchemy/engine/default.py +2627 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3636 -0
  91. sqlalchemy/engine/mock.py +133 -0
  92. sqlalchemy/engine/processors.py +83 -0
  93. sqlalchemy/engine/reflection.py +2141 -0
  94. sqlalchemy/engine/result.py +2012 -0
  95. sqlalchemy/engine/row.py +397 -0
  96. sqlalchemy/engine/strategies.py +16 -0
  97. sqlalchemy/engine/url.py +922 -0
  98. sqlalchemy/engine/util.py +164 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +675 -0
  102. sqlalchemy/event/base.py +473 -0
  103. sqlalchemy/event/legacy.py +259 -0
  104. sqlalchemy/event/registry.py +391 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +939 -0
  107. sqlalchemy/ext/__init__.py +10 -0
  108. sqlalchemy/ext/associationproxy.py +2073 -0
  109. sqlalchemy/ext/asyncio/__init__.py +29 -0
  110. sqlalchemy/ext/asyncio/base.py +281 -0
  111. sqlalchemy/ext/asyncio/engine.py +1487 -0
  112. sqlalchemy/ext/asyncio/exc.py +21 -0
  113. sqlalchemy/ext/asyncio/result.py +994 -0
  114. sqlalchemy/ext/asyncio/scoping.py +1679 -0
  115. sqlalchemy/ext/asyncio/session.py +2006 -0
  116. sqlalchemy/ext/automap.py +1702 -0
  117. sqlalchemy/ext/baked.py +558 -0
  118. sqlalchemy/ext/compiler.py +601 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +561 -0
  121. sqlalchemy/ext/horizontal_shard.py +481 -0
  122. sqlalchemy/ext/hybrid.py +1877 -0
  123. sqlalchemy/ext/indexable.py +364 -0
  124. sqlalchemy/ext/instrumentation.py +450 -0
  125. sqlalchemy/ext/mutable.py +1081 -0
  126. sqlalchemy/ext/orderinglist.py +440 -0
  127. sqlalchemy/ext/serializer.py +184 -0
  128. sqlalchemy/future/__init__.py +17 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +188 -0
  131. sqlalchemy/log.py +279 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +180 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +991 -0
  137. sqlalchemy/orm/bulk_persistence.py +2168 -0
  138. sqlalchemy/orm/clsregistry.py +630 -0
  139. sqlalchemy/orm/collections.py +1569 -0
  140. sqlalchemy/orm/context.py +3475 -0
  141. sqlalchemy/orm/decl_api.py +2283 -0
  142. sqlalchemy/orm/decl_base.py +2320 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +306 -0
  146. sqlalchemy/orm/evaluator.py +378 -0
  147. sqlalchemy/orm/events.py +3387 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +749 -0
  151. sqlalchemy/orm/interfaces.py +1595 -0
  152. sqlalchemy/orm/loading.py +1712 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4465 -0
  155. sqlalchemy/orm/path_registry.py +907 -0
  156. sqlalchemy/orm/persistence.py +1790 -0
  157. sqlalchemy/orm/properties.py +972 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3608 -0
  160. sqlalchemy/orm/scoping.py +2233 -0
  161. sqlalchemy/orm/session.py +5468 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3552 -0
  165. sqlalchemy/orm/strategy_options.py +2648 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +797 -0
  168. sqlalchemy/orm/util.py +2461 -0
  169. sqlalchemy/orm/writeonly.py +701 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1540 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +583 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +75 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +402 -0
  178. sqlalchemy/sql/_cache_key_cy.cp315-win_amd64.pyd +0 -0
  179. sqlalchemy/sql/_cache_key_cy.py +363 -0
  180. sqlalchemy/sql/_dml_constructors.py +132 -0
  181. sqlalchemy/sql/_elements_constructors.py +2190 -0
  182. sqlalchemy/sql/_orm_types.py +19 -0
  183. sqlalchemy/sql/_selectable_constructors.py +840 -0
  184. sqlalchemy/sql/_typing.py +500 -0
  185. sqlalchemy/sql/_util_cy.cp315-win_amd64.pyd +0 -0
  186. sqlalchemy/sql/_util_cy.pxd +11 -0
  187. sqlalchemy/sql/_util_cy.py +127 -0
  188. sqlalchemy/sql/annotation.py +590 -0
  189. sqlalchemy/sql/base.py +2702 -0
  190. sqlalchemy/sql/cache_key.py +915 -0
  191. sqlalchemy/sql/coercions.py +1373 -0
  192. sqlalchemy/sql/compiler.py +8453 -0
  193. sqlalchemy/sql/crud.py +1816 -0
  194. sqlalchemy/sql/ddl.py +1962 -0
  195. sqlalchemy/sql/default_comparator.py +660 -0
  196. sqlalchemy/sql/dml.py +2018 -0
  197. sqlalchemy/sql/elements.py +6057 -0
  198. sqlalchemy/sql/events.py +458 -0
  199. sqlalchemy/sql/expression.py +171 -0
  200. sqlalchemy/sql/functions.py +2380 -0
  201. sqlalchemy/sql/lambdas.py +1442 -0
  202. sqlalchemy/sql/naming.py +204 -0
  203. sqlalchemy/sql/operators.py +2909 -0
  204. sqlalchemy/sql/roles.py +332 -0
  205. sqlalchemy/sql/schema.py +7075 -0
  206. sqlalchemy/sql/selectable.py +7634 -0
  207. sqlalchemy/sql/sqltypes.py +4130 -0
  208. sqlalchemy/sql/traversals.py +1041 -0
  209. sqlalchemy/sql/type_api.py +2450 -0
  210. sqlalchemy/sql/util.py +1496 -0
  211. sqlalchemy/sql/visitors.py +1153 -0
  212. sqlalchemy/testing/__init__.py +97 -0
  213. sqlalchemy/testing/assertions.py +1007 -0
  214. sqlalchemy/testing/assertsql.py +519 -0
  215. sqlalchemy/testing/asyncio.py +128 -0
  216. sqlalchemy/testing/cancellation.py +237 -0
  217. sqlalchemy/testing/config.py +440 -0
  218. sqlalchemy/testing/engines.py +482 -0
  219. sqlalchemy/testing/entities.py +117 -0
  220. sqlalchemy/testing/exclusions.py +501 -0
  221. sqlalchemy/testing/fixtures/__init__.py +30 -0
  222. sqlalchemy/testing/fixtures/base.py +426 -0
  223. sqlalchemy/testing/fixtures/mypy.py +247 -0
  224. sqlalchemy/testing/fixtures/orm.py +227 -0
  225. sqlalchemy/testing/fixtures/sql.py +538 -0
  226. sqlalchemy/testing/pickleable.py +155 -0
  227. sqlalchemy/testing/plugin/__init__.py +6 -0
  228. sqlalchemy/testing/plugin/bootstrap.py +50 -0
  229. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  230. sqlalchemy/testing/plugin/pytestplugin.py +896 -0
  231. sqlalchemy/testing/profiles_file.py +350 -0
  232. sqlalchemy/testing/profiling.py +294 -0
  233. sqlalchemy/testing/provision.py +633 -0
  234. sqlalchemy/testing/requirements.py +1971 -0
  235. sqlalchemy/testing/schema.py +198 -0
  236. sqlalchemy/testing/suite/__init__.py +19 -0
  237. sqlalchemy/testing/suite/test_cte.py +237 -0
  238. sqlalchemy/testing/suite/test_ddl.py +420 -0
  239. sqlalchemy/testing/suite/test_dialect.py +776 -0
  240. sqlalchemy/testing/suite/test_insert.py +630 -0
  241. sqlalchemy/testing/suite/test_reflection.py +3815 -0
  242. sqlalchemy/testing/suite/test_results.py +660 -0
  243. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  244. sqlalchemy/testing/suite/test_select.py +2112 -0
  245. sqlalchemy/testing/suite/test_sequence.py +317 -0
  246. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  247. sqlalchemy/testing/suite/test_types.py +2271 -0
  248. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  249. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  250. sqlalchemy/testing/util.py +575 -0
  251. sqlalchemy/testing/warnings.py +52 -0
  252. sqlalchemy/types.py +75 -0
  253. sqlalchemy/util/__init__.py +165 -0
  254. sqlalchemy/util/_collections.py +688 -0
  255. sqlalchemy/util/_collections_cy.cp315-win_amd64.pyd +0 -0
  256. sqlalchemy/util/_collections_cy.pxd +8 -0
  257. sqlalchemy/util/_collections_cy.py +516 -0
  258. sqlalchemy/util/_has_cython.py +48 -0
  259. sqlalchemy/util/_immutabledict_cy.cp315-win_amd64.pyd +0 -0
  260. sqlalchemy/util/_immutabledict_cy.py +240 -0
  261. sqlalchemy/util/compat.py +298 -0
  262. sqlalchemy/util/concurrency.py +272 -0
  263. sqlalchemy/util/cython.py +95 -0
  264. sqlalchemy/util/deprecations.py +401 -0
  265. sqlalchemy/util/langhelpers.py +2797 -0
  266. sqlalchemy/util/preloaded.py +153 -0
  267. sqlalchemy/util/queue.py +304 -0
  268. sqlalchemy/util/tool_support.py +202 -0
  269. sqlalchemy/util/topological.py +120 -0
  270. sqlalchemy/util/typing.py +709 -0
  271. sqlalchemy-2.1.0rc1.dist-info/METADATA +270 -0
  272. sqlalchemy-2.1.0rc1.dist-info/RECORD +276 -0
  273. sqlalchemy-2.1.0rc1.dist-info/WHEEL +5 -0
  274. sqlalchemy-2.1.0rc1.dist-info/licenses/AUTHORS +30 -0
  275. sqlalchemy-2.1.0rc1.dist-info/licenses/LICENSE +19 -0
  276. sqlalchemy-2.1.0rc1.dist-info/top_level.txt +1 -0
sqlalchemy/orm/util.py ADDED
@@ -0,0 +1,2461 @@
1
+ # orm/util.py
2
+ # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+ # mypy: allow-untyped-defs, allow-untyped-calls
8
+
9
+ from __future__ import annotations
10
+
11
+ import enum
12
+ import functools
13
+ import re
14
+ import types
15
+ import typing
16
+ from typing import AbstractSet
17
+ from typing import Any
18
+ from typing import Callable
19
+ from typing import cast
20
+ from typing import Dict
21
+ from typing import FrozenSet
22
+ from typing import Generic
23
+ from typing import get_origin
24
+ from typing import Iterable
25
+ from typing import Iterator
26
+ from typing import List
27
+ from typing import Literal
28
+ from typing import Match
29
+ from typing import Optional
30
+ from typing import Protocol
31
+ from typing import Sequence
32
+ from typing import Tuple
33
+ from typing import Type
34
+ from typing import TYPE_CHECKING
35
+ from typing import TypeVar
36
+ from typing import Union
37
+ import weakref
38
+
39
+ from . import attributes # noqa
40
+ from . import exc as orm_exc
41
+ from ._typing import _O
42
+ from ._typing import insp_is_aliased_class
43
+ from ._typing import insp_is_mapper
44
+ from ._typing import prop_is_relationship
45
+ from .base import _class_to_mapper as _class_to_mapper
46
+ from .base import _MappedAnnotationBase
47
+ from .base import _never_set as _never_set # noqa: F401
48
+ from .base import _none_only_set as _none_only_set # noqa: F401
49
+ from .base import _none_set as _none_set # noqa: F401
50
+ from .base import attribute_str as attribute_str # noqa: F401
51
+ from .base import class_mapper as class_mapper
52
+ from .base import DynamicMapped
53
+ from .base import InspectionAttr as InspectionAttr
54
+ from .base import instance_str as instance_str # noqa: F401
55
+ from .base import Mapped
56
+ from .base import object_mapper as object_mapper
57
+ from .base import object_state as object_state # noqa: F401
58
+ from .base import opt_manager_of_class
59
+ from .base import ORMDescriptor
60
+ from .base import state_attribute_str as state_attribute_str # noqa: F401
61
+ from .base import state_class_str as state_class_str # noqa: F401
62
+ from .base import state_str as state_str # noqa: F401
63
+ from .base import WriteOnlyMapped
64
+ from .interfaces import CriteriaOption
65
+ from .interfaces import MapperProperty as MapperProperty
66
+ from .interfaces import ORMColumnsClauseRole
67
+ from .interfaces import ORMEntityColumnsClauseRole
68
+ from .interfaces import ORMFromClauseRole
69
+ from .path_registry import PathRegistry as PathRegistry
70
+ from .. import event
71
+ from .. import exc as sa_exc
72
+ from .. import inspection
73
+ from .. import sql
74
+ from .. import util
75
+ from ..engine.result import result_tuple
76
+ from ..sql import coercions
77
+ from ..sql import expression
78
+ from ..sql import lambdas
79
+ from ..sql import roles
80
+ from ..sql import util as sql_util
81
+ from ..sql import visitors
82
+ from ..sql._typing import is_selectable
83
+ from ..sql.annotation import SupportsCloneAnnotations
84
+ from ..sql.base import WriteableColumnCollection
85
+ from ..sql.cache_key import HasCacheKey
86
+ from ..sql.cache_key import MemoizedHasCacheKey
87
+ from ..sql.elements import ColumnElement
88
+ from ..sql.elements import KeyedColumnElement
89
+ from ..sql.schema import MetaData
90
+ from ..sql.selectable import FromClause
91
+ from ..sql.selectable import GenerativeSelect
92
+ from ..util.langhelpers import MemoizedSlots
93
+ from ..util.typing import de_stringify_annotation as _de_stringify_annotation
94
+ from ..util.typing import eval_name_only as _eval_name_only
95
+ from ..util.typing import fixup_container_fwd_refs
96
+ from ..util.typing import GenericProtocol
97
+ from ..util.typing import is_origin_of_cls
98
+ from ..util.typing import TupleAny
99
+ from ..util.typing import Unpack
100
+
101
+ if typing.TYPE_CHECKING:
102
+ from ._typing import _EntityType
103
+ from ._typing import _IdentityKeyType
104
+ from ._typing import _InternalEntityType
105
+ from ._typing import _ORMCOLEXPR
106
+ from .context import _MapperEntity
107
+ from .context import _ORMCompileState
108
+ from .decl_api import RegistryType
109
+ from .mapper import Mapper
110
+ from .path_registry import _AbstractEntityRegistry
111
+ from .query import Query
112
+ from .relationships import RelationshipProperty
113
+ from ..engine import Row
114
+ from ..engine import RowMapping
115
+ from ..sql._typing import _CE
116
+ from ..sql._typing import _ColumnExpressionArgument
117
+ from ..sql._typing import _EquivalentColumnMap
118
+ from ..sql._typing import _FromClauseArgument
119
+ from ..sql._typing import _OnClauseArgument
120
+ from ..sql._typing import _PropagateAttrsType
121
+ from ..sql.annotation import _SA
122
+ from ..sql.base import ReadOnlyColumnCollection
123
+ from ..sql.elements import BindParameter
124
+ from ..sql.selectable import _ColumnsClauseElement
125
+ from ..sql.selectable import Select
126
+ from ..sql.selectable import Selectable
127
+ from ..sql.visitors import anon_map
128
+ from ..util.typing import _AnnotationScanType
129
+ from ..util.typing import _MatchedOnType
130
+
131
+ _T = TypeVar("_T", bound=Any)
132
+
133
+ all_cascades = frozenset(
134
+ (
135
+ "delete",
136
+ "delete-orphan",
137
+ "all",
138
+ "merge",
139
+ "expunge",
140
+ "save-update",
141
+ "refresh-expire",
142
+ "none",
143
+ )
144
+ )
145
+
146
+ _de_stringify_partial = functools.partial(
147
+ functools.partial,
148
+ locals_=util.immutabledict(
149
+ {
150
+ "Mapped": Mapped,
151
+ "WriteOnlyMapped": WriteOnlyMapped,
152
+ "DynamicMapped": DynamicMapped,
153
+ }
154
+ ),
155
+ )
156
+
157
+ # partial is practically useless as we have to write out the whole
158
+ # function and maintain the signature anyway
159
+
160
+
161
+ class _DeStringifyAnnotation(Protocol):
162
+ def __call__(
163
+ self,
164
+ cls: Type[Any],
165
+ annotation: _AnnotationScanType,
166
+ originating_module: str,
167
+ *,
168
+ str_cleanup_fn: Optional[Callable[[str, str], str]] = None,
169
+ include_generic: bool = False,
170
+ ) -> _MatchedOnType: ...
171
+
172
+
173
+ de_stringify_annotation = cast(
174
+ _DeStringifyAnnotation, _de_stringify_partial(_de_stringify_annotation)
175
+ )
176
+
177
+
178
+ class _EvalNameOnly(Protocol):
179
+ def __call__(self, name: str, module_name: str) -> Any: ...
180
+
181
+
182
+ eval_name_only = cast(_EvalNameOnly, _de_stringify_partial(_eval_name_only))
183
+
184
+
185
+ class CascadeOptions(FrozenSet[str]):
186
+ """Keeps track of the options sent to
187
+ :paramref:`.relationship.cascade`"""
188
+
189
+ _add_w_all_cascades = all_cascades.difference(
190
+ ["all", "none", "delete-orphan"]
191
+ )
192
+ _allowed_cascades = all_cascades
193
+
194
+ _viewonly_cascades = ["expunge", "all", "none", "refresh-expire", "merge"]
195
+
196
+ __slots__ = (
197
+ "save_update",
198
+ "delete",
199
+ "refresh_expire",
200
+ "merge",
201
+ "expunge",
202
+ "delete_orphan",
203
+ )
204
+
205
+ save_update: bool
206
+ delete: bool
207
+ refresh_expire: bool
208
+ merge: bool
209
+ expunge: bool
210
+ delete_orphan: bool
211
+
212
+ def __new__(
213
+ cls, value_list: Optional[Union[Iterable[str], str]]
214
+ ) -> CascadeOptions:
215
+ if isinstance(value_list, str) or value_list is None:
216
+ return cls.from_string(value_list) # type: ignore[no-any-return]
217
+ values = set(value_list)
218
+ if values.difference(cls._allowed_cascades):
219
+ raise sa_exc.ArgumentError(
220
+ "Invalid cascade option(s): %s"
221
+ % ", ".join(
222
+ [
223
+ repr(x)
224
+ for x in sorted(
225
+ values.difference(cls._allowed_cascades)
226
+ )
227
+ ]
228
+ )
229
+ )
230
+
231
+ if "all" in values:
232
+ values.update(cls._add_w_all_cascades)
233
+ if "none" in values:
234
+ values.clear()
235
+ values.discard("all")
236
+
237
+ self = super().__new__(cls, values)
238
+ self.save_update = "save-update" in values
239
+ self.delete = "delete" in values
240
+ self.refresh_expire = "refresh-expire" in values
241
+ self.merge = "merge" in values
242
+ self.expunge = "expunge" in values
243
+ self.delete_orphan = "delete-orphan" in values
244
+
245
+ if self.delete_orphan and not self.delete:
246
+ util.warn("The 'delete-orphan' cascade option requires 'delete'.")
247
+ return self
248
+
249
+ def __repr__(self):
250
+ return "CascadeOptions(%r)" % (",".join([x for x in sorted(self)]))
251
+
252
+ @classmethod
253
+ def from_string(cls, arg):
254
+ values = [c for c in re.split(r"\s*,\s*", arg or "") if c]
255
+ return cls(values)
256
+
257
+
258
+ def _metadata_for_cls(cls: Type[Any], registry: RegistryType) -> MetaData:
259
+ meta = getattr(cls, "metadata", None)
260
+ if meta is not None and isinstance(meta, MetaData):
261
+ return meta
262
+ return registry.metadata
263
+
264
+
265
+ def _validator_events(desc, key, validator, include_removes, include_backrefs):
266
+ """Runs a validation method on an attribute value to be set or
267
+ appended.
268
+ """
269
+
270
+ if not include_backrefs:
271
+
272
+ def detect_is_backref(state, initiator):
273
+ impl = state.manager[key].impl
274
+ return initiator.impl is not impl
275
+
276
+ if include_removes:
277
+
278
+ def append(state, value, initiator):
279
+ if initiator.op is not attributes.OP_BULK_REPLACE and (
280
+ include_backrefs or not detect_is_backref(state, initiator)
281
+ ):
282
+ return validator(state.obj(), key, value, False)
283
+ else:
284
+ return value
285
+
286
+ def bulk_set(state, values, initiator):
287
+ if include_backrefs or not detect_is_backref(state, initiator):
288
+ obj = state.obj()
289
+ values[:] = [
290
+ validator(obj, key, value, False) for value in values
291
+ ]
292
+
293
+ def set_(state, value, oldvalue, initiator):
294
+ if include_backrefs or not detect_is_backref(state, initiator):
295
+ return validator(state.obj(), key, value, False)
296
+ else:
297
+ return value
298
+
299
+ def remove(state, value, initiator):
300
+ if include_backrefs or not detect_is_backref(state, initiator):
301
+ validator(state.obj(), key, value, True)
302
+
303
+ else:
304
+
305
+ def append(state, value, initiator):
306
+ if initiator.op is not attributes.OP_BULK_REPLACE and (
307
+ include_backrefs or not detect_is_backref(state, initiator)
308
+ ):
309
+ return validator(state.obj(), key, value)
310
+ else:
311
+ return value
312
+
313
+ def bulk_set(state, values, initiator):
314
+ if include_backrefs or not detect_is_backref(state, initiator):
315
+ obj = state.obj()
316
+ values[:] = [validator(obj, key, value) for value in values]
317
+
318
+ def set_(state, value, oldvalue, initiator):
319
+ if include_backrefs or not detect_is_backref(state, initiator):
320
+ return validator(state.obj(), key, value)
321
+ else:
322
+ return value
323
+
324
+ event.listen(desc, "append", append, raw=True, retval=True)
325
+ event.listen(desc, "bulk_replace", bulk_set, raw=True)
326
+ event.listen(desc, "set", set_, raw=True, retval=True)
327
+ if include_removes:
328
+ event.listen(desc, "remove", remove, raw=True, retval=True)
329
+
330
+
331
+ def polymorphic_union(
332
+ table_map, typecolname, aliasname="p_union", cast_nulls=True
333
+ ):
334
+ """Create a ``UNION`` statement used by a polymorphic mapper.
335
+
336
+ See :ref:`concrete_inheritance` for an example of how
337
+ this is used.
338
+
339
+ :param table_map: mapping of polymorphic identities to
340
+ :class:`_schema.Table` objects.
341
+ :param typecolname: string name of a "discriminator" column, which will be
342
+ derived from the query, producing the polymorphic identity for
343
+ each row. If ``None``, no polymorphic discriminator is generated.
344
+ :param aliasname: name of the :func:`~sqlalchemy.sql.expression.alias()`
345
+ construct generated.
346
+ :param cast_nulls: if True, non-existent columns, which are represented
347
+ as labeled NULLs, will be passed into CAST. This is a legacy behavior
348
+ that is problematic on some backends such as Oracle - in which case it
349
+ can be set to False.
350
+
351
+ """
352
+
353
+ colnames: util.OrderedSet[str] = util.OrderedSet()
354
+ colnamemaps = {}
355
+ types = {}
356
+ for key in table_map:
357
+ table = table_map[key]
358
+
359
+ table = coercions.expect(roles.FromClauseRole, table)
360
+ table_map[key] = table
361
+
362
+ m = {}
363
+ for c in table.c:
364
+ if c.key == typecolname:
365
+ raise sa_exc.InvalidRequestError(
366
+ "Polymorphic union can't use '%s' as the discriminator "
367
+ "column due to mapped column %r; please apply the "
368
+ "'typecolname' "
369
+ "argument; this is available on "
370
+ "ConcreteBase as '_concrete_discriminator_name'"
371
+ % (typecolname, c)
372
+ )
373
+ colnames.add(c.key)
374
+ m[c.key] = c
375
+ types[c.key] = c.type
376
+ colnamemaps[table] = m
377
+
378
+ def col(name, table):
379
+ try:
380
+ return colnamemaps[table][name]
381
+ except KeyError:
382
+ if cast_nulls:
383
+ return sql.cast(sql.null(), types[name]).label(name)
384
+ else:
385
+ return sql.type_coerce(sql.null(), types[name]).label(name)
386
+
387
+ result = []
388
+ for type_, table in table_map.items():
389
+ if typecolname is not None:
390
+ result.append(
391
+ sql.select(
392
+ *(
393
+ [col(name, table) for name in colnames]
394
+ + [
395
+ sql.literal_column(
396
+ sql_util._quote_ddl_expr(type_)
397
+ ).label(typecolname)
398
+ ]
399
+ )
400
+ ).select_from(table)
401
+ )
402
+ else:
403
+ result.append(
404
+ sql.select(
405
+ *[col(name, table) for name in colnames]
406
+ ).select_from(table)
407
+ )
408
+ return sql.union_all(*result).alias(aliasname)
409
+
410
+
411
+ def identity_key(
412
+ class_: Optional[Type[_T]] = None,
413
+ ident: Union[Any, Tuple[Any, ...]] = None,
414
+ *,
415
+ instance: Optional[_T] = None,
416
+ row: Optional[Union[Row[Unpack[TupleAny]], RowMapping]] = None,
417
+ identity_token: Optional[Any] = None,
418
+ ) -> _IdentityKeyType[_T]:
419
+ r"""Generate "identity key" tuples, as are used as keys in the
420
+ :attr:`.Session.identity_map` dictionary.
421
+
422
+ This function has several call styles:
423
+
424
+ * ``identity_key(class, ident, identity_token=token)``
425
+
426
+ This form receives a mapped class and a primary key scalar or
427
+ tuple as an argument.
428
+
429
+ E.g.::
430
+
431
+ >>> identity_key(MyClass, (1, 2))
432
+ (<class '__main__.MyClass'>, (1, 2), None)
433
+
434
+ :param class: mapped class (must be a positional argument)
435
+ :param ident: primary key, may be a scalar or tuple argument.
436
+ :param identity_token: optional identity token
437
+
438
+ * ``identity_key(instance=instance)``
439
+
440
+ This form will produce the identity key for a given instance. The
441
+ instance need not be persistent, only that its primary key attributes
442
+ are populated (else the key will contain ``None`` for those missing
443
+ values).
444
+
445
+ E.g.::
446
+
447
+ >>> instance = MyClass(1, 2)
448
+ >>> identity_key(instance=instance)
449
+ (<class '__main__.MyClass'>, (1, 2), None)
450
+
451
+ In this form, the given instance is ultimately run though
452
+ :meth:`_orm.Mapper.identity_key_from_instance`, which will have the
453
+ effect of performing a database check for the corresponding row
454
+ if the object is expired.
455
+
456
+ :param instance: object instance (must be given as a keyword arg)
457
+
458
+ * ``identity_key(class, row=row, identity_token=token)``
459
+
460
+ This form is similar to the class/tuple form, except is passed a
461
+ database result row as a :class:`.Row` or :class:`.RowMapping` object.
462
+
463
+ E.g.::
464
+
465
+ >>> row = engine.execute(text("select * from table where a=1 and b=2")).first()
466
+ >>> identity_key(MyClass, row=row)
467
+ (<class '__main__.MyClass'>, (1, 2), None)
468
+
469
+ :param class: mapped class (must be a positional argument)
470
+ :param row: :class:`.Row` row returned by a :class:`_engine.CursorResult`
471
+ (must be given as a keyword arg)
472
+ :param identity_token: optional identity token
473
+
474
+ """ # noqa: E501
475
+ if class_ is not None:
476
+ mapper = class_mapper(class_)
477
+ if row is None:
478
+ if ident is None:
479
+ raise sa_exc.ArgumentError("ident or row is required")
480
+ return mapper.identity_key_from_primary_key(
481
+ tuple(util.to_list(ident)), identity_token=identity_token
482
+ )
483
+ else:
484
+ return mapper.identity_key_from_row(
485
+ row, identity_token=identity_token
486
+ )
487
+ elif instance is not None:
488
+ mapper = object_mapper(instance)
489
+ return mapper.identity_key_from_instance(instance)
490
+ else:
491
+ raise sa_exc.ArgumentError("class or instance is required")
492
+
493
+
494
+ class _TraceAdaptRole(enum.Enum):
495
+ """Enumeration of all the use cases for ORMAdapter.
496
+
497
+ ORMAdapter remains one of the most complicated aspects of the ORM, as it is
498
+ used for in-place adaption of column expressions to be applied to a SELECT,
499
+ replacing :class:`.Table` and other objects that are mapped to classes with
500
+ aliases of those tables in the case of joined eager loading, or in the case
501
+ of polymorphic loading as used with concrete mappings or other custom "with
502
+ polymorphic" parameters, with whole user-defined subqueries. The
503
+ enumerations provide an overview of all the use cases used by ORMAdapter, a
504
+ layer of formality as to the introduction of new ORMAdapter use cases (of
505
+ which none are anticipated), as well as a means to trace the origins of a
506
+ particular ORMAdapter within runtime debugging.
507
+
508
+ SQLAlchemy 2.0 has greatly scaled back ORM features which relied heavily on
509
+ open-ended statement adaption, including the ``Query.with_polymorphic()``
510
+ method and the ``Query.select_from_entity()`` methods, favoring
511
+ user-explicit aliasing schemes using the ``aliased()`` and
512
+ ``with_polymorphic()`` standalone constructs; these still use adaption,
513
+ however the adaption is applied in a narrower scope.
514
+
515
+ """
516
+
517
+ # aliased() use that is used to adapt individual attributes at query
518
+ # construction time
519
+ ALIASED_INSP = enum.auto()
520
+
521
+ # joinedload cases; typically adapt an ON clause of a relationship
522
+ # join
523
+ JOINEDLOAD_USER_DEFINED_ALIAS = enum.auto()
524
+ JOINEDLOAD_PATH_WITH_POLYMORPHIC = enum.auto()
525
+ JOINEDLOAD_MEMOIZED_ADAPTER = enum.auto()
526
+
527
+ # polymorphic cases - these are complex ones that replace FROM
528
+ # clauses, replacing tables with subqueries
529
+ MAPPER_POLYMORPHIC_ADAPTER = enum.auto()
530
+ WITH_POLYMORPHIC_ADAPTER = enum.auto()
531
+ WITH_POLYMORPHIC_ADAPTER_RIGHT_JOIN = enum.auto()
532
+ DEPRECATED_JOIN_ADAPT_RIGHT_SIDE = enum.auto()
533
+
534
+ # the from_statement() case, used only to adapt individual attributes
535
+ # from a given statement to local ORM attributes at result fetching
536
+ # time. assigned to ORMCompileState._from_obj_alias
537
+ ADAPT_FROM_STATEMENT = enum.auto()
538
+
539
+ # the joinedload for queries that have LIMIT/OFFSET/DISTINCT case;
540
+ # the query is placed inside of a subquery with the LIMIT/OFFSET/etc.,
541
+ # joinedloads are then placed on the outside.
542
+ # assigned to ORMCompileState.compound_eager_adapter
543
+ COMPOUND_EAGER_STATEMENT = enum.auto()
544
+
545
+ # the legacy Query._set_select_from() case.
546
+ # this is needed for Query's set operations (i.e. UNION, etc. )
547
+ # as well as "legacy from_self()", which while removed from 2.0 as
548
+ # public API, is used for the Query.count() method. this one
549
+ # still does full statement traversal
550
+ # assigned to ORMCompileState._from_obj_alias
551
+ LEGACY_SELECT_FROM_ALIAS = enum.auto()
552
+
553
+
554
+ class ORMStatementAdapter(sql_util.ColumnAdapter):
555
+ """ColumnAdapter which includes a role attribute."""
556
+
557
+ __slots__ = ("role",)
558
+
559
+ def __init__(
560
+ self,
561
+ role: _TraceAdaptRole,
562
+ selectable: Selectable,
563
+ *,
564
+ equivalents: Optional[_EquivalentColumnMap] = None,
565
+ adapt_required: bool = False,
566
+ allow_label_resolve: bool = True,
567
+ anonymize_labels: bool = False,
568
+ adapt_on_names: bool = False,
569
+ adapt_from_selectables: Optional[AbstractSet[FromClause]] = None,
570
+ ):
571
+ self.role = role
572
+ super().__init__(
573
+ selectable,
574
+ equivalents=equivalents,
575
+ adapt_required=adapt_required,
576
+ allow_label_resolve=allow_label_resolve,
577
+ anonymize_labels=anonymize_labels,
578
+ adapt_on_names=adapt_on_names,
579
+ adapt_from_selectables=adapt_from_selectables,
580
+ )
581
+
582
+
583
+ class ORMAdapter(sql_util.ColumnAdapter):
584
+ """ColumnAdapter subclass which excludes adaptation of entities from
585
+ non-matching mappers.
586
+
587
+ """
588
+
589
+ __slots__ = ("role", "mapper", "is_aliased_class", "aliased_insp")
590
+
591
+ is_aliased_class: bool
592
+ aliased_insp: Optional[AliasedInsp[Any]]
593
+
594
+ def __init__(
595
+ self,
596
+ role: _TraceAdaptRole,
597
+ entity: _InternalEntityType[Any],
598
+ *,
599
+ equivalents: Optional[_EquivalentColumnMap] = None,
600
+ adapt_required: bool = False,
601
+ allow_label_resolve: bool = True,
602
+ anonymize_labels: bool = False,
603
+ selectable: Optional[Selectable] = None,
604
+ limit_on_entity: bool = True,
605
+ adapt_on_names: bool = False,
606
+ adapt_from_selectables: Optional[AbstractSet[FromClause]] = None,
607
+ ):
608
+ self.role = role
609
+ self.mapper = entity.mapper
610
+ if selectable is None:
611
+ selectable = entity.selectable
612
+ if insp_is_aliased_class(entity):
613
+ self.is_aliased_class = True
614
+ self.aliased_insp = entity
615
+ else:
616
+ self.is_aliased_class = False
617
+ self.aliased_insp = None
618
+
619
+ super().__init__(
620
+ selectable,
621
+ equivalents,
622
+ adapt_required=adapt_required,
623
+ allow_label_resolve=allow_label_resolve,
624
+ anonymize_labels=anonymize_labels,
625
+ include_fn=self._include_fn if limit_on_entity else None,
626
+ adapt_on_names=adapt_on_names,
627
+ adapt_from_selectables=adapt_from_selectables,
628
+ )
629
+
630
+ def _include_fn(self, elem):
631
+ entity = elem._annotations.get("parentmapper", None)
632
+
633
+ return not entity or entity.isa(self.mapper) or self.mapper.isa(entity)
634
+
635
+
636
+ class AliasedClass(
637
+ inspection.Inspectable["AliasedInsp[_O]"], ORMColumnsClauseRole[_O]
638
+ ):
639
+ r"""Represents an "aliased" form of a mapped class for usage with Query.
640
+
641
+ The ORM equivalent of a :func:`~sqlalchemy.sql.expression.alias`
642
+ construct, this object mimics the mapped class using a
643
+ ``__getattr__`` scheme and maintains a reference to a
644
+ real :class:`~sqlalchemy.sql.expression.Alias` object.
645
+
646
+ A primary purpose of :class:`.AliasedClass` is to serve as an alternate
647
+ within a SQL statement generated by the ORM, such that an existing
648
+ mapped entity can be used in multiple contexts. A simple example::
649
+
650
+ # find all pairs of users with the same name
651
+ user_alias = aliased(User)
652
+ session.query(User, user_alias).join(
653
+ (user_alias, User.id > user_alias.id)
654
+ ).filter(User.name == user_alias.name)
655
+
656
+ :class:`.AliasedClass` is also capable of mapping an existing mapped
657
+ class to an entirely new selectable, provided this selectable is column-
658
+ compatible with the existing mapped selectable, and it can also be
659
+ configured in a mapping as the target of a :func:`_orm.relationship`.
660
+ See the links below for examples.
661
+
662
+ The :class:`.AliasedClass` object is constructed typically using the
663
+ :func:`_orm.aliased` function. It also is produced with additional
664
+ configuration when using the :func:`_orm.with_polymorphic` function.
665
+
666
+ The resulting object is an instance of :class:`.AliasedClass`.
667
+ This object implements an attribute scheme which produces the
668
+ same attribute and method interface as the original mapped
669
+ class, allowing :class:`.AliasedClass` to be compatible
670
+ with any attribute technique which works on the original class,
671
+ including hybrid attributes (see :ref:`hybrids_toplevel`).
672
+
673
+ The :class:`.AliasedClass` can be inspected for its underlying
674
+ :class:`_orm.Mapper`, aliased selectable, and other information
675
+ using :func:`_sa.inspect`::
676
+
677
+ from sqlalchemy import inspect
678
+
679
+ my_alias = aliased(MyClass)
680
+ insp = inspect(my_alias)
681
+
682
+ The resulting inspection object is an instance of :class:`.AliasedInsp`.
683
+
684
+
685
+ .. seealso::
686
+
687
+ :func:`.aliased`
688
+
689
+ :func:`.with_polymorphic`
690
+
691
+ :ref:`relationship_aliased_class`
692
+
693
+ :ref:`relationship_to_window_function`
694
+
695
+
696
+ """
697
+
698
+ __name__: str
699
+
700
+ def __init__(
701
+ self,
702
+ mapped_class_or_ac: _EntityType[_O],
703
+ alias: Optional[FromClause] = None,
704
+ name: Optional[str] = None,
705
+ flat: bool = False,
706
+ adapt_on_names: bool = False,
707
+ with_polymorphic_mappers: Optional[Sequence[Mapper[Any]]] = None,
708
+ with_polymorphic_discriminator: Optional[ColumnElement[Any]] = None,
709
+ base_alias: Optional[AliasedInsp[Any]] = None,
710
+ use_mapper_path: bool = False,
711
+ represents_outer_join: bool = False,
712
+ ):
713
+ insp = cast(
714
+ "_InternalEntityType[_O]", inspection.inspect(mapped_class_or_ac)
715
+ )
716
+ mapper = insp.mapper
717
+
718
+ nest_adapters = False
719
+
720
+ if alias is None:
721
+ if insp.is_aliased_class and insp.selectable._is_subquery:
722
+ alias = insp.selectable.alias()
723
+ else:
724
+ alias = (
725
+ mapper._with_polymorphic_selectable._anonymous_fromclause(
726
+ name=name,
727
+ flat=flat,
728
+ )
729
+ )
730
+ elif insp.is_aliased_class:
731
+ nest_adapters = True
732
+
733
+ assert alias is not None
734
+ self._aliased_insp = AliasedInsp(
735
+ self,
736
+ insp,
737
+ alias,
738
+ name,
739
+ (
740
+ with_polymorphic_mappers
741
+ if with_polymorphic_mappers
742
+ else mapper.with_polymorphic_mappers
743
+ ),
744
+ (
745
+ with_polymorphic_discriminator
746
+ if with_polymorphic_discriminator is not None
747
+ else mapper.polymorphic_on
748
+ ),
749
+ base_alias,
750
+ use_mapper_path,
751
+ adapt_on_names,
752
+ represents_outer_join,
753
+ nest_adapters,
754
+ )
755
+
756
+ self.__name__ = f"aliased({mapper.class_.__name__})"
757
+
758
+ @classmethod
759
+ def _reconstitute_from_aliased_insp(
760
+ cls, aliased_insp: AliasedInsp[_O]
761
+ ) -> AliasedClass[_O]:
762
+ obj = cls.__new__(cls)
763
+ obj.__name__ = f"aliased({aliased_insp.mapper.class_.__name__})"
764
+ obj._aliased_insp = aliased_insp
765
+
766
+ if aliased_insp._is_with_polymorphic:
767
+ for sub_aliased_insp in aliased_insp._with_polymorphic_entities:
768
+ if sub_aliased_insp is not aliased_insp:
769
+ ent = AliasedClass._reconstitute_from_aliased_insp(
770
+ sub_aliased_insp
771
+ )
772
+ setattr(obj, sub_aliased_insp.class_.__name__, ent)
773
+
774
+ return obj
775
+
776
+ def __getattr__(self, key: str) -> Any:
777
+ try:
778
+ _aliased_insp = self.__dict__["_aliased_insp"]
779
+ except KeyError:
780
+ raise AttributeError()
781
+ else:
782
+ target = _aliased_insp._target
783
+ # maintain all getattr mechanics
784
+ attr = getattr(target, key)
785
+
786
+ # attribute is a method, that will be invoked against a
787
+ # "self"; so just return a new method with the same function and
788
+ # new self
789
+ if hasattr(attr, "__call__") and hasattr(attr, "__self__"):
790
+ return types.MethodType(attr.__func__, self)
791
+
792
+ # attribute is a descriptor, that will be invoked against a
793
+ # "self"; so invoke the descriptor against this self
794
+ if hasattr(attr, "__get__"):
795
+ attr = attr.__get__(None, self)
796
+
797
+ # attributes within the QueryableAttribute system will want this
798
+ # to be invoked so the object can be adapted
799
+ if hasattr(attr, "adapt_to_entity"):
800
+ attr = attr.adapt_to_entity(_aliased_insp)
801
+ setattr(self, key, attr)
802
+
803
+ return attr
804
+
805
+ def _get_from_serialized(
806
+ self, key: str, mapped_class: _O, aliased_insp: AliasedInsp[_O]
807
+ ) -> Any:
808
+ # this method is only used in terms of the
809
+ # sqlalchemy.ext.serializer extension
810
+ attr = getattr(mapped_class, key)
811
+ if hasattr(attr, "__call__") and hasattr(attr, "__self__"):
812
+ return types.MethodType(attr.__func__, self)
813
+
814
+ # attribute is a descriptor, that will be invoked against a
815
+ # "self"; so invoke the descriptor against this self
816
+ if hasattr(attr, "__get__"):
817
+ attr = attr.__get__(None, self)
818
+
819
+ # attributes within the QueryableAttribute system will want this
820
+ # to be invoked so the object can be adapted
821
+ if hasattr(attr, "adapt_to_entity"):
822
+ aliased_insp._weak_entity = weakref.ref(self)
823
+ attr = attr.adapt_to_entity(aliased_insp)
824
+ setattr(self, key, attr)
825
+
826
+ return attr
827
+
828
+ def __repr__(self) -> str:
829
+ return "<AliasedClass at 0x%x; %s>" % (
830
+ id(self),
831
+ self._aliased_insp._target.__name__,
832
+ )
833
+
834
+ def __str__(self) -> str:
835
+ return str(self._aliased_insp)
836
+
837
+
838
+ @inspection._self_inspects
839
+ class AliasedInsp(
840
+ ORMEntityColumnsClauseRole[_O],
841
+ ORMFromClauseRole,
842
+ HasCacheKey,
843
+ InspectionAttr,
844
+ MemoizedSlots,
845
+ inspection.Inspectable["AliasedInsp[_O]"],
846
+ Generic[_O],
847
+ ):
848
+ """Provide an inspection interface for an
849
+ :class:`.AliasedClass` object.
850
+
851
+ The :class:`.AliasedInsp` object is returned
852
+ given an :class:`.AliasedClass` using the
853
+ :func:`_sa.inspect` function::
854
+
855
+ from sqlalchemy import inspect
856
+ from sqlalchemy.orm import aliased
857
+
858
+ my_alias = aliased(MyMappedClass)
859
+ insp = inspect(my_alias)
860
+
861
+ Attributes on :class:`.AliasedInsp`
862
+ include:
863
+
864
+ * ``entity`` - the :class:`.AliasedClass` represented.
865
+ * ``mapper`` - the :class:`_orm.Mapper` mapping the underlying class.
866
+ * ``selectable`` - the :class:`_expression.Alias`
867
+ construct which ultimately
868
+ represents an aliased :class:`_schema.Table` or
869
+ :class:`_expression.Select`
870
+ construct.
871
+ * ``name`` - the name of the alias. Also is used as the attribute
872
+ name when returned in a result tuple from :class:`_query.Query`.
873
+ * ``with_polymorphic_mappers`` - collection of :class:`_orm.Mapper`
874
+ objects
875
+ indicating all those mappers expressed in the select construct
876
+ for the :class:`.AliasedClass`.
877
+ * ``polymorphic_on`` - an alternate column or SQL expression which
878
+ will be used as the "discriminator" for a polymorphic load.
879
+
880
+ .. seealso::
881
+
882
+ :ref:`inspection_toplevel`
883
+
884
+ """
885
+
886
+ __slots__ = (
887
+ "__weakref__",
888
+ "_weak_entity",
889
+ "mapper",
890
+ "selectable",
891
+ "name",
892
+ "_adapt_on_names",
893
+ "with_polymorphic_mappers",
894
+ "polymorphic_on",
895
+ "_use_mapper_path",
896
+ "_base_alias",
897
+ "represents_outer_join",
898
+ "persist_selectable",
899
+ "local_table",
900
+ "_is_with_polymorphic",
901
+ "_with_polymorphic_entities",
902
+ "_adapter",
903
+ "_target",
904
+ "__clause_element__",
905
+ "_memoized_values",
906
+ "_all_column_expressions",
907
+ "_nest_adapters",
908
+ )
909
+
910
+ _cache_key_traversal = [
911
+ ("name", visitors.ExtendedInternalTraversal.dp_string),
912
+ ("_adapt_on_names", visitors.ExtendedInternalTraversal.dp_boolean),
913
+ ("_use_mapper_path", visitors.ExtendedInternalTraversal.dp_boolean),
914
+ ("_target", visitors.ExtendedInternalTraversal.dp_inspectable),
915
+ ("selectable", visitors.ExtendedInternalTraversal.dp_clauseelement),
916
+ (
917
+ "with_polymorphic_mappers",
918
+ visitors.InternalTraversal.dp_has_cache_key_list,
919
+ ),
920
+ ("polymorphic_on", visitors.InternalTraversal.dp_clauseelement),
921
+ ]
922
+
923
+ mapper: Mapper[_O]
924
+ selectable: FromClause
925
+ _adapter: ORMAdapter
926
+ with_polymorphic_mappers: Sequence[Mapper[Any]]
927
+ _with_polymorphic_entities: Sequence[AliasedInsp[Any]]
928
+
929
+ _weak_entity: weakref.ref[AliasedClass[_O]]
930
+ """the AliasedClass that refers to this AliasedInsp"""
931
+
932
+ _target: Union[Type[_O], AliasedClass[_O]]
933
+ """the thing referenced by the AliasedClass/AliasedInsp.
934
+
935
+ In the vast majority of cases, this is the mapped class. However
936
+ it may also be another AliasedClass (alias of alias).
937
+
938
+ """
939
+
940
+ def __init__(
941
+ self,
942
+ entity: AliasedClass[_O],
943
+ inspected: _InternalEntityType[_O],
944
+ selectable: FromClause,
945
+ name: Optional[str],
946
+ with_polymorphic_mappers: Optional[Sequence[Mapper[Any]]],
947
+ polymorphic_on: Optional[ColumnElement[Any]],
948
+ _base_alias: Optional[AliasedInsp[Any]],
949
+ _use_mapper_path: bool,
950
+ adapt_on_names: bool,
951
+ represents_outer_join: bool,
952
+ nest_adapters: bool,
953
+ ):
954
+ mapped_class_or_ac = inspected.entity
955
+ mapper = inspected.mapper
956
+
957
+ self._weak_entity = weakref.ref(entity)
958
+ self.mapper = mapper
959
+ self.selectable = self.persist_selectable = self.local_table = (
960
+ selectable
961
+ )
962
+ self.name = name
963
+ self.polymorphic_on = polymorphic_on
964
+ self._base_alias = weakref.ref(_base_alias or self)
965
+ self._use_mapper_path = _use_mapper_path
966
+ self.represents_outer_join = represents_outer_join
967
+ self._nest_adapters = nest_adapters
968
+
969
+ if with_polymorphic_mappers:
970
+ self._is_with_polymorphic = True
971
+ self.with_polymorphic_mappers = with_polymorphic_mappers
972
+ self._with_polymorphic_entities = []
973
+ for poly in self.with_polymorphic_mappers:
974
+ if poly is not mapper:
975
+ ent = AliasedClass(
976
+ poly.class_,
977
+ selectable,
978
+ base_alias=self,
979
+ adapt_on_names=adapt_on_names,
980
+ use_mapper_path=_use_mapper_path,
981
+ )
982
+
983
+ setattr(self.entity, poly.class_.__name__, ent)
984
+ self._with_polymorphic_entities.append(ent._aliased_insp)
985
+
986
+ else:
987
+ self._is_with_polymorphic = False
988
+ self.with_polymorphic_mappers = [mapper]
989
+
990
+ self._adapter = ORMAdapter(
991
+ _TraceAdaptRole.ALIASED_INSP,
992
+ mapper,
993
+ selectable=selectable,
994
+ equivalents=mapper._equivalent_columns,
995
+ adapt_on_names=adapt_on_names,
996
+ anonymize_labels=True,
997
+ # make sure the adapter doesn't try to grab other tables that
998
+ # are not even the thing we are mapping, such as embedded
999
+ # selectables in subqueries or CTEs. See issue #6060
1000
+ adapt_from_selectables={
1001
+ m.selectable
1002
+ for m in self.with_polymorphic_mappers
1003
+ if not adapt_on_names
1004
+ },
1005
+ limit_on_entity=False,
1006
+ )
1007
+
1008
+ if nest_adapters:
1009
+ # supports "aliased class of aliased class" use case
1010
+ assert isinstance(inspected, AliasedInsp)
1011
+ self._adapter = inspected._adapter.wrap(self._adapter)
1012
+
1013
+ self._adapt_on_names = adapt_on_names
1014
+ self._target = mapped_class_or_ac
1015
+
1016
+ @property
1017
+ def _post_inspect(self): # type: ignore[override]
1018
+ self.mapper._check_configure()
1019
+
1020
+ @classmethod
1021
+ def _alias_factory(
1022
+ cls,
1023
+ element: Union[_EntityType[_O], FromClause],
1024
+ alias: Optional[FromClause] = None,
1025
+ name: Optional[str] = None,
1026
+ flat: bool = False,
1027
+ adapt_on_names: bool = False,
1028
+ ) -> Union[AliasedClass[_O], FromClause]:
1029
+ if isinstance(element, GenerativeSelect):
1030
+ return coercions.expect(roles.FromClauseRole, element, flat=flat)
1031
+ elif isinstance(element, FromClause):
1032
+ if adapt_on_names:
1033
+ raise sa_exc.ArgumentError(
1034
+ "adapt_on_names only applies to ORM elements"
1035
+ )
1036
+ if name:
1037
+ return element.alias(name=name, flat=flat)
1038
+ else:
1039
+ # see selectable.py->Alias._factory() for similar
1040
+ # mypy issue. Cannot get the overload to see this
1041
+ # in mypy (works fine in pyright)
1042
+ return coercions.expect( # type: ignore[no-any-return]
1043
+ roles.AnonymizedFromClauseRole, element, flat=flat
1044
+ )
1045
+ else:
1046
+ return AliasedClass(
1047
+ element,
1048
+ alias=alias,
1049
+ flat=flat,
1050
+ name=name,
1051
+ adapt_on_names=adapt_on_names,
1052
+ )
1053
+
1054
+ @classmethod
1055
+ def _with_polymorphic_factory(
1056
+ cls,
1057
+ base: Union[Type[_O], Mapper[_O]],
1058
+ classes: Union[Literal["*"], Iterable[_EntityType[Any]]],
1059
+ selectable: Union[Literal[False, None], FromClause] = False,
1060
+ flat: bool = False,
1061
+ polymorphic_on: Optional[ColumnElement[Any]] = None,
1062
+ aliased: bool = False,
1063
+ innerjoin: bool = False,
1064
+ adapt_on_names: bool = False,
1065
+ name: Optional[str] = None,
1066
+ _use_mapper_path: bool = False,
1067
+ ) -> AliasedClass[_O]:
1068
+ primary_mapper = _class_to_mapper(base)
1069
+
1070
+ if selectable not in (None, False) and flat:
1071
+ raise sa_exc.ArgumentError(
1072
+ "the 'flat' and 'selectable' arguments cannot be passed "
1073
+ "simultaneously to with_polymorphic()"
1074
+ )
1075
+
1076
+ mappers, selectable = primary_mapper._with_polymorphic_args(
1077
+ classes, selectable, innerjoin=innerjoin
1078
+ )
1079
+ if aliased or flat:
1080
+ assert selectable is not None
1081
+ selectable = selectable._anonymous_fromclause(flat=flat)
1082
+
1083
+ return AliasedClass(
1084
+ base,
1085
+ selectable,
1086
+ name=name,
1087
+ with_polymorphic_mappers=mappers,
1088
+ adapt_on_names=adapt_on_names,
1089
+ with_polymorphic_discriminator=polymorphic_on,
1090
+ use_mapper_path=_use_mapper_path,
1091
+ represents_outer_join=not innerjoin,
1092
+ )
1093
+
1094
+ @property
1095
+ def entity(self) -> AliasedClass[_O]:
1096
+ # to eliminate reference cycles, the AliasedClass is held weakly.
1097
+ # this produces some situations where the AliasedClass gets lost,
1098
+ # particularly when one is created internally and only the AliasedInsp
1099
+ # is passed around.
1100
+ # to work around this case, we just generate a new one when we need
1101
+ # it, as it is a simple class with very little initial state on it.
1102
+ ent = self._weak_entity()
1103
+ if ent is None:
1104
+ ent = AliasedClass._reconstitute_from_aliased_insp(self)
1105
+ self._weak_entity = weakref.ref(ent)
1106
+ return ent
1107
+
1108
+ is_aliased_class = True
1109
+ "always returns True"
1110
+
1111
+ def _memoized_method___clause_element__(self) -> FromClause:
1112
+ return self.selectable._annotate(
1113
+ {
1114
+ "parentmapper": self.mapper,
1115
+ "parententity": self,
1116
+ "entity_namespace": self,
1117
+ }
1118
+ )._set_propagate_attrs(
1119
+ {"compile_state_plugin": "orm", "plugin_subject": self}
1120
+ )
1121
+
1122
+ @property
1123
+ def entity_namespace(self) -> AliasedClass[_O]:
1124
+ return self.entity
1125
+
1126
+ @property
1127
+ def class_(self) -> Type[_O]:
1128
+ """Return the mapped class ultimately represented by this
1129
+ :class:`.AliasedInsp`."""
1130
+ return self.mapper.class_
1131
+
1132
+ @property
1133
+ def _path_registry(self) -> _AbstractEntityRegistry:
1134
+ if self._use_mapper_path:
1135
+ return self.mapper._path_registry
1136
+ else:
1137
+ return PathRegistry.per_mapper(self)
1138
+
1139
+ def __getstate__(self) -> Dict[str, Any]:
1140
+ return {
1141
+ "entity": self.entity,
1142
+ "mapper": self.mapper,
1143
+ "alias": self.selectable,
1144
+ "name": self.name,
1145
+ "adapt_on_names": self._adapt_on_names,
1146
+ "with_polymorphic_mappers": self.with_polymorphic_mappers,
1147
+ "with_polymorphic_discriminator": self.polymorphic_on,
1148
+ "base_alias": self._base_alias(),
1149
+ "use_mapper_path": self._use_mapper_path,
1150
+ "represents_outer_join": self.represents_outer_join,
1151
+ "nest_adapters": self._nest_adapters,
1152
+ }
1153
+
1154
+ def __setstate__(self, state: Dict[str, Any]) -> None:
1155
+ self.__init__( # type: ignore[misc]
1156
+ state["entity"],
1157
+ state["mapper"],
1158
+ state["alias"],
1159
+ state["name"],
1160
+ state["with_polymorphic_mappers"],
1161
+ state["with_polymorphic_discriminator"],
1162
+ state["base_alias"],
1163
+ state["use_mapper_path"],
1164
+ state["adapt_on_names"],
1165
+ state["represents_outer_join"],
1166
+ state["nest_adapters"],
1167
+ )
1168
+
1169
+ def _merge_with(self, other: AliasedInsp[_O]) -> AliasedInsp[_O]:
1170
+ # assert self._is_with_polymorphic
1171
+ # assert other._is_with_polymorphic
1172
+
1173
+ primary_mapper = other.mapper
1174
+
1175
+ assert self.mapper is primary_mapper
1176
+
1177
+ our_classes = util.to_set(
1178
+ mp.class_ for mp in self.with_polymorphic_mappers
1179
+ )
1180
+ new_classes = {mp.class_ for mp in other.with_polymorphic_mappers}
1181
+ if our_classes == new_classes:
1182
+ return other
1183
+ else:
1184
+ classes = our_classes.union(new_classes)
1185
+
1186
+ mappers, selectable = primary_mapper._with_polymorphic_args(
1187
+ classes, None, innerjoin=not other.represents_outer_join
1188
+ )
1189
+ selectable = selectable._anonymous_fromclause(flat=True)
1190
+ return AliasedClass(
1191
+ primary_mapper,
1192
+ selectable,
1193
+ with_polymorphic_mappers=mappers,
1194
+ with_polymorphic_discriminator=other.polymorphic_on,
1195
+ use_mapper_path=other._use_mapper_path,
1196
+ represents_outer_join=other.represents_outer_join,
1197
+ )._aliased_insp
1198
+
1199
+ def _adapt_element(
1200
+ self, expr: _ORMCOLEXPR, key: Optional[str] = None
1201
+ ) -> _ORMCOLEXPR:
1202
+ assert isinstance(expr, ColumnElement)
1203
+ d: Dict[str, Any] = {
1204
+ "parententity": self,
1205
+ "parentmapper": self.mapper,
1206
+ }
1207
+ if key:
1208
+ d["proxy_key"] = key
1209
+
1210
+ # userspace adapt of an attribute from AliasedClass; validate that
1211
+ # it actually was present
1212
+ adapted = self._adapter.adapt_check_present(expr)
1213
+ if adapted is None:
1214
+ adapted = expr
1215
+ if self._adapter.adapt_on_names:
1216
+ util.warn_limited(
1217
+ "Did not locate an expression in selectable for "
1218
+ "attribute %r; ensure name is correct in expression",
1219
+ (key,),
1220
+ )
1221
+ else:
1222
+ util.warn_limited(
1223
+ "Did not locate an expression in selectable for "
1224
+ "attribute %r; to match by name, use the "
1225
+ "adapt_on_names parameter",
1226
+ (key,),
1227
+ )
1228
+
1229
+ return adapted._annotate(d)._set_propagate_attrs(
1230
+ {"compile_state_plugin": "orm", "plugin_subject": self}
1231
+ )
1232
+
1233
+ if TYPE_CHECKING:
1234
+ # establish compatibility with the _ORMAdapterProto protocol,
1235
+ # which in turn is compatible with _CoreAdapterProto.
1236
+
1237
+ def _orm_adapt_element(
1238
+ self,
1239
+ obj: _CE,
1240
+ key: Optional[str] = None,
1241
+ ) -> _CE: ...
1242
+
1243
+ else:
1244
+ _orm_adapt_element = _adapt_element
1245
+
1246
+ def _entity_for_mapper(self, mapper):
1247
+ self_poly = self.with_polymorphic_mappers
1248
+ if mapper in self_poly:
1249
+ if mapper is self.mapper:
1250
+ return self
1251
+ else:
1252
+ return getattr(
1253
+ self.entity, mapper.class_.__name__
1254
+ )._aliased_insp
1255
+ elif mapper.isa(self.mapper):
1256
+ return self
1257
+ else:
1258
+ assert False, "mapper %s doesn't correspond to %s" % (mapper, self)
1259
+
1260
+ def _memoized_attr__get_clause(self):
1261
+ onclause, replacemap = self.mapper._get_clause
1262
+ return (
1263
+ self._adapter.traverse(onclause),
1264
+ {
1265
+ self._adapter.traverse(col): param
1266
+ for col, param in replacemap.items()
1267
+ },
1268
+ )
1269
+
1270
+ def _memoized_attr__memoized_values(self):
1271
+ return {}
1272
+
1273
+ def _memoized_attr__all_column_expressions(self):
1274
+ if self._is_with_polymorphic:
1275
+ cols_plus_keys = self.mapper._columns_plus_keys(
1276
+ [ent.mapper for ent in self._with_polymorphic_entities]
1277
+ )
1278
+ else:
1279
+ cols_plus_keys = self.mapper._columns_plus_keys()
1280
+
1281
+ cols_plus_keys = [
1282
+ (key, self._adapt_element(col)) for key, col in cols_plus_keys
1283
+ ]
1284
+
1285
+ return WriteableColumnCollection(cols_plus_keys)
1286
+
1287
+ def _memo(self, key, callable_, *args, **kw):
1288
+ if key in self._memoized_values:
1289
+ return self._memoized_values[key]
1290
+ else:
1291
+ self._memoized_values[key] = value = callable_(*args, **kw)
1292
+ return value
1293
+
1294
+ def __repr__(self):
1295
+ if self.with_polymorphic_mappers:
1296
+ with_poly = "(%s)" % ", ".join(
1297
+ mp.class_.__name__ for mp in self.with_polymorphic_mappers
1298
+ )
1299
+ else:
1300
+ with_poly = ""
1301
+ return "<AliasedInsp at 0x%x; %s%s>" % (
1302
+ id(self),
1303
+ self.class_.__name__,
1304
+ with_poly,
1305
+ )
1306
+
1307
+ def __str__(self):
1308
+ return self.path_string()
1309
+
1310
+ def path_string(self) -> str:
1311
+ """Return a user-facing name for this :class:`.AliasedInsp`,
1312
+ for use in a :class:`_orm.PathRegistry` string representation.
1313
+
1314
+ """
1315
+ if self._is_with_polymorphic:
1316
+ return "with_polymorphic(%s, [%s])" % (
1317
+ self._target.__name__,
1318
+ ", ".join(
1319
+ mp.class_.__name__
1320
+ for mp in self.with_polymorphic_mappers
1321
+ if mp is not self.mapper
1322
+ ),
1323
+ )
1324
+ else:
1325
+ return "aliased(%s)" % (self._target.__name__,)
1326
+
1327
+
1328
+ class _WrapUserEntity:
1329
+ """A wrapper used within the loader_criteria lambda caller so that
1330
+ we can bypass declared_attr descriptors on unmapped mixins, which
1331
+ normally emit a warning for such use.
1332
+
1333
+ might also be useful for other per-lambda instrumentations should
1334
+ the need arise.
1335
+
1336
+ """
1337
+
1338
+ __slots__ = ("subject",)
1339
+
1340
+ def __init__(self, subject):
1341
+ self.subject = subject
1342
+
1343
+ @util.preload_module("sqlalchemy.orm.decl_api")
1344
+ def __getattribute__(self, name):
1345
+ decl_api = util.preloaded.orm.decl_api
1346
+
1347
+ subject = object.__getattribute__(self, "subject")
1348
+ if name in subject.__dict__ and isinstance(
1349
+ subject.__dict__[name], decl_api.declared_attr
1350
+ ):
1351
+ return subject.__dict__[name].fget(subject)
1352
+ else:
1353
+ return getattr(subject, name)
1354
+
1355
+
1356
+ class LoaderCriteriaOption(CriteriaOption):
1357
+ """Add additional WHERE criteria to the load for all occurrences of
1358
+ a particular entity.
1359
+
1360
+ :class:`_orm.LoaderCriteriaOption` is invoked using the
1361
+ :func:`_orm.with_loader_criteria` function; see that function for
1362
+ details.
1363
+
1364
+ .. versionadded:: 1.4
1365
+
1366
+ """
1367
+
1368
+ __slots__ = (
1369
+ "root_entity",
1370
+ "entity",
1371
+ "deferred_where_criteria",
1372
+ "where_criteria",
1373
+ "_where_crit_orig",
1374
+ "include_aliases",
1375
+ "propagate_to_loaders",
1376
+ )
1377
+
1378
+ _traverse_internals = [
1379
+ ("root_entity", visitors.ExtendedInternalTraversal.dp_plain_obj),
1380
+ ("entity", visitors.ExtendedInternalTraversal.dp_has_cache_key),
1381
+ ("where_criteria", visitors.InternalTraversal.dp_clauseelement),
1382
+ ("include_aliases", visitors.InternalTraversal.dp_boolean),
1383
+ ("propagate_to_loaders", visitors.InternalTraversal.dp_boolean),
1384
+ ]
1385
+
1386
+ root_entity: Optional[Type[Any]]
1387
+ entity: Optional[_InternalEntityType[Any]]
1388
+ where_criteria: Union[ColumnElement[bool], lambdas.DeferredLambdaElement]
1389
+ deferred_where_criteria: bool
1390
+ include_aliases: bool
1391
+ propagate_to_loaders: bool
1392
+
1393
+ _where_crit_orig: Any
1394
+
1395
+ def __init__(
1396
+ self,
1397
+ entity_or_base: _EntityType[Any],
1398
+ where_criteria: Union[
1399
+ _ColumnExpressionArgument[bool],
1400
+ Callable[[Any], _ColumnExpressionArgument[bool]],
1401
+ ],
1402
+ loader_only: bool = False,
1403
+ include_aliases: bool = False,
1404
+ propagate_to_loaders: bool = True,
1405
+ track_closure_variables: bool = True,
1406
+ ):
1407
+ entity = cast(
1408
+ "_InternalEntityType[Any]",
1409
+ inspection.inspect(entity_or_base, False),
1410
+ )
1411
+ if entity is None:
1412
+ self.root_entity = cast("Type[Any]", entity_or_base)
1413
+ self.entity = None
1414
+ else:
1415
+ self.root_entity = None
1416
+ self.entity = entity
1417
+
1418
+ self._where_crit_orig = where_criteria
1419
+ if callable(where_criteria):
1420
+ if self.root_entity is not None:
1421
+ wrap_entity = self.root_entity
1422
+ else:
1423
+ assert entity is not None
1424
+ wrap_entity = entity.entity
1425
+
1426
+ self.deferred_where_criteria = True
1427
+ self.where_criteria = lambdas.DeferredLambdaElement(
1428
+ where_criteria,
1429
+ roles.WhereHavingRole,
1430
+ lambda_args=(_WrapUserEntity(wrap_entity),),
1431
+ opts=lambdas.LambdaOptions(
1432
+ track_closure_variables=track_closure_variables
1433
+ ),
1434
+ )
1435
+ else:
1436
+ self.deferred_where_criteria = False
1437
+ self.where_criteria = coercions.expect(
1438
+ roles.WhereHavingRole, where_criteria
1439
+ )
1440
+
1441
+ self.include_aliases = include_aliases
1442
+ self.propagate_to_loaders = propagate_to_loaders
1443
+
1444
+ @classmethod
1445
+ def _unreduce(
1446
+ cls, entity, where_criteria, include_aliases, propagate_to_loaders
1447
+ ):
1448
+ return LoaderCriteriaOption(
1449
+ entity,
1450
+ where_criteria,
1451
+ include_aliases=include_aliases,
1452
+ propagate_to_loaders=propagate_to_loaders,
1453
+ )
1454
+
1455
+ def __reduce__(self):
1456
+ return (
1457
+ LoaderCriteriaOption._unreduce,
1458
+ (
1459
+ self.entity.class_ if self.entity else self.root_entity,
1460
+ self._where_crit_orig,
1461
+ self.include_aliases,
1462
+ self.propagate_to_loaders,
1463
+ ),
1464
+ )
1465
+
1466
+ def _all_mappers(self) -> Iterator[Mapper[Any]]:
1467
+ if self.entity:
1468
+ yield from self.entity.mapper.self_and_descendants
1469
+ else:
1470
+ assert self.root_entity
1471
+ stack = list(self.root_entity.__subclasses__())
1472
+ while stack:
1473
+ subclass = stack.pop(0)
1474
+ ent = cast(
1475
+ "_InternalEntityType[Any]",
1476
+ inspection.inspect(subclass, raiseerr=False),
1477
+ )
1478
+ if ent:
1479
+ yield from ent.mapper.self_and_descendants
1480
+ else:
1481
+ stack.extend(subclass.__subclasses__())
1482
+
1483
+ def _should_include(self, compile_state: _ORMCompileState) -> bool:
1484
+ if (
1485
+ compile_state.select_statement._annotations.get(
1486
+ "for_loader_criteria", None
1487
+ )
1488
+ is self
1489
+ ):
1490
+ return False
1491
+ return True
1492
+
1493
+ def _resolve_where_criteria(
1494
+ self, ext_info: _InternalEntityType[Any]
1495
+ ) -> ColumnElement[bool]:
1496
+ if self.deferred_where_criteria:
1497
+ crit = cast(
1498
+ "ColumnElement[bool]",
1499
+ self.where_criteria._resolve_with_args(ext_info.entity),
1500
+ )
1501
+ else:
1502
+ crit = self.where_criteria # type: ignore[assignment]
1503
+ assert isinstance(crit, ColumnElement)
1504
+ return sql_util._deep_annotate(
1505
+ crit,
1506
+ {"for_loader_criteria": self},
1507
+ detect_subquery_cols=True,
1508
+ ind_cols_on_fromclause=True,
1509
+ )
1510
+
1511
+ def process_compile_state_replaced_entities(
1512
+ self,
1513
+ compile_state: _ORMCompileState,
1514
+ mapper_entities: Iterable[_MapperEntity],
1515
+ ) -> None:
1516
+ self.process_compile_state(compile_state)
1517
+
1518
+ def process_compile_state(self, compile_state: _ORMCompileState) -> None:
1519
+ """Apply a modification to a given :class:`.CompileState`."""
1520
+
1521
+ # if options to limit the criteria to immediate query only,
1522
+ # use compile_state.attributes instead
1523
+
1524
+ self.get_global_criteria(compile_state.global_attributes)
1525
+
1526
+ def get_global_criteria(self, attributes: Dict[Any, Any]) -> None:
1527
+ for mp in self._all_mappers():
1528
+ load_criteria = attributes.setdefault(
1529
+ ("additional_entity_criteria", mp), []
1530
+ )
1531
+
1532
+ load_criteria.append(self)
1533
+
1534
+
1535
+ inspection._inspects(AliasedClass)(lambda target: target._aliased_insp)
1536
+
1537
+
1538
+ @inspection._inspects(type)
1539
+ def _inspect_mc(
1540
+ class_: Type[_O],
1541
+ ) -> Optional[Mapper[_O]]:
1542
+ try:
1543
+ class_manager = opt_manager_of_class(class_)
1544
+ if class_manager is None or not class_manager.is_mapped:
1545
+ return None
1546
+ mapper = class_manager.mapper
1547
+ except orm_exc.NO_STATE:
1548
+ return None
1549
+ else:
1550
+ return mapper
1551
+
1552
+
1553
+ GenericAlias = type(List[Any])
1554
+
1555
+
1556
+ @inspection._inspects(GenericAlias)
1557
+ def _inspect_generic_alias(
1558
+ class_: Type[_O],
1559
+ ) -> Optional[Mapper[_O]]:
1560
+ origin = cast("Type[_O]", get_origin(class_))
1561
+ return _inspect_mc(origin)
1562
+
1563
+
1564
+ @inspection._self_inspects
1565
+ class Bundle(
1566
+ ORMColumnsClauseRole[_T],
1567
+ SupportsCloneAnnotations,
1568
+ MemoizedHasCacheKey,
1569
+ inspection.Inspectable["Bundle[_T]"],
1570
+ InspectionAttr,
1571
+ ):
1572
+ """A grouping of SQL expressions that are returned by a :class:`.Query`
1573
+ under one namespace.
1574
+
1575
+ The :class:`.Bundle` essentially allows nesting of the tuple-based
1576
+ results returned by a column-oriented :class:`_query.Query` object.
1577
+ It also
1578
+ is extensible via simple subclassing, where the primary capability
1579
+ to override is that of how the set of expressions should be returned,
1580
+ allowing post-processing as well as custom return types, without
1581
+ involving ORM identity-mapped classes.
1582
+
1583
+ .. seealso::
1584
+
1585
+ :ref:`bundles`
1586
+
1587
+ :class:`.DictBundle`
1588
+
1589
+ """
1590
+
1591
+ single_entity = False
1592
+ """If True, queries for a single Bundle will be returned as a single
1593
+ entity, rather than an element within a keyed tuple."""
1594
+
1595
+ is_clause_element = False
1596
+
1597
+ is_mapper = False
1598
+
1599
+ is_aliased_class = False
1600
+
1601
+ is_bundle = True
1602
+
1603
+ _propagate_attrs: _PropagateAttrsType = util.immutabledict()
1604
+
1605
+ proxy_set = util.EMPTY_SET
1606
+
1607
+ exprs: List[_ColumnsClauseElement]
1608
+
1609
+ def __init__(
1610
+ self, name: str, *exprs: _ColumnExpressionArgument[Any], **kw: Any
1611
+ ) -> None:
1612
+ r"""Construct a new :class:`.Bundle`.
1613
+
1614
+ e.g.::
1615
+
1616
+ bn = Bundle("mybundle", MyClass.x, MyClass.y)
1617
+
1618
+ for row in session.query(bn).filter(bn.c.x == 5).filter(bn.c.y == 4):
1619
+ print(row.mybundle.x, row.mybundle.y)
1620
+
1621
+ :param name: name of the bundle.
1622
+ :param \*exprs: columns or SQL expressions comprising the bundle.
1623
+ :param single_entity=False: if True, rows for this :class:`.Bundle`
1624
+ can be returned as a "single entity" outside of any enclosing tuple
1625
+ in the same manner as a mapped entity.
1626
+
1627
+ """ # noqa: E501
1628
+ self.name = self._label = name
1629
+ coerced_exprs = [
1630
+ coercions.expect(
1631
+ roles.ColumnsClauseRole, expr, apply_propagate_attrs=self
1632
+ )
1633
+ for expr in exprs
1634
+ ]
1635
+ self.exprs = coerced_exprs
1636
+
1637
+ self.c = self.columns = WriteableColumnCollection(
1638
+ (getattr(col, "key", col._label), col)
1639
+ for col in [e._annotations.get("bundle", e) for e in coerced_exprs]
1640
+ ).as_readonly()
1641
+ self.single_entity = kw.pop("single_entity", self.single_entity)
1642
+
1643
+ def _gen_cache_key(
1644
+ self, anon_map: anon_map, bindparams: List[BindParameter[Any]]
1645
+ ) -> Tuple[Any, ...]:
1646
+ return (self.__class__, self.name, self.single_entity) + tuple(
1647
+ [expr._gen_cache_key(anon_map, bindparams) for expr in self.exprs]
1648
+ )
1649
+
1650
+ @property
1651
+ def mapper(self) -> Optional[Mapper[Any]]:
1652
+ mp: Optional[Mapper[Any]] = self.exprs[0]._annotations.get(
1653
+ "parentmapper", None
1654
+ )
1655
+ return mp
1656
+
1657
+ @property
1658
+ def entity(self) -> Optional[_InternalEntityType[Any]]:
1659
+ ie: Optional[_InternalEntityType[Any]] = self.exprs[
1660
+ 0
1661
+ ]._annotations.get("parententity", None)
1662
+ return ie
1663
+
1664
+ @property
1665
+ def entity_namespace(
1666
+ self,
1667
+ ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
1668
+ return self.c
1669
+
1670
+ columns: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]
1671
+
1672
+ """A namespace of SQL expressions referred to by this :class:`.Bundle`.
1673
+
1674
+ e.g.::
1675
+
1676
+ bn = Bundle("mybundle", MyClass.x, MyClass.y)
1677
+
1678
+ q = sess.query(bn).filter(bn.c.x == 5)
1679
+
1680
+ Nesting of bundles is also supported::
1681
+
1682
+ b1 = Bundle(
1683
+ "b1",
1684
+ Bundle("b2", MyClass.a, MyClass.b),
1685
+ Bundle("b3", MyClass.x, MyClass.y),
1686
+ )
1687
+
1688
+ q = sess.query(b1).filter(b1.c.b2.c.a == 5).filter(b1.c.b3.c.y == 9)
1689
+
1690
+ .. seealso::
1691
+
1692
+ :attr:`.Bundle.c`
1693
+
1694
+ """ # noqa: E501
1695
+
1696
+ c: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]
1697
+ """An alias for :attr:`.Bundle.columns`."""
1698
+
1699
+ def _clone(self, **kw):
1700
+ cloned = self.__class__.__new__(self.__class__)
1701
+ cloned.__dict__.update(self.__dict__)
1702
+ return cloned
1703
+
1704
+ def __clause_element__(self):
1705
+ # ensure existing entity_namespace remains
1706
+ annotations = {"bundle": self, "entity_namespace": self}
1707
+ annotations.update(self._annotations)
1708
+
1709
+ plugin_subject = self.exprs[0]._propagate_attrs.get(
1710
+ "plugin_subject", self.entity
1711
+ )
1712
+ return (
1713
+ expression.ClauseList(
1714
+ _literal_as_text_role=roles.ColumnsClauseRole,
1715
+ group=False,
1716
+ *[e._annotations.get("bundle", e) for e in self.exprs],
1717
+ )
1718
+ ._annotate(annotations)
1719
+ ._set_propagate_attrs(
1720
+ # the Bundle *must* use the orm plugin no matter what. the
1721
+ # subject can be None but it's much better if it's not.
1722
+ {
1723
+ "compile_state_plugin": "orm",
1724
+ "plugin_subject": plugin_subject,
1725
+ }
1726
+ )
1727
+ )
1728
+
1729
+ @property
1730
+ def clauses(self):
1731
+ return self.__clause_element__().clauses
1732
+
1733
+ def label(self, name):
1734
+ """Provide a copy of this :class:`.Bundle` passing a new label."""
1735
+
1736
+ cloned = self._clone()
1737
+ cloned.name = name
1738
+ return cloned
1739
+
1740
+ def create_row_processor(
1741
+ self,
1742
+ query: Select[Unpack[TupleAny]],
1743
+ procs: Sequence[Callable[[Row[Unpack[TupleAny]]], Any]],
1744
+ labels: Sequence[str],
1745
+ ) -> Callable[[Row[Unpack[TupleAny]]], Any]:
1746
+ """Produce the "row processing" function for this :class:`.Bundle`.
1747
+
1748
+ May be overridden by subclasses to provide custom behaviors when
1749
+ results are fetched. The method is passed the statement object and a
1750
+ set of "row processor" functions at query execution time; these
1751
+ processor functions when given a result row will return the individual
1752
+ attribute value, which can then be adapted into any kind of return data
1753
+ structure.
1754
+
1755
+ The example below illustrates replacing the usual :class:`.Row`
1756
+ return structure with a straight Python dictionary::
1757
+
1758
+ from sqlalchemy.orm import Bundle
1759
+
1760
+
1761
+ class DictBundle(Bundle):
1762
+ def create_row_processor(self, query, procs, labels):
1763
+ "Override create_row_processor to return values as dictionaries"
1764
+
1765
+ def proc(row):
1766
+ return dict(zip(labels, (proc(row) for proc in procs)))
1767
+
1768
+ return proc
1769
+
1770
+ A result from the above :class:`_orm.Bundle` will return dictionary
1771
+ values::
1772
+
1773
+ bn = DictBundle("mybundle", MyClass.data1, MyClass.data2)
1774
+ for row in session.execute(select(bn)).where(bn.c.data1 == "d1"):
1775
+ print(row.mybundle["data1"], row.mybundle["data2"])
1776
+
1777
+ The above example is available natively using :class:`.DictBundle`
1778
+
1779
+ .. seealso::
1780
+
1781
+ :class:`.DictBundle`
1782
+
1783
+ """ # noqa: E501
1784
+ keyed_tuple = result_tuple(labels, [() for l in labels])
1785
+
1786
+ def proc(row: Row[Unpack[TupleAny]]) -> Any:
1787
+ return keyed_tuple([proc(row) for proc in procs])
1788
+
1789
+ return proc
1790
+
1791
+
1792
+ class DictBundle(Bundle[_T]):
1793
+ """Like :class:`.Bundle` but returns ``dict`` instances instead of
1794
+ named tuple like objects::
1795
+
1796
+ bn = DictBundle("mybundle", MyClass.data1, MyClass.data2)
1797
+ for row in session.execute(select(bn)).where(bn.c.data1 == "d1"):
1798
+ print(row.mybundle["data1"], row.mybundle["data2"])
1799
+
1800
+ Differently from :class:`.Bundle`, multiple columns with the same name are
1801
+ not supported.
1802
+
1803
+ .. versionadded:: 2.1
1804
+
1805
+ .. seealso::
1806
+
1807
+ :ref:`bundles`
1808
+
1809
+ :class:`.Bundle`
1810
+ """
1811
+
1812
+ def __init__(
1813
+ self, name: str, *exprs: _ColumnExpressionArgument[Any], **kw: Any
1814
+ ) -> None:
1815
+ super().__init__(name, *exprs, **kw)
1816
+ if len(set(self.c.keys())) != len(self.c):
1817
+ raise sa_exc.ArgumentError(
1818
+ "DictBundle does not support duplicate column names"
1819
+ )
1820
+
1821
+ def create_row_processor(
1822
+ self,
1823
+ query: Select[Unpack[TupleAny]],
1824
+ procs: Sequence[Callable[[Row[Unpack[TupleAny]]], Any]],
1825
+ labels: Sequence[str],
1826
+ ) -> Callable[[Row[Unpack[TupleAny]]], dict[str, Any]]:
1827
+ def proc(row: Row[Unpack[TupleAny]]) -> dict[str, Any]:
1828
+ return dict(zip(labels, (proc(row) for proc in procs)))
1829
+
1830
+ return proc
1831
+
1832
+
1833
+ def _orm_full_deannotate(element: _SA) -> _SA:
1834
+ return sql_util._deep_deannotate(element)
1835
+
1836
+
1837
+ class _ORMJoin(expression.Join):
1838
+ """Extend Join to support ORM constructs as input."""
1839
+
1840
+ __visit_name__ = expression.Join.__visit_name__
1841
+
1842
+ inherit_cache = True
1843
+
1844
+ def __init__(
1845
+ self,
1846
+ left: _FromClauseArgument,
1847
+ right: _FromClauseArgument,
1848
+ onclause: Optional[_OnClauseArgument] = None,
1849
+ isouter: bool = False,
1850
+ full: bool = False,
1851
+ _left_memo: Optional[Any] = None,
1852
+ _right_memo: Optional[Any] = None,
1853
+ _extra_criteria: Tuple[ColumnElement[bool], ...] = (),
1854
+ ):
1855
+ left_info = cast(
1856
+ "Union[FromClause, _InternalEntityType[Any]]",
1857
+ inspection.inspect(left),
1858
+ )
1859
+
1860
+ right_info = cast(
1861
+ "Union[FromClause, _InternalEntityType[Any]]",
1862
+ inspection.inspect(right),
1863
+ )
1864
+ adapt_to = right_info.selectable
1865
+
1866
+ # used by joined eager loader
1867
+ self._left_memo = _left_memo
1868
+ self._right_memo = _right_memo
1869
+
1870
+ if isinstance(onclause, attributes.QueryableAttribute):
1871
+ if TYPE_CHECKING:
1872
+ assert isinstance(
1873
+ onclause.comparator, RelationshipProperty.Comparator
1874
+ )
1875
+ on_selectable = onclause.comparator._source_selectable()
1876
+ prop = onclause.property
1877
+ _extra_criteria += onclause._extra_criteria
1878
+ elif isinstance(onclause, MapperProperty):
1879
+ # used internally by joined eager loader...possibly not ideal
1880
+ prop = onclause
1881
+ on_selectable = prop.parent.selectable
1882
+ else:
1883
+ prop = None
1884
+ on_selectable = None
1885
+
1886
+ left_selectable = left_info.selectable
1887
+ if prop:
1888
+ adapt_from: Optional[FromClause]
1889
+ if sql_util.clause_is_present(on_selectable, left_selectable):
1890
+ adapt_from = on_selectable
1891
+ else:
1892
+ assert isinstance(left_selectable, FromClause)
1893
+ adapt_from = left_selectable
1894
+
1895
+ (
1896
+ pj,
1897
+ sj,
1898
+ source,
1899
+ dest,
1900
+ secondary,
1901
+ target_adapter,
1902
+ ) = prop._create_joins(
1903
+ source_selectable=adapt_from,
1904
+ dest_selectable=adapt_to,
1905
+ source_polymorphic=True,
1906
+ of_type_entity=right_info,
1907
+ alias_secondary=True,
1908
+ extra_criteria=_extra_criteria,
1909
+ )
1910
+
1911
+ if sj is not None:
1912
+ if isouter:
1913
+ # note this is an inner join from secondary->right
1914
+ right = sql.join(secondary, right, sj)
1915
+ onclause = pj
1916
+ else:
1917
+ left = sql.join(left, secondary, pj, isouter)
1918
+ onclause = sj
1919
+ else:
1920
+ onclause = pj
1921
+
1922
+ self._target_adapter = target_adapter
1923
+
1924
+ # we don't use the normal coercions logic for _ORMJoin
1925
+ # (probably should), so do some gymnastics to get the entity.
1926
+ # logic here is for #8721, which was a major bug in 1.4
1927
+ # for almost two years, not reported/fixed until 1.4.43 (!)
1928
+ if is_selectable(left_info):
1929
+ parententity = left_selectable._annotations.get(
1930
+ "parententity", None
1931
+ )
1932
+ elif insp_is_mapper(left_info) or insp_is_aliased_class(left_info):
1933
+ parententity = left_info
1934
+ else:
1935
+ parententity = None
1936
+
1937
+ if parententity is not None:
1938
+ self._annotations = self._annotations.union(
1939
+ {"parententity": parententity}
1940
+ )
1941
+
1942
+ augment_onclause = bool(_extra_criteria) and not prop
1943
+ expression.Join.__init__(self, left, right, onclause, isouter, full)
1944
+
1945
+ assert self.onclause is not None
1946
+
1947
+ if augment_onclause:
1948
+ self.onclause &= sql.and_(*_extra_criteria)
1949
+
1950
+ if (
1951
+ not prop
1952
+ and getattr(right_info, "mapper", None)
1953
+ and right_info.mapper.single # type: ignore[union-attr]
1954
+ ):
1955
+ right_info = cast("_InternalEntityType[Any]", right_info)
1956
+ # if single inheritance target and we are using a manual
1957
+ # or implicit ON clause, augment it the same way we'd augment the
1958
+ # WHERE.
1959
+ single_crit = right_info.mapper._single_table_criterion
1960
+ if single_crit is not None:
1961
+ if insp_is_aliased_class(right_info):
1962
+ single_crit = right_info._adapter.traverse(single_crit)
1963
+ self.onclause = self.onclause & single_crit
1964
+
1965
+ def _splice_into_center(self, other):
1966
+ """Splice a join into the center.
1967
+
1968
+ Given join(a, b) and join(b, c), return join(a, b).join(c)
1969
+
1970
+ """
1971
+ leftmost = other
1972
+ while isinstance(leftmost, sql.Join):
1973
+ leftmost = leftmost.left
1974
+
1975
+ assert self.right is leftmost
1976
+
1977
+ left = _ORMJoin(
1978
+ self.left,
1979
+ other.left,
1980
+ self.onclause,
1981
+ isouter=self.isouter,
1982
+ _left_memo=self._left_memo,
1983
+ _right_memo=other._left_memo._path_registry,
1984
+ )
1985
+
1986
+ return _ORMJoin(
1987
+ left,
1988
+ other.right,
1989
+ other.onclause,
1990
+ isouter=other.isouter,
1991
+ _right_memo=other._right_memo,
1992
+ )
1993
+
1994
+ def join(
1995
+ self,
1996
+ right: _FromClauseArgument,
1997
+ onclause: Optional[_OnClauseArgument] = None,
1998
+ isouter: bool = False,
1999
+ full: bool = False,
2000
+ ) -> _ORMJoin:
2001
+ return _ORMJoin(self, right, onclause, full=full, isouter=isouter)
2002
+
2003
+ def outerjoin(
2004
+ self,
2005
+ right: _FromClauseArgument,
2006
+ onclause: Optional[_OnClauseArgument] = None,
2007
+ full: bool = False,
2008
+ ) -> _ORMJoin:
2009
+ return _ORMJoin(self, right, onclause, isouter=True, full=full)
2010
+
2011
+
2012
+ def with_parent(
2013
+ instance: object,
2014
+ prop: attributes.QueryableAttribute[Any],
2015
+ from_entity: Optional[_EntityType[Any]] = None,
2016
+ ) -> ColumnElement[bool]:
2017
+ """Create filtering criterion that relates this query's primary entity
2018
+ to the given related instance, using established
2019
+ :func:`_orm.relationship()`
2020
+ configuration.
2021
+
2022
+ E.g.::
2023
+
2024
+ stmt = select(Address).where(with_parent(some_user, User.addresses))
2025
+
2026
+ The SQL rendered is the same as that rendered when a lazy loader
2027
+ would fire off from the given parent on that attribute, meaning
2028
+ that the appropriate state is taken from the parent object in
2029
+ Python without the need to render joins to the parent table
2030
+ in the rendered statement.
2031
+
2032
+ The given property may also make use of :meth:`_orm.PropComparator.of_type`
2033
+ to indicate the left side of the criteria::
2034
+
2035
+
2036
+ a1 = aliased(Address)
2037
+ a2 = aliased(Address)
2038
+ stmt = select(a1, a2).where(with_parent(u1, User.addresses.of_type(a2)))
2039
+
2040
+ The above use is equivalent to using the
2041
+ :func:`_orm.with_parent.from_entity` argument::
2042
+
2043
+ a1 = aliased(Address)
2044
+ a2 = aliased(Address)
2045
+ stmt = select(a1, a2).where(
2046
+ with_parent(u1, User.addresses, from_entity=a2)
2047
+ )
2048
+
2049
+ :param instance:
2050
+ An instance which has some :func:`_orm.relationship`.
2051
+
2052
+ :param property:
2053
+ Class-bound attribute, which indicates
2054
+ what relationship from the instance should be used to reconcile the
2055
+ parent/child relationship.
2056
+
2057
+ :param from_entity:
2058
+ Entity in which to consider as the left side. This defaults to the
2059
+ "zero" entity of the :class:`_query.Query` itself.
2060
+
2061
+ """ # noqa: E501
2062
+ prop_t: RelationshipProperty[Any]
2063
+
2064
+ if isinstance(prop, str):
2065
+ raise sa_exc.ArgumentError(
2066
+ "with_parent() accepts class-bound mapped attributes, not strings"
2067
+ )
2068
+ elif isinstance(prop, attributes.QueryableAttribute):
2069
+ if prop._of_type:
2070
+ from_entity = prop._of_type
2071
+ mapper_property = prop.property
2072
+ if mapper_property is None or not prop_is_relationship(
2073
+ mapper_property
2074
+ ):
2075
+ raise sa_exc.ArgumentError(
2076
+ f"Expected relationship property for with_parent(), "
2077
+ f"got {mapper_property}"
2078
+ )
2079
+ prop_t = mapper_property
2080
+ else:
2081
+ prop_t = prop
2082
+
2083
+ return prop_t._with_parent(instance, from_entity=from_entity)
2084
+
2085
+
2086
+ def has_identity(object_: object) -> bool:
2087
+ """Return True if the given object has a database
2088
+ identity.
2089
+
2090
+ This typically corresponds to the object being
2091
+ in either the persistent or detached state.
2092
+
2093
+ .. seealso::
2094
+
2095
+ :func:`.was_deleted`
2096
+
2097
+ """
2098
+ state = attributes.instance_state(object_)
2099
+ return state.has_identity
2100
+
2101
+
2102
+ def was_deleted(object_: object) -> bool:
2103
+ """Return True if the given object was deleted
2104
+ within a session flush.
2105
+
2106
+ This is regardless of whether or not the object is
2107
+ persistent or detached.
2108
+
2109
+ .. seealso::
2110
+
2111
+ :attr:`.InstanceState.was_deleted`
2112
+
2113
+ """
2114
+
2115
+ state = attributes.instance_state(object_)
2116
+ return state.was_deleted
2117
+
2118
+
2119
+ def _entity_corresponds_to(
2120
+ given: _InternalEntityType[Any], entity: _InternalEntityType[Any]
2121
+ ) -> bool:
2122
+ """determine if 'given' corresponds to 'entity', in terms
2123
+ of an entity passed to Query that would match the same entity
2124
+ being referred to elsewhere in the query.
2125
+
2126
+ """
2127
+ if insp_is_aliased_class(entity):
2128
+ if insp_is_aliased_class(given):
2129
+ if entity._base_alias() is given._base_alias():
2130
+ return True
2131
+ return False
2132
+ elif insp_is_aliased_class(given):
2133
+ if given._use_mapper_path:
2134
+ return entity in given.with_polymorphic_mappers
2135
+ else:
2136
+ return entity is given
2137
+
2138
+ assert insp_is_mapper(given)
2139
+ return entity.common_parent(given)
2140
+
2141
+
2142
+ def _entity_corresponds_to_use_path_impl(
2143
+ given: _InternalEntityType[Any], entity: _InternalEntityType[Any]
2144
+ ) -> bool:
2145
+ """determine if 'given' corresponds to 'entity', in terms
2146
+ of a path of loader options where a mapped attribute is taken to
2147
+ be a member of a parent entity.
2148
+
2149
+ e.g.::
2150
+
2151
+ someoption(A).someoption(A.b) # -> fn(A, A) -> True
2152
+ someoption(A).someoption(C.d) # -> fn(A, C) -> False
2153
+
2154
+ a1 = aliased(A)
2155
+ someoption(a1).someoption(A.b) # -> fn(a1, A) -> False
2156
+ someoption(a1).someoption(a1.b) # -> fn(a1, a1) -> True
2157
+
2158
+ wp = with_polymorphic(A, [A1, A2])
2159
+ someoption(wp).someoption(A1.foo) # -> fn(wp, A1) -> False
2160
+ someoption(wp).someoption(wp.A1.foo) # -> fn(wp, wp.A1) -> True
2161
+
2162
+ """
2163
+ if insp_is_aliased_class(given):
2164
+ return (
2165
+ insp_is_aliased_class(entity)
2166
+ and not entity._use_mapper_path
2167
+ and (given is entity or entity in given._with_polymorphic_entities)
2168
+ )
2169
+ elif not insp_is_aliased_class(entity):
2170
+ return given.isa(entity.mapper)
2171
+ else:
2172
+ return (
2173
+ entity._use_mapper_path
2174
+ and given in entity.with_polymorphic_mappers
2175
+ )
2176
+
2177
+
2178
+ def _entity_isa(given: _InternalEntityType[Any], mapper: Mapper[Any]) -> bool:
2179
+ """determine if 'given' "is a" mapper, in terms of the given
2180
+ would load rows of type 'mapper'.
2181
+
2182
+ """
2183
+ if given.is_aliased_class:
2184
+ return mapper in given.with_polymorphic_mappers or given.mapper.isa(
2185
+ mapper
2186
+ )
2187
+ elif given.with_polymorphic_mappers:
2188
+ return mapper in given.with_polymorphic_mappers or given.isa(mapper)
2189
+ else:
2190
+ return given.isa(mapper)
2191
+
2192
+
2193
+ def _getitem(iterable_query: Query[Any], item: Any) -> Any:
2194
+ """calculate __getitem__ in terms of an iterable query object
2195
+ that also has a slice() method.
2196
+
2197
+ """
2198
+
2199
+ def _no_negative_indexes():
2200
+ raise IndexError(
2201
+ "negative indexes are not accepted by SQL "
2202
+ "index / slice operators"
2203
+ )
2204
+
2205
+ if isinstance(item, slice):
2206
+ start, stop, step = util.decode_slice(item)
2207
+
2208
+ if (
2209
+ isinstance(stop, int)
2210
+ and isinstance(start, int)
2211
+ and stop - start <= 0
2212
+ ):
2213
+ return []
2214
+
2215
+ elif (isinstance(start, int) and start < 0) or (
2216
+ isinstance(stop, int) and stop < 0
2217
+ ):
2218
+ _no_negative_indexes()
2219
+
2220
+ res = iterable_query.slice(start, stop)
2221
+ if step is not None:
2222
+ return list(res)[None : None : item.step]
2223
+ else:
2224
+ return list(res)
2225
+ else:
2226
+ if item == -1:
2227
+ _no_negative_indexes()
2228
+ else:
2229
+ return list(iterable_query[item : item + 1])[0]
2230
+
2231
+
2232
+ def _is_mapped_annotation(
2233
+ raw_annotation: _AnnotationScanType,
2234
+ cls: Type[Any],
2235
+ originating_cls: Type[Any],
2236
+ ) -> bool:
2237
+ try:
2238
+ annotated = de_stringify_annotation(
2239
+ cls, raw_annotation, originating_cls.__module__
2240
+ )
2241
+ except NameError:
2242
+ # in most cases, at least within our own tests, we can raise
2243
+ # here, which is more accurate as it prevents us from returning
2244
+ # false negatives. However, in the real world, try to avoid getting
2245
+ # involved with end-user annotations that have nothing to do with us.
2246
+ # see issue #8888 where we bypass using this function in the case
2247
+ # that we want to detect an unresolvable Mapped[] type.
2248
+ return False
2249
+ else:
2250
+ return is_origin_of_cls(annotated, _MappedAnnotationBase)
2251
+
2252
+
2253
+ class _CleanupError(Exception):
2254
+ pass
2255
+
2256
+
2257
+ def _cleanup_mapped_str_annotation(
2258
+ annotation: str, originating_module: str
2259
+ ) -> str:
2260
+ # fix up an annotation that comes in as the form:
2261
+ # 'Mapped[List[Address]]' so that it instead looks like:
2262
+ # 'Mapped[List["Address"]]' , which will allow us to get
2263
+ # "Address" as a string
2264
+
2265
+ # additionally, resolve symbols for these names since this is where
2266
+ # we'd have to do it
2267
+
2268
+ inner: Optional[Match[str]]
2269
+
2270
+ mm = re.match(r"^([^ \|]+?)\[(.+)\]$", annotation)
2271
+
2272
+ if not mm:
2273
+ return annotation
2274
+
2275
+ # ticket #8759. Resolve the Mapped name to a real symbol.
2276
+ # originally this just checked the name.
2277
+ try:
2278
+ obj = eval_name_only(mm.group(1), originating_module)
2279
+ except NameError as ne:
2280
+ raise _CleanupError(
2281
+ f'For annotation "{annotation}", could not resolve '
2282
+ f'container type "{mm.group(1)}". '
2283
+ "Please ensure this type is imported at the module level "
2284
+ "outside of TYPE_CHECKING blocks"
2285
+ ) from ne
2286
+
2287
+ if obj is typing.ClassVar:
2288
+ real_symbol = "ClassVar"
2289
+ else:
2290
+ try:
2291
+ if issubclass(obj, _MappedAnnotationBase):
2292
+ real_symbol = obj.__name__
2293
+ else:
2294
+ return annotation
2295
+ except TypeError:
2296
+ # avoid isinstance(obj, type) check, just catch TypeError
2297
+ return annotation
2298
+
2299
+ # note: if one of the codepaths above didn't define real_symbol and
2300
+ # then didn't return, real_symbol raises UnboundLocalError
2301
+ # which is actually a NameError, and the calling routines don't
2302
+ # notice this since they are catching NameError anyway. Just in case
2303
+ # this is being modified in the future, something to be aware of.
2304
+
2305
+ stack = []
2306
+ inner = mm
2307
+ while True:
2308
+ stack.append(real_symbol if mm is inner else inner.group(1))
2309
+ g2 = inner.group(2)
2310
+ inner = re.match(r"^([^ \|]+?)\[(.+)\]$", g2)
2311
+ if inner is None:
2312
+ stack.append(g2)
2313
+ break
2314
+
2315
+ # stacks we want to rewrite, that is, quote the last entry which
2316
+ # we think is a relationship class name:
2317
+ #
2318
+ # ['Mapped', 'List', 'Address']
2319
+ # ['Mapped', 'A']
2320
+ #
2321
+ # stacks we dont want to rewrite, which are generally MappedColumn
2322
+ # use cases:
2323
+ #
2324
+ # ['Mapped', "'Optional[Dict[str, str]]'"]
2325
+ # ['Mapped', 'dict[str, str] | None']
2326
+
2327
+ if (
2328
+ # avoid already quoted symbols such as
2329
+ # ['Mapped', "'Optional[Dict[str, str]]'"]
2330
+ not re.match(r"""^["'].*["']$""", stack[-1])
2331
+ # avoid further generics like Dict[] such as
2332
+ # ['Mapped', 'dict[str, str] | None'],
2333
+ # ['Mapped', 'list[int] | list[str]'],
2334
+ # ['Mapped', 'Union[list[int], list[str]]'],
2335
+ and not re.search(r"[\[\]]", stack[-1])
2336
+ ):
2337
+ stripchars = "\"' "
2338
+ stack[-1] = ", ".join(
2339
+ f'"{elem.strip(stripchars)}"' for elem in stack[-1].split(",")
2340
+ )
2341
+
2342
+ annotation = "[".join(stack) + ("]" * (len(stack) - 1))
2343
+
2344
+ return annotation
2345
+
2346
+
2347
+ def _extract_mapped_subtype(
2348
+ raw_annotation: Optional[_AnnotationScanType],
2349
+ cls: type,
2350
+ originating_module: str,
2351
+ key: str,
2352
+ attr_cls: Type[Any],
2353
+ required: bool,
2354
+ is_dataclass_field: bool,
2355
+ expect_mapped: bool = True,
2356
+ raiseerr: bool = True,
2357
+ ) -> Optional[Tuple[Union[_AnnotationScanType, str], Optional[type]]]:
2358
+ """given an annotation, figure out if it's ``Mapped[something]`` and if
2359
+ so, return the ``something`` part.
2360
+
2361
+ Includes error raise scenarios and other options.
2362
+
2363
+ """
2364
+
2365
+ if raw_annotation is None:
2366
+ if required:
2367
+ raise orm_exc.MappedAnnotationError(
2368
+ f"Python typing annotation is required for attribute "
2369
+ f'"{cls.__name__}.{key}" when primary argument(s) for '
2370
+ f'"{attr_cls.__name__}" construct are None or not present'
2371
+ )
2372
+ return None
2373
+
2374
+ try:
2375
+ # destringify the "outside" of the annotation. note we are not
2376
+ # adding include_generic so it will *not* dig into generic contents,
2377
+ # which will remain as ForwardRef or plain str under future annotations
2378
+ # mode. The full destringify happens later when mapped_column goes
2379
+ # to do a full lookup in the registry type_annotations_map.
2380
+ annotated = de_stringify_annotation(
2381
+ cls,
2382
+ raw_annotation,
2383
+ originating_module,
2384
+ str_cleanup_fn=_cleanup_mapped_str_annotation,
2385
+ )
2386
+ except _CleanupError as ce:
2387
+ raise orm_exc.MappedAnnotationError(
2388
+ f"Could not interpret annotation {raw_annotation}. "
2389
+ "Check that it uses names that are correctly imported at the "
2390
+ "module level. See chained stack trace for more hints."
2391
+ ) from ce
2392
+ except NameError as ne:
2393
+ if raiseerr and "Mapped[" in raw_annotation: # type: ignore[operator]
2394
+ raise orm_exc.MappedAnnotationError(
2395
+ f"Could not interpret annotation {raw_annotation}. "
2396
+ "Check that it uses names that are correctly imported at the "
2397
+ "module level. See chained stack trace for more hints."
2398
+ ) from ne
2399
+
2400
+ annotated = raw_annotation # type: ignore[assignment]
2401
+
2402
+ if is_dataclass_field:
2403
+ return annotated, None
2404
+ else:
2405
+ if not hasattr(annotated, "__origin__") or not is_origin_of_cls(
2406
+ annotated, _MappedAnnotationBase
2407
+ ):
2408
+ if expect_mapped:
2409
+ if not raiseerr:
2410
+ return None
2411
+
2412
+ origin = getattr(annotated, "__origin__", None)
2413
+ if origin is typing.ClassVar:
2414
+ return None
2415
+
2416
+ # check for other kind of ORM descriptor like AssociationProxy,
2417
+ # don't raise for that (issue #9957)
2418
+ elif isinstance(origin, type) and issubclass(
2419
+ origin, ORMDescriptor
2420
+ ):
2421
+ return None
2422
+
2423
+ raise orm_exc.MappedAnnotationError(
2424
+ f'Type annotation for "{cls.__name__}.{key}" '
2425
+ "can't be correctly interpreted for "
2426
+ "Annotated Declarative Table form. ORM annotations "
2427
+ "should normally make use of the ``Mapped[]`` generic "
2428
+ "type, or other ORM-compatible generic type, as a "
2429
+ "container for the actual type, which indicates the "
2430
+ "intent that the attribute is mapped. "
2431
+ "Class variables that are not intended to be mapped "
2432
+ "by the ORM should use ClassVar[]. "
2433
+ "To allow Annotated Declarative to disregard legacy "
2434
+ "annotations which don't use Mapped[] to pass, set "
2435
+ '"__allow_unmapped__ = True" on the class or a '
2436
+ "superclass this class.",
2437
+ code="zlpr",
2438
+ )
2439
+
2440
+ else:
2441
+ return annotated, None
2442
+
2443
+ generic_annotated = cast(GenericProtocol[Any], annotated)
2444
+ if len(generic_annotated.__args__) != 1:
2445
+ raise orm_exc.MappedAnnotationError(
2446
+ "Expected sub-type for Mapped[] annotation"
2447
+ )
2448
+
2449
+ return (
2450
+ # fix dict/list/set args to be ForwardRef, see #11814
2451
+ fixup_container_fwd_refs(generic_annotated.__args__[0]),
2452
+ generic_annotated.__origin__,
2453
+ )
2454
+
2455
+
2456
+ def _mapper_property_as_plain_name(prop: Type[Any]) -> str:
2457
+ if hasattr(prop, "_mapper_property_name"):
2458
+ name = prop._mapper_property_name()
2459
+ else:
2460
+ name = None
2461
+ return util.clsname_as_plain_name(prop, name)