SQLAlchemy 2.1.0b2__cp313-cp313t-win_arm64.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 (270) hide show
  1. sqlalchemy/__init__.py +298 -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 +250 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +30 -0
  8. sqlalchemy/dialects/mssql/__init__.py +89 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4166 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +220 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +698 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +226 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +214 -0
  21. sqlalchemy/dialects/mysql/base.py +3877 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +106 -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 +330 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +296 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +312 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +157 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +156 -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 +3977 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1601 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +158 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +909 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +368 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +171 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +229 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1323 -0
  50. sqlalchemy/dialects/postgresql/base.py +5789 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +593 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +423 -0
  55. sqlalchemy/dialects/postgresql/json.py +408 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +521 -0
  57. sqlalchemy/dialects/postgresql/operators.py +130 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +670 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +344 -0
  60. sqlalchemy/dialects/postgresql/provision.py +184 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +799 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +321 -0
  68. sqlalchemy/dialects/sqlite/base.py +3063 -0
  69. sqlalchemy/dialects/sqlite/dml.py +279 -0
  70. sqlalchemy/dialects/sqlite/json.py +100 -0
  71. sqlalchemy/dialects/sqlite/provision.py +229 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +161 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +754 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp313t-win_arm64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp313t-win_arm64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +633 -0
  80. sqlalchemy/engine/_row_cy.cp313t-win_arm64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp313t-win_arm64.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3354 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2421 -0
  88. sqlalchemy/engine/default.py +2402 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3495 -0
  91. sqlalchemy/engine/mock.py +134 -0
  92. sqlalchemy/engine/processors.py +82 -0
  93. sqlalchemy/engine/reflection.py +2100 -0
  94. sqlalchemy/engine/result.py +1966 -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 +156 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +674 -0
  102. sqlalchemy/event/base.py +472 -0
  103. sqlalchemy/event/legacy.py +258 -0
  104. sqlalchemy/event/registry.py +390 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +922 -0
  107. sqlalchemy/ext/__init__.py +11 -0
  108. sqlalchemy/ext/associationproxy.py +2072 -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 +2007 -0
  116. sqlalchemy/ext/automap.py +1701 -0
  117. sqlalchemy/ext/baked.py +559 -0
  118. sqlalchemy/ext/compiler.py +600 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +560 -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 +439 -0
  127. sqlalchemy/ext/serializer.py +185 -0
  128. sqlalchemy/future/__init__.py +16 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +174 -0
  131. sqlalchemy/log.py +283 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +179 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +976 -0
  137. sqlalchemy/orm/bulk_persistence.py +2152 -0
  138. sqlalchemy/orm/clsregistry.py +582 -0
  139. sqlalchemy/orm/collections.py +1568 -0
  140. sqlalchemy/orm/context.py +3471 -0
  141. sqlalchemy/orm/decl_api.py +2280 -0
  142. sqlalchemy/orm/decl_base.py +2309 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +307 -0
  146. sqlalchemy/orm/evaluator.py +379 -0
  147. sqlalchemy/orm/events.py +3386 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +746 -0
  151. sqlalchemy/orm/interfaces.py +1589 -0
  152. sqlalchemy/orm/loading.py +1684 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4411 -0
  155. sqlalchemy/orm/path_registry.py +829 -0
  156. sqlalchemy/orm/persistence.py +1789 -0
  157. sqlalchemy/orm/properties.py +973 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3570 -0
  160. sqlalchemy/orm/scoping.py +2232 -0
  161. sqlalchemy/orm/session.py +5403 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3492 -0
  165. sqlalchemy/orm/strategy_options.py +2562 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +798 -0
  168. sqlalchemy/orm/util.py +2438 -0
  169. sqlalchemy/orm/writeonly.py +694 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1522 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +582 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +74 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +397 -0
  178. sqlalchemy/sql/_dml_constructors.py +132 -0
  179. sqlalchemy/sql/_elements_constructors.py +2164 -0
  180. sqlalchemy/sql/_orm_types.py +20 -0
  181. sqlalchemy/sql/_selectable_constructors.py +840 -0
  182. sqlalchemy/sql/_typing.py +487 -0
  183. sqlalchemy/sql/_util_cy.cp313t-win_arm64.pyd +0 -0
  184. sqlalchemy/sql/_util_cy.py +127 -0
  185. sqlalchemy/sql/annotation.py +590 -0
  186. sqlalchemy/sql/base.py +2699 -0
  187. sqlalchemy/sql/cache_key.py +1066 -0
  188. sqlalchemy/sql/coercions.py +1373 -0
  189. sqlalchemy/sql/compiler.py +8327 -0
  190. sqlalchemy/sql/crud.py +1815 -0
  191. sqlalchemy/sql/ddl.py +1928 -0
  192. sqlalchemy/sql/default_comparator.py +654 -0
  193. sqlalchemy/sql/dml.py +1977 -0
  194. sqlalchemy/sql/elements.py +6033 -0
  195. sqlalchemy/sql/events.py +458 -0
  196. sqlalchemy/sql/expression.py +172 -0
  197. sqlalchemy/sql/functions.py +2305 -0
  198. sqlalchemy/sql/lambdas.py +1443 -0
  199. sqlalchemy/sql/naming.py +209 -0
  200. sqlalchemy/sql/operators.py +2897 -0
  201. sqlalchemy/sql/roles.py +332 -0
  202. sqlalchemy/sql/schema.py +6703 -0
  203. sqlalchemy/sql/selectable.py +7553 -0
  204. sqlalchemy/sql/sqltypes.py +4093 -0
  205. sqlalchemy/sql/traversals.py +1042 -0
  206. sqlalchemy/sql/type_api.py +2446 -0
  207. sqlalchemy/sql/util.py +1495 -0
  208. sqlalchemy/sql/visitors.py +1157 -0
  209. sqlalchemy/testing/__init__.py +96 -0
  210. sqlalchemy/testing/assertions.py +1007 -0
  211. sqlalchemy/testing/assertsql.py +519 -0
  212. sqlalchemy/testing/asyncio.py +128 -0
  213. sqlalchemy/testing/config.py +440 -0
  214. sqlalchemy/testing/engines.py +483 -0
  215. sqlalchemy/testing/entities.py +117 -0
  216. sqlalchemy/testing/exclusions.py +476 -0
  217. sqlalchemy/testing/fixtures/__init__.py +30 -0
  218. sqlalchemy/testing/fixtures/base.py +384 -0
  219. sqlalchemy/testing/fixtures/mypy.py +247 -0
  220. sqlalchemy/testing/fixtures/orm.py +227 -0
  221. sqlalchemy/testing/fixtures/sql.py +538 -0
  222. sqlalchemy/testing/pickleable.py +155 -0
  223. sqlalchemy/testing/plugin/__init__.py +6 -0
  224. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  225. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  226. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  227. sqlalchemy/testing/profiling.py +329 -0
  228. sqlalchemy/testing/provision.py +613 -0
  229. sqlalchemy/testing/requirements.py +1978 -0
  230. sqlalchemy/testing/schema.py +198 -0
  231. sqlalchemy/testing/suite/__init__.py +19 -0
  232. sqlalchemy/testing/suite/test_cte.py +237 -0
  233. sqlalchemy/testing/suite/test_ddl.py +420 -0
  234. sqlalchemy/testing/suite/test_dialect.py +776 -0
  235. sqlalchemy/testing/suite/test_insert.py +630 -0
  236. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  237. sqlalchemy/testing/suite/test_results.py +660 -0
  238. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  239. sqlalchemy/testing/suite/test_select.py +2112 -0
  240. sqlalchemy/testing/suite/test_sequence.py +317 -0
  241. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  242. sqlalchemy/testing/suite/test_types.py +2271 -0
  243. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  244. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  245. sqlalchemy/testing/util.py +535 -0
  246. sqlalchemy/testing/warnings.py +52 -0
  247. sqlalchemy/types.py +76 -0
  248. sqlalchemy/util/__init__.py +158 -0
  249. sqlalchemy/util/_collections.py +688 -0
  250. sqlalchemy/util/_collections_cy.cp313t-win_arm64.pyd +0 -0
  251. sqlalchemy/util/_collections_cy.pxd +8 -0
  252. sqlalchemy/util/_collections_cy.py +516 -0
  253. sqlalchemy/util/_has_cython.py +46 -0
  254. sqlalchemy/util/_immutabledict_cy.cp313t-win_arm64.pyd +0 -0
  255. sqlalchemy/util/_immutabledict_cy.py +240 -0
  256. sqlalchemy/util/compat.py +299 -0
  257. sqlalchemy/util/concurrency.py +322 -0
  258. sqlalchemy/util/cython.py +79 -0
  259. sqlalchemy/util/deprecations.py +401 -0
  260. sqlalchemy/util/langhelpers.py +2320 -0
  261. sqlalchemy/util/preloaded.py +152 -0
  262. sqlalchemy/util/queue.py +304 -0
  263. sqlalchemy/util/tool_support.py +201 -0
  264. sqlalchemy/util/topological.py +120 -0
  265. sqlalchemy/util/typing.py +711 -0
  266. sqlalchemy-2.1.0b2.dist-info/METADATA +269 -0
  267. sqlalchemy-2.1.0b2.dist-info/RECORD +270 -0
  268. sqlalchemy-2.1.0b2.dist-info/WHEEL +5 -0
  269. sqlalchemy-2.1.0b2.dist-info/licenses/LICENSE +19 -0
  270. sqlalchemy-2.1.0b2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1183 @@
1
+ # orm/descriptor_props.py
2
+ # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+
8
+ """Descriptor properties are more "auxiliary" properties
9
+ that exist as configurational elements, but don't participate
10
+ as actively in the load/persist ORM loop.
11
+
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import is_dataclass
16
+ import inspect
17
+ import itertools
18
+ import operator
19
+ import typing
20
+ from typing import Any
21
+ from typing import Callable
22
+ from typing import Dict
23
+ from typing import get_args
24
+ from typing import List
25
+ from typing import NoReturn
26
+ from typing import Optional
27
+ from typing import Sequence
28
+ from typing import Tuple
29
+ from typing import Type
30
+ from typing import TYPE_CHECKING
31
+ from typing import TypeVar
32
+ from typing import Union
33
+ import weakref
34
+
35
+ from . import attributes
36
+ from . import util as orm_util
37
+ from .base import _DeclarativeMapped
38
+ from .base import DONT_SET
39
+ from .base import LoaderCallableStatus
40
+ from .base import Mapped
41
+ from .base import PassiveFlag
42
+ from .base import SQLORMOperations
43
+ from .interfaces import _AttributeOptions
44
+ from .interfaces import _IntrospectsAnnotations
45
+ from .interfaces import _MapsColumns
46
+ from .interfaces import MapperProperty
47
+ from .interfaces import PropComparator
48
+ from .util import de_stringify_annotation
49
+ from .. import event
50
+ from .. import exc as sa_exc
51
+ from .. import schema
52
+ from .. import sql
53
+ from .. import util
54
+ from ..sql import expression
55
+ from ..sql import operators
56
+ from ..sql.base import _NoArg
57
+ from ..sql.elements import BindParameter
58
+ from ..util.typing import de_optionalize_union_types
59
+ from ..util.typing import includes_none
60
+ from ..util.typing import is_fwd_ref
61
+ from ..util.typing import is_pep593
62
+ from ..util.typing import is_union
63
+ from ..util.typing import TupleAny
64
+ from ..util.typing import Unpack
65
+
66
+
67
+ if typing.TYPE_CHECKING:
68
+ from ._typing import _InstanceDict
69
+ from ._typing import _RegistryType
70
+ from .attributes import History
71
+ from .attributes import InstrumentedAttribute
72
+ from .attributes import QueryableAttribute
73
+ from .context import _ORMCompileState
74
+ from .decl_base import _ClassScanAbstractConfig
75
+ from .decl_base import _DeclarativeMapperConfig
76
+ from .interfaces import _DataclassArguments
77
+ from .mapper import Mapper
78
+ from .properties import ColumnProperty
79
+ from .properties import MappedColumn
80
+ from .state import InstanceState
81
+ from ..engine.base import Connection
82
+ from ..engine.row import Row
83
+ from ..sql._typing import _DMLColumnArgument
84
+ from ..sql._typing import _InfoType
85
+ from ..sql.elements import ClauseList
86
+ from ..sql.elements import ColumnElement
87
+ from ..sql.operators import OperatorType
88
+ from ..sql.schema import Column
89
+ from ..sql.selectable import Select
90
+ from ..util.typing import _AnnotationScanType
91
+ from ..util.typing import CallableReference
92
+ from ..util.typing import DescriptorReference
93
+ from ..util.typing import RODescriptorReference
94
+
95
+ _T = TypeVar("_T", bound=Any)
96
+ _PT = TypeVar("_PT", bound=Any)
97
+
98
+
99
+ class DescriptorProperty(MapperProperty[_T]):
100
+ """:class:`.MapperProperty` which proxies access to a
101
+ user-defined descriptor."""
102
+
103
+ doc: Optional[str] = None
104
+
105
+ uses_objects = False
106
+ _links_to_entity = False
107
+
108
+ descriptor: DescriptorReference[Any]
109
+
110
+ def _column_strategy_attrs(self) -> Sequence[QueryableAttribute[Any]]:
111
+ raise NotImplementedError(
112
+ "This MapperProperty does not implement column loader strategies"
113
+ )
114
+
115
+ def get_history(
116
+ self,
117
+ state: InstanceState[Any],
118
+ dict_: _InstanceDict,
119
+ passive: PassiveFlag = PassiveFlag.PASSIVE_OFF,
120
+ ) -> History:
121
+ raise NotImplementedError()
122
+
123
+ def instrument_class(self, mapper: Mapper[Any]) -> None:
124
+ prop = self
125
+
126
+ class _ProxyImpl(attributes._AttributeImpl):
127
+ accepts_scalar_loader = False
128
+ load_on_unexpire = True
129
+ collection = False
130
+
131
+ @property
132
+ def uses_objects(self) -> bool: # type: ignore
133
+ return prop.uses_objects
134
+
135
+ def __init__(self, key: str):
136
+ self.key = key
137
+
138
+ def get_history(
139
+ self,
140
+ state: InstanceState[Any],
141
+ dict_: _InstanceDict,
142
+ passive: PassiveFlag = PassiveFlag.PASSIVE_OFF,
143
+ ) -> History:
144
+ return prop.get_history(state, dict_, passive)
145
+
146
+ if self.descriptor is None:
147
+ desc = getattr(mapper.class_, self.key, None)
148
+ if mapper._is_userland_descriptor(self.key, desc):
149
+ self.descriptor = desc
150
+
151
+ if self.descriptor is None:
152
+
153
+ def fset(obj: Any, value: Any) -> None:
154
+ setattr(obj, self.name, value)
155
+
156
+ def fdel(obj: Any) -> None:
157
+ delattr(obj, self.name)
158
+
159
+ def fget(obj: Any) -> Any:
160
+ return getattr(obj, self.name)
161
+
162
+ self.descriptor = property(fget=fget, fset=fset, fdel=fdel)
163
+
164
+ proxy_attr = attributes._create_proxied_attribute(self.descriptor)(
165
+ self.parent.class_,
166
+ self.key,
167
+ self.descriptor,
168
+ lambda: self._comparator_factory(mapper),
169
+ doc=self.doc,
170
+ original_property=self,
171
+ )
172
+
173
+ proxy_attr.impl = _ProxyImpl(self.key)
174
+ mapper.class_manager.instrument_attribute(self.key, proxy_attr)
175
+
176
+
177
+ _CompositeAttrType = Union[
178
+ str,
179
+ "Column[_T]",
180
+ "MappedColumn[_T]",
181
+ "InstrumentedAttribute[_T]",
182
+ "Mapped[_T]",
183
+ ]
184
+
185
+
186
+ _CC = TypeVar("_CC", bound=Any)
187
+
188
+
189
+ _composite_getters: weakref.WeakKeyDictionary[
190
+ Type[Any], Callable[[Any], Tuple[Any, ...]]
191
+ ] = weakref.WeakKeyDictionary()
192
+
193
+
194
+ class CompositeProperty(
195
+ _MapsColumns[_CC], _IntrospectsAnnotations, DescriptorProperty[_CC]
196
+ ):
197
+ """Defines a "composite" mapped attribute, representing a collection
198
+ of columns as one attribute.
199
+
200
+ :class:`.CompositeProperty` is constructed using the :func:`.composite`
201
+ function.
202
+
203
+ .. seealso::
204
+
205
+ :ref:`mapper_composite`
206
+
207
+ """
208
+
209
+ composite_class: Union[Type[_CC], Callable[..., _CC]]
210
+ attrs: Tuple[_CompositeAttrType[Any], ...]
211
+
212
+ _generated_composite_accessor: CallableReference[
213
+ Optional[Callable[[_CC], Tuple[Any, ...]]]
214
+ ]
215
+
216
+ comparator_factory: Type[Comparator[_CC]]
217
+
218
+ def __init__(
219
+ self,
220
+ _class_or_attr: Union[
221
+ None, Type[_CC], Callable[..., _CC], _CompositeAttrType[Any]
222
+ ] = None,
223
+ *attrs: _CompositeAttrType[Any],
224
+ return_none_on: Union[
225
+ _NoArg, None, Callable[..., bool]
226
+ ] = _NoArg.NO_ARG,
227
+ attribute_options: Optional[_AttributeOptions] = None,
228
+ active_history: bool = False,
229
+ deferred: bool = False,
230
+ group: Optional[str] = None,
231
+ comparator_factory: Optional[Type[Comparator[_CC]]] = None,
232
+ info: Optional[_InfoType] = None,
233
+ **kwargs: Any,
234
+ ):
235
+ super().__init__(attribute_options=attribute_options)
236
+
237
+ if isinstance(_class_or_attr, (Mapped, str, sql.ColumnElement)):
238
+ self.attrs = (_class_or_attr,) + attrs
239
+ # will initialize within declarative_scan
240
+ self.composite_class = None # type: ignore
241
+ else:
242
+ self.composite_class = _class_or_attr # type: ignore
243
+ self.attrs = attrs
244
+
245
+ self.return_none_on = return_none_on
246
+ self.active_history = active_history
247
+ self.deferred = deferred
248
+ self.group = group
249
+ self.comparator_factory = (
250
+ comparator_factory
251
+ if comparator_factory is not None
252
+ else self.__class__.Comparator
253
+ )
254
+ self._generated_composite_accessor = None
255
+ if info is not None:
256
+ self.info.update(info)
257
+
258
+ util.set_creation_order(self)
259
+ self._create_descriptor()
260
+ self._init_accessor()
261
+
262
+ @util.memoized_property
263
+ def _construct_composite(self) -> Callable[..., Any]:
264
+ return_none_on = self.return_none_on
265
+ if callable(return_none_on):
266
+
267
+ def construct(*args: Any) -> Any:
268
+ if return_none_on(*args):
269
+ return None
270
+ else:
271
+ return self.composite_class(*args)
272
+
273
+ return construct
274
+ else:
275
+ return self.composite_class
276
+
277
+ def instrument_class(self, mapper: Mapper[Any]) -> None:
278
+ super().instrument_class(mapper)
279
+ self._setup_event_handlers()
280
+
281
+ def _composite_values_from_instance(self, value: _CC) -> Tuple[Any, ...]:
282
+ if self._generated_composite_accessor:
283
+ return self._generated_composite_accessor(value)
284
+ else:
285
+ try:
286
+ accessor = value.__composite_values__
287
+ except AttributeError as ae:
288
+ raise sa_exc.InvalidRequestError(
289
+ f"Composite class {self.composite_class.__name__} is not "
290
+ f"a dataclass and does not define a __composite_values__()"
291
+ " method; can't get state"
292
+ ) from ae
293
+ else:
294
+ return accessor() # type: ignore
295
+
296
+ def do_init(self) -> None:
297
+ """Initialization which occurs after the :class:`.Composite`
298
+ has been associated with its parent mapper.
299
+
300
+ """
301
+ self._setup_arguments_on_columns()
302
+
303
+ _COMPOSITE_FGET = object()
304
+
305
+ def _create_descriptor(self) -> None:
306
+ """Create the Python descriptor that will serve as
307
+ the access point on instances of the mapped class.
308
+
309
+ """
310
+
311
+ def fget(instance: Any) -> Any:
312
+ dict_ = attributes.instance_dict(instance)
313
+ state = attributes.instance_state(instance)
314
+
315
+ if self.key not in dict_:
316
+ # key not present. Iterate through related
317
+ # attributes, retrieve their values. This
318
+ # ensures they all load.
319
+ values = [
320
+ getattr(instance, key) for key in self._attribute_keys
321
+ ]
322
+
323
+ if self.key not in dict_:
324
+ dict_[self.key] = self._construct_composite(*values)
325
+ state.manager.dispatch.refresh(
326
+ state, self._COMPOSITE_FGET, [self.key]
327
+ )
328
+
329
+ return dict_.get(self.key, None)
330
+
331
+ def fset(instance: Any, value: Any) -> None:
332
+ if value is LoaderCallableStatus.DONT_SET:
333
+ return
334
+
335
+ dict_ = attributes.instance_dict(instance)
336
+ state = attributes.instance_state(instance)
337
+ attr = state.manager[self.key]
338
+
339
+ if attr.dispatch._active_history:
340
+ previous = fget(instance)
341
+ else:
342
+ previous = dict_.get(self.key, LoaderCallableStatus.NO_VALUE)
343
+
344
+ for fn in attr.dispatch.set:
345
+ value = fn(state, value, previous, attr.impl)
346
+ dict_[self.key] = value
347
+ if value is None:
348
+ for key in self._attribute_keys:
349
+ setattr(instance, key, None)
350
+ else:
351
+ for key, value in zip(
352
+ self._attribute_keys,
353
+ self._composite_values_from_instance(value),
354
+ ):
355
+ setattr(instance, key, value)
356
+
357
+ def fdel(instance: Any) -> None:
358
+ state = attributes.instance_state(instance)
359
+ dict_ = attributes.instance_dict(instance)
360
+ attr = state.manager[self.key]
361
+
362
+ if attr.dispatch._active_history:
363
+ previous = fget(instance)
364
+ dict_.pop(self.key, None)
365
+ else:
366
+ previous = dict_.pop(self.key, LoaderCallableStatus.NO_VALUE)
367
+
368
+ attr = state.manager[self.key]
369
+ attr.dispatch.remove(state, previous, attr.impl)
370
+ for key in self._attribute_keys:
371
+ setattr(instance, key, None)
372
+
373
+ self.descriptor = property(fget, fset, fdel)
374
+
375
+ @util.preload_module("sqlalchemy.orm.properties")
376
+ def declarative_scan(
377
+ self,
378
+ decl_scan: _DeclarativeMapperConfig,
379
+ registry: _RegistryType,
380
+ cls: Type[Any],
381
+ originating_module: Optional[str],
382
+ key: str,
383
+ mapped_container: Optional[Type[Mapped[Any]]],
384
+ annotation: Optional[_AnnotationScanType],
385
+ extracted_mapped_annotation: Optional[_AnnotationScanType],
386
+ is_dataclass_field: bool,
387
+ ) -> None:
388
+ MappedColumn = util.preloaded.orm_properties.MappedColumn
389
+ if (
390
+ self.composite_class is None
391
+ and extracted_mapped_annotation is None
392
+ ):
393
+ self._raise_for_required(key, cls)
394
+ argument = extracted_mapped_annotation
395
+
396
+ if is_pep593(argument):
397
+ argument = get_args(argument)[0]
398
+
399
+ if argument and self.composite_class is None:
400
+ if isinstance(argument, str) or is_fwd_ref(
401
+ argument, check_generic=True
402
+ ):
403
+ if originating_module is None:
404
+ str_arg = (
405
+ argument.__forward_arg__
406
+ if hasattr(argument, "__forward_arg__")
407
+ else str(argument)
408
+ )
409
+ raise sa_exc.ArgumentError(
410
+ f"Can't use forward ref {argument} for composite "
411
+ f"class argument; set up the type as Mapped[{str_arg}]"
412
+ )
413
+ argument = de_stringify_annotation(
414
+ cls, argument, originating_module, include_generic=True
415
+ )
416
+
417
+ if is_union(argument) and includes_none(argument):
418
+ if self.return_none_on is _NoArg.NO_ARG:
419
+ self.return_none_on = lambda *args: all(
420
+ arg is None for arg in args
421
+ )
422
+ argument = de_optionalize_union_types(argument)
423
+
424
+ self.composite_class = argument
425
+
426
+ if is_dataclass(self.composite_class):
427
+ self._setup_for_dataclass(
428
+ decl_scan, registry, cls, originating_module, key
429
+ )
430
+ else:
431
+ for attr in self.attrs:
432
+ if (
433
+ isinstance(attr, (MappedColumn, schema.Column))
434
+ and attr.name is None
435
+ ):
436
+ raise sa_exc.ArgumentError(
437
+ "Composite class column arguments must be named "
438
+ "unless a dataclass is used"
439
+ )
440
+ self._init_accessor()
441
+
442
+ def _init_accessor(self) -> None:
443
+ if is_dataclass(self.composite_class) and not hasattr(
444
+ self.composite_class, "__composite_values__"
445
+ ):
446
+ insp = inspect.signature(self.composite_class)
447
+ getter = operator.attrgetter(
448
+ *[p.name for p in insp.parameters.values()]
449
+ )
450
+ if len(insp.parameters) == 1:
451
+ self._generated_composite_accessor = lambda obj: (getter(obj),)
452
+ else:
453
+ self._generated_composite_accessor = getter
454
+
455
+ if (
456
+ self.composite_class is not None
457
+ and isinstance(self.composite_class, type)
458
+ and self.composite_class not in _composite_getters
459
+ ):
460
+ if self._generated_composite_accessor is not None:
461
+ _composite_getters[self.composite_class] = (
462
+ self._generated_composite_accessor
463
+ )
464
+ elif hasattr(self.composite_class, "__composite_values__"):
465
+ _composite_getters[self.composite_class] = (
466
+ lambda obj: obj.__composite_values__()
467
+ )
468
+
469
+ @util.preload_module("sqlalchemy.orm.properties")
470
+ @util.preload_module("sqlalchemy.orm.decl_base")
471
+ def _setup_for_dataclass(
472
+ self,
473
+ decl_scan: _DeclarativeMapperConfig,
474
+ registry: _RegistryType,
475
+ cls: Type[Any],
476
+ originating_module: Optional[str],
477
+ key: str,
478
+ ) -> None:
479
+ MappedColumn = util.preloaded.orm_properties.MappedColumn
480
+
481
+ decl_base = util.preloaded.orm_decl_base
482
+
483
+ insp = inspect.signature(self.composite_class)
484
+ for param, attr in itertools.zip_longest(
485
+ insp.parameters.values(), self.attrs
486
+ ):
487
+ if param is None:
488
+ raise sa_exc.ArgumentError(
489
+ f"number of composite attributes "
490
+ f"{len(self.attrs)} exceeds "
491
+ f"that of the number of attributes in class "
492
+ f"{self.composite_class.__name__} {len(insp.parameters)}"
493
+ )
494
+ if attr is None:
495
+ # fill in missing attr spots with empty MappedColumn
496
+ attr = MappedColumn()
497
+ self.attrs += (attr,)
498
+
499
+ if isinstance(attr, MappedColumn):
500
+ attr.declarative_scan_for_composite(
501
+ decl_scan,
502
+ registry,
503
+ cls,
504
+ originating_module,
505
+ key,
506
+ param.name,
507
+ param.annotation,
508
+ )
509
+ elif isinstance(attr, schema.Column):
510
+ decl_base._undefer_column_name(param.name, attr)
511
+
512
+ @util.memoized_property
513
+ def _comparable_elements(self) -> Sequence[QueryableAttribute[Any]]:
514
+ return [getattr(self.parent.class_, prop.key) for prop in self.props]
515
+
516
+ @util.memoized_property
517
+ @util.preload_module("orm.properties")
518
+ def props(self) -> Sequence[MapperProperty[Any]]:
519
+ props = []
520
+ MappedColumn = util.preloaded.orm_properties.MappedColumn
521
+
522
+ for attr in self.attrs:
523
+ if isinstance(attr, str):
524
+ prop = self.parent.get_property(attr, _configure_mappers=False)
525
+ elif isinstance(attr, schema.Column):
526
+ prop = self.parent._columntoproperty[attr]
527
+ elif isinstance(attr, MappedColumn):
528
+ prop = self.parent._columntoproperty[attr.column]
529
+ elif isinstance(attr, attributes.InstrumentedAttribute):
530
+ prop = attr.property
531
+ else:
532
+ prop = None
533
+
534
+ if not isinstance(prop, MapperProperty):
535
+ raise sa_exc.ArgumentError(
536
+ "Composite expects Column objects or mapped "
537
+ f"attributes/attribute names as arguments, got: {attr!r}"
538
+ )
539
+
540
+ props.append(prop)
541
+ return props
542
+
543
+ def _column_strategy_attrs(self) -> Sequence[QueryableAttribute[Any]]:
544
+ return self._comparable_elements
545
+
546
+ @util.non_memoized_property
547
+ @util.preload_module("orm.properties")
548
+ def columns(self) -> Sequence[Column[Any]]:
549
+ MappedColumn = util.preloaded.orm_properties.MappedColumn
550
+ return [
551
+ a.column if isinstance(a, MappedColumn) else a
552
+ for a in self.attrs
553
+ if isinstance(a, (schema.Column, MappedColumn))
554
+ ]
555
+
556
+ @property
557
+ def mapper_property_to_assign(self) -> Optional[MapperProperty[_CC]]:
558
+ return self
559
+
560
+ @property
561
+ def columns_to_assign(self) -> List[Tuple[schema.Column[Any], int]]:
562
+ return [(c, 0) for c in self.columns if c.table is None]
563
+
564
+ @util.preload_module("orm.properties")
565
+ def _setup_arguments_on_columns(self) -> None:
566
+ """Propagate configuration arguments made on this composite
567
+ to the target columns, for those that apply.
568
+
569
+ """
570
+ ColumnProperty = util.preloaded.orm_properties.ColumnProperty
571
+
572
+ for prop in self.props:
573
+ if not isinstance(prop, ColumnProperty):
574
+ continue
575
+ else:
576
+ cprop = prop
577
+
578
+ cprop.active_history = self.active_history
579
+ if self.deferred:
580
+ cprop.deferred = self.deferred
581
+ cprop.strategy_key = (("deferred", True), ("instrument", True))
582
+ cprop.group = self.group
583
+
584
+ def _setup_event_handlers(self) -> None:
585
+ """Establish events that populate/expire the composite attribute."""
586
+
587
+ def load_handler(
588
+ state: InstanceState[Any], context: _ORMCompileState
589
+ ) -> None:
590
+ _load_refresh_handler(state, context, None, is_refresh=False)
591
+
592
+ def refresh_handler(
593
+ state: InstanceState[Any],
594
+ context: _ORMCompileState,
595
+ to_load: Optional[Sequence[str]],
596
+ ) -> None:
597
+ # note this corresponds to sqlalchemy.ext.mutable load_attrs()
598
+
599
+ if not to_load or (
600
+ {self.key}.union(self._attribute_keys)
601
+ ).intersection(to_load):
602
+ _load_refresh_handler(state, context, to_load, is_refresh=True)
603
+
604
+ def _load_refresh_handler(
605
+ state: InstanceState[Any],
606
+ context: _ORMCompileState,
607
+ to_load: Optional[Sequence[str]],
608
+ is_refresh: bool,
609
+ ) -> None:
610
+ dict_ = state.dict
611
+
612
+ # if context indicates we are coming from the
613
+ # fget() handler, this already set the value; skip the
614
+ # handler here. (other handlers like mutablecomposite will still
615
+ # want to catch it)
616
+ # there's an insufficiency here in that the fget() handler
617
+ # really should not be using the refresh event and there should
618
+ # be some other event that mutablecomposite can subscribe
619
+ # towards for this.
620
+
621
+ if (
622
+ not is_refresh or context is self._COMPOSITE_FGET
623
+ ) and self.key in dict_:
624
+ return
625
+
626
+ # if column elements aren't loaded, skip.
627
+ # __get__() will initiate a load for those
628
+ # columns
629
+ for k in self._attribute_keys:
630
+ if k not in dict_:
631
+ return
632
+
633
+ dict_[self.key] = self._construct_composite(
634
+ *[state.dict[key] for key in self._attribute_keys]
635
+ )
636
+
637
+ def expire_handler(
638
+ state: InstanceState[Any], keys: Optional[Sequence[str]]
639
+ ) -> None:
640
+ if keys is None or set(self._attribute_keys).intersection(keys):
641
+ state.dict.pop(self.key, None)
642
+
643
+ def insert_update_handler(
644
+ mapper: Mapper[Any],
645
+ connection: Connection,
646
+ state: InstanceState[Any],
647
+ ) -> None:
648
+ """After an insert or update, some columns may be expired due
649
+ to server side defaults, or re-populated due to client side
650
+ defaults. Pop out the composite value here so that it
651
+ recreates.
652
+
653
+ """
654
+
655
+ state.dict.pop(self.key, None)
656
+
657
+ event.listen(
658
+ self.parent, "after_insert", insert_update_handler, raw=True
659
+ )
660
+ event.listen(
661
+ self.parent, "after_update", insert_update_handler, raw=True
662
+ )
663
+ event.listen(
664
+ self.parent, "load", load_handler, raw=True, propagate=True
665
+ )
666
+ event.listen(
667
+ self.parent, "refresh", refresh_handler, raw=True, propagate=True
668
+ )
669
+ event.listen(
670
+ self.parent, "expire", expire_handler, raw=True, propagate=True
671
+ )
672
+
673
+ proxy_attr = self.parent.class_manager[self.key]
674
+ proxy_attr.impl.dispatch = proxy_attr.dispatch # type: ignore
675
+ proxy_attr.impl.dispatch._active_history = self.active_history
676
+
677
+ # TODO: need a deserialize hook here
678
+
679
+ @util.memoized_property
680
+ def _attribute_keys(self) -> Sequence[str]:
681
+ return [prop.key for prop in self.props]
682
+
683
+ def _populate_composite_bulk_save_mappings_fn(
684
+ self,
685
+ ) -> Callable[[Dict[str, Any]], None]:
686
+ if self._generated_composite_accessor:
687
+ get_values = self._generated_composite_accessor
688
+ else:
689
+
690
+ def get_values(val: Any) -> Tuple[Any]:
691
+ return val.__composite_values__() # type: ignore
692
+
693
+ attrs = [prop.key for prop in self.props]
694
+
695
+ def populate(dest_dict: Dict[str, Any]) -> None:
696
+ dest_dict.update(
697
+ {
698
+ key: val
699
+ for key, val in zip(
700
+ attrs, get_values(dest_dict.pop(self.key))
701
+ )
702
+ }
703
+ )
704
+
705
+ return populate
706
+
707
+ def get_history(
708
+ self,
709
+ state: InstanceState[Any],
710
+ dict_: _InstanceDict,
711
+ passive: PassiveFlag = PassiveFlag.PASSIVE_OFF,
712
+ ) -> History:
713
+ """Provided for userland code that uses attributes.get_history()."""
714
+
715
+ added: List[Any] = []
716
+ deleted: List[Any] = []
717
+
718
+ has_history = False
719
+ for prop in self.props:
720
+ key = prop.key
721
+ hist = state.manager[key].impl.get_history(state, dict_)
722
+ if hist.has_changes():
723
+ has_history = True
724
+
725
+ non_deleted = hist.non_deleted()
726
+ if non_deleted:
727
+ added.extend(non_deleted)
728
+ else:
729
+ added.append(None)
730
+ if hist.deleted:
731
+ deleted.extend(hist.deleted)
732
+ else:
733
+ deleted.append(None)
734
+
735
+ if has_history:
736
+ return attributes.History(
737
+ [self._construct_composite(*added)],
738
+ (),
739
+ [self._construct_composite(*deleted)],
740
+ )
741
+ else:
742
+ return attributes.History(
743
+ (), [self._construct_composite(*added)], ()
744
+ )
745
+
746
+ def _comparator_factory(
747
+ self, mapper: Mapper[Any]
748
+ ) -> Composite.Comparator[_CC]:
749
+ return self.comparator_factory(self, mapper)
750
+
751
+ class CompositeBundle(orm_util.Bundle[_T]):
752
+ def __init__(
753
+ self,
754
+ property_: Composite[_T],
755
+ expr: ClauseList,
756
+ ):
757
+ self.property = property_
758
+ super().__init__(property_.key, *expr)
759
+
760
+ def create_row_processor(
761
+ self,
762
+ query: Select[Unpack[TupleAny]],
763
+ procs: Sequence[Callable[[Row[Unpack[TupleAny]]], Any]],
764
+ labels: Sequence[str],
765
+ ) -> Callable[[Row[Unpack[TupleAny]]], Any]:
766
+ def proc(row: Row[Unpack[TupleAny]]) -> Any:
767
+ return self.property._construct_composite(
768
+ *[proc(row) for proc in procs]
769
+ )
770
+
771
+ return proc
772
+
773
+ class Comparator(PropComparator[_PT]):
774
+ """Produce boolean, comparison, and other operators for
775
+ :class:`.Composite` attributes.
776
+
777
+ See the example in :ref:`composite_operations` for an overview
778
+ of usage , as well as the documentation for :class:`.PropComparator`.
779
+
780
+ .. seealso::
781
+
782
+ :class:`.PropComparator`
783
+
784
+ :class:`.ColumnOperators`
785
+
786
+ :ref:`types_operators`
787
+
788
+ :attr:`.TypeEngine.comparator_factory`
789
+
790
+ """
791
+
792
+ # https://github.com/python/mypy/issues/4266
793
+ __hash__ = None # type: ignore
794
+
795
+ prop: RODescriptorReference[Composite[_PT]]
796
+
797
+ @util.memoized_property
798
+ def clauses(self) -> ClauseList:
799
+ return expression.ClauseList(
800
+ group=False, *self._comparable_elements
801
+ )
802
+
803
+ def __clause_element__(self) -> CompositeProperty.CompositeBundle[_PT]:
804
+ return self.expression
805
+
806
+ @util.memoized_property
807
+ def expression(self) -> CompositeProperty.CompositeBundle[_PT]:
808
+ clauses = self.clauses._annotate(
809
+ {
810
+ "parententity": self._parententity,
811
+ "parentmapper": self._parententity,
812
+ "proxy_key": self.prop.key,
813
+ }
814
+ )
815
+ return CompositeProperty.CompositeBundle(self.prop, clauses)
816
+
817
+ def _bulk_update_tuples(
818
+ self, value: Any
819
+ ) -> Sequence[Tuple[_DMLColumnArgument, Any]]:
820
+ if isinstance(value, BindParameter):
821
+ value = value.value
822
+
823
+ values: Sequence[Any]
824
+
825
+ if value is None:
826
+ values = [None for key in self.prop._attribute_keys]
827
+ elif isinstance(self.prop.composite_class, type) and isinstance(
828
+ value, self.prop.composite_class
829
+ ):
830
+ values = self.prop._composite_values_from_instance(
831
+ value # type: ignore[arg-type]
832
+ )
833
+ else:
834
+ raise sa_exc.ArgumentError(
835
+ "Can't UPDATE composite attribute %s to %r"
836
+ % (self.prop, value)
837
+ )
838
+
839
+ return list(zip(self._comparable_elements, values))
840
+
841
+ def _bulk_dml_setter(self, key: str) -> Optional[Callable[..., Any]]:
842
+ return self.prop._populate_composite_bulk_save_mappings_fn()
843
+
844
+ @util.memoized_property
845
+ def _comparable_elements(self) -> Sequence[QueryableAttribute[Any]]:
846
+ if self._adapt_to_entity:
847
+ return [
848
+ getattr(self._adapt_to_entity.entity, prop.key)
849
+ for prop in self.prop._comparable_elements
850
+ ]
851
+ else:
852
+ return self.prop._comparable_elements
853
+
854
+ def __eq__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
855
+ return self._compare(operators.eq, other)
856
+
857
+ def __ne__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
858
+ return self._compare(operators.ne, other)
859
+
860
+ def __lt__(self, other: Any) -> ColumnElement[bool]:
861
+ return self._compare(operators.lt, other)
862
+
863
+ def __gt__(self, other: Any) -> ColumnElement[bool]:
864
+ return self._compare(operators.gt, other)
865
+
866
+ def __le__(self, other: Any) -> ColumnElement[bool]:
867
+ return self._compare(operators.le, other)
868
+
869
+ def __ge__(self, other: Any) -> ColumnElement[bool]:
870
+ return self._compare(operators.ge, other)
871
+
872
+ def desc(self) -> operators.OrderingOperators: # type: ignore[override] # noqa: E501
873
+ return expression.OrderByList(
874
+ [e.desc() for e in self._comparable_elements]
875
+ )
876
+
877
+ def asc(self) -> operators.OrderingOperators: # type: ignore[override] # noqa: E501
878
+ return expression.OrderByList(
879
+ [e.asc() for e in self._comparable_elements]
880
+ )
881
+
882
+ def nulls_first(self) -> operators.OrderingOperators: # type: ignore[override] # noqa: E501
883
+ return expression.OrderByList(
884
+ [e.nulls_first() for e in self._comparable_elements]
885
+ )
886
+
887
+ def nulls_last(self) -> operators.OrderingOperators: # type: ignore[override] # noqa: E501
888
+ return expression.OrderByList(
889
+ [e.nulls_last() for e in self._comparable_elements]
890
+ )
891
+
892
+ # what might be interesting would be if we create
893
+ # an instance of the composite class itself with
894
+ # the columns as data members, then use "hybrid style" comparison
895
+ # to create these comparisons. then your Point.__eq__() method could
896
+ # be where comparison behavior is defined for SQL also. Likely
897
+ # not a good choice for default behavior though, not clear how it would
898
+ # work w/ dataclasses, etc. also no demand for any of this anyway.
899
+ def _compare(
900
+ self, operator: OperatorType, other: Any
901
+ ) -> ColumnElement[bool]:
902
+ values: Sequence[Any]
903
+ if other is None:
904
+ values = [None] * len(self.prop._comparable_elements)
905
+ else:
906
+ values = self.prop._composite_values_from_instance(other)
907
+ comparisons = [
908
+ operator(a, b)
909
+ for a, b in zip(self.prop._comparable_elements, values)
910
+ ]
911
+ if self._adapt_to_entity:
912
+ assert self.adapter is not None
913
+ comparisons = [self.adapter(x) for x in comparisons]
914
+ return sql.and_(*comparisons)
915
+
916
+ def __str__(self) -> str:
917
+ return str(self.parent.class_.__name__) + "." + self.key
918
+
919
+
920
+ class Composite(CompositeProperty[_T], _DeclarativeMapped[_T]):
921
+ """Declarative-compatible front-end for the :class:`.CompositeProperty`
922
+ class.
923
+
924
+ Public constructor is the :func:`_orm.composite` function.
925
+
926
+ .. versionchanged:: 2.0 Added :class:`_orm.Composite` as a Declarative
927
+ compatible subclass of :class:`_orm.CompositeProperty`.
928
+
929
+ .. seealso::
930
+
931
+ :ref:`mapper_composite`
932
+
933
+ """
934
+
935
+ inherit_cache = True
936
+ """:meta private:"""
937
+
938
+
939
+ class ConcreteInheritedProperty(DescriptorProperty[_T]):
940
+ """A 'do nothing' :class:`.MapperProperty` that disables
941
+ an attribute on a concrete subclass that is only present
942
+ on the inherited mapper, not the concrete classes' mapper.
943
+
944
+ Cases where this occurs include:
945
+
946
+ * When the superclass mapper is mapped against a
947
+ "polymorphic union", which includes all attributes from
948
+ all subclasses.
949
+ * When a relationship() is configured on an inherited mapper,
950
+ but not on the subclass mapper. Concrete mappers require
951
+ that relationship() is configured explicitly on each
952
+ subclass.
953
+
954
+ """
955
+
956
+ def _comparator_factory(
957
+ self, mapper: Mapper[Any]
958
+ ) -> Type[PropComparator[_T]]:
959
+ comparator_callable = None
960
+
961
+ for m in self.parent.iterate_to_root():
962
+ p = m._props[self.key]
963
+ if getattr(p, "comparator_factory", None) is not None:
964
+ comparator_callable = p.comparator_factory
965
+ break
966
+ assert comparator_callable is not None
967
+ return comparator_callable(p, mapper) # type: ignore
968
+
969
+ def __init__(self) -> None:
970
+ super().__init__()
971
+
972
+ def warn() -> NoReturn:
973
+ raise AttributeError(
974
+ "Concrete %s does not implement "
975
+ "attribute %r at the instance level. Add "
976
+ "this property explicitly to %s."
977
+ % (self.parent, self.key, self.parent)
978
+ )
979
+
980
+ class NoninheritedConcreteProp:
981
+ def __set__(s: Any, obj: Any, value: Any) -> NoReturn:
982
+ warn()
983
+
984
+ def __delete__(s: Any, obj: Any) -> NoReturn:
985
+ warn()
986
+
987
+ def __get__(s: Any, obj: Any, owner: Any) -> Any:
988
+ if obj is None:
989
+ return self.descriptor
990
+ warn()
991
+
992
+ self.descriptor = NoninheritedConcreteProp()
993
+
994
+
995
+ class SynonymProperty(DescriptorProperty[_T]):
996
+ """Denote an attribute name as a synonym to a mapped property,
997
+ in that the attribute will mirror the value and expression behavior
998
+ of another attribute.
999
+
1000
+ :class:`.Synonym` is constructed using the :func:`_orm.synonym`
1001
+ function.
1002
+
1003
+ .. seealso::
1004
+
1005
+ :ref:`synonyms` - Overview of synonyms
1006
+
1007
+ """
1008
+
1009
+ comparator_factory: Optional[Type[PropComparator[_T]]]
1010
+
1011
+ def __init__(
1012
+ self,
1013
+ name: str,
1014
+ map_column: Optional[bool] = None,
1015
+ descriptor: Optional[Any] = None,
1016
+ comparator_factory: Optional[Type[PropComparator[_T]]] = None,
1017
+ attribute_options: Optional[_AttributeOptions] = None,
1018
+ info: Optional[_InfoType] = None,
1019
+ doc: Optional[str] = None,
1020
+ ):
1021
+ super().__init__(attribute_options=attribute_options)
1022
+
1023
+ self.name = name
1024
+ self.map_column = map_column
1025
+ self.descriptor = descriptor
1026
+ self.comparator_factory = comparator_factory
1027
+ if doc:
1028
+ self.doc = doc
1029
+ elif descriptor and descriptor.__doc__:
1030
+ self.doc = descriptor.__doc__
1031
+ else:
1032
+ self.doc = None
1033
+ if info:
1034
+ self.info.update(info)
1035
+
1036
+ util.set_creation_order(self)
1037
+
1038
+ if not TYPE_CHECKING:
1039
+
1040
+ @property
1041
+ def uses_objects(self) -> bool:
1042
+ return getattr(self.parent.class_, self.name).impl.uses_objects
1043
+
1044
+ # TODO: when initialized, check _proxied_object,
1045
+ # emit a warning if its not a column-based property
1046
+
1047
+ @util.memoized_property
1048
+ def _proxied_object(
1049
+ self,
1050
+ ) -> Union[MapperProperty[_T], SQLORMOperations[_T]]:
1051
+ attr = getattr(self.parent.class_, self.name)
1052
+ if not hasattr(attr, "property") or not isinstance(
1053
+ attr.property, MapperProperty
1054
+ ):
1055
+ # attribute is a non-MapperProprerty proxy such as
1056
+ # hybrid or association proxy
1057
+ if isinstance(attr, attributes.QueryableAttribute):
1058
+ return attr.comparator
1059
+ elif isinstance(attr, SQLORMOperations):
1060
+ # association proxy comes here
1061
+ return attr
1062
+
1063
+ raise sa_exc.InvalidRequestError(
1064
+ """synonym() attribute "%s.%s" only supports """
1065
+ """ORM mapped attributes, got %r"""
1066
+ % (self.parent.class_.__name__, self.name, attr)
1067
+ )
1068
+ return attr.property
1069
+
1070
+ def _column_strategy_attrs(self) -> Sequence[QueryableAttribute[Any]]:
1071
+ return (getattr(self.parent.class_, self.name),)
1072
+
1073
+ def _comparator_factory(self, mapper: Mapper[Any]) -> SQLORMOperations[_T]:
1074
+ prop = self._proxied_object
1075
+
1076
+ if isinstance(prop, MapperProperty):
1077
+ if self.comparator_factory:
1078
+ comp = self.comparator_factory(prop, mapper)
1079
+ else:
1080
+ comp = prop.comparator_factory(prop, mapper)
1081
+ return comp
1082
+ else:
1083
+ return prop
1084
+
1085
+ def get_history(
1086
+ self,
1087
+ state: InstanceState[Any],
1088
+ dict_: _InstanceDict,
1089
+ passive: PassiveFlag = PassiveFlag.PASSIVE_OFF,
1090
+ ) -> History:
1091
+ attr: QueryableAttribute[Any] = getattr(self.parent.class_, self.name)
1092
+ return attr.impl.get_history(state, dict_, passive=passive)
1093
+
1094
+ def _get_dataclass_setup_options(
1095
+ self,
1096
+ decl_scan: _ClassScanAbstractConfig,
1097
+ key: str,
1098
+ dataclass_setup_arguments: _DataclassArguments,
1099
+ enable_descriptor_defaults: bool,
1100
+ ) -> _AttributeOptions:
1101
+ dataclasses_default = self._attribute_options.dataclasses_default
1102
+ if (
1103
+ dataclasses_default is not _NoArg.NO_ARG
1104
+ and not callable(dataclasses_default)
1105
+ and enable_descriptor_defaults
1106
+ and not getattr(
1107
+ decl_scan.cls, "_sa_disable_descriptor_defaults", False
1108
+ )
1109
+ ):
1110
+ proxied = decl_scan.collected_attributes[self.name]
1111
+ proxied_default = proxied._attribute_options.dataclasses_default
1112
+ if proxied_default != dataclasses_default:
1113
+ raise sa_exc.ArgumentError(
1114
+ f"Synonym {key!r} default argument "
1115
+ f"{dataclasses_default!r} must match the dataclasses "
1116
+ f"default value of proxied object {self.name!r}, "
1117
+ f"""currently {
1118
+ repr(proxied_default)
1119
+ if proxied_default is not _NoArg.NO_ARG
1120
+ else 'not set'}"""
1121
+ )
1122
+ self._default_scalar_value = dataclasses_default
1123
+ return self._attribute_options._replace(
1124
+ dataclasses_default=DONT_SET
1125
+ )
1126
+
1127
+ return self._attribute_options
1128
+
1129
+ @util.preload_module("sqlalchemy.orm.properties")
1130
+ def set_parent(self, parent: Mapper[Any], init: bool) -> None:
1131
+ properties = util.preloaded.orm_properties
1132
+
1133
+ if self.map_column:
1134
+ # implement the 'map_column' option.
1135
+ if self.key not in parent.persist_selectable.c:
1136
+ raise sa_exc.ArgumentError(
1137
+ "Can't compile synonym '%s': no column on table "
1138
+ "'%s' named '%s'"
1139
+ % (
1140
+ self.name,
1141
+ parent.persist_selectable.description,
1142
+ self.key,
1143
+ )
1144
+ )
1145
+ elif (
1146
+ parent.persist_selectable.c[self.key]
1147
+ in parent._columntoproperty
1148
+ and parent._columntoproperty[
1149
+ parent.persist_selectable.c[self.key]
1150
+ ].key
1151
+ == self.name
1152
+ ):
1153
+ raise sa_exc.ArgumentError(
1154
+ "Can't call map_column=True for synonym %r=%r, "
1155
+ "a ColumnProperty already exists keyed to the name "
1156
+ "%r for column %r"
1157
+ % (self.key, self.name, self.name, self.key)
1158
+ )
1159
+ p: ColumnProperty[Any] = properties.ColumnProperty(
1160
+ parent.persist_selectable.c[self.key]
1161
+ )
1162
+ parent._configure_property(self.name, p, init=init, setparent=True)
1163
+ p._mapped_by_synonym = self.key
1164
+
1165
+ self.parent = parent
1166
+
1167
+
1168
+ class Synonym(SynonymProperty[_T], _DeclarativeMapped[_T]):
1169
+ """Declarative front-end for the :class:`.SynonymProperty` class.
1170
+
1171
+ Public constructor is the :func:`_orm.synonym` function.
1172
+
1173
+ .. versionchanged:: 2.0 Added :class:`_orm.Synonym` as a Declarative
1174
+ compatible subclass for :class:`_orm.SynonymProperty`
1175
+
1176
+ .. seealso::
1177
+
1178
+ :ref:`synonyms` - Overview of synonyms
1179
+
1180
+ """
1181
+
1182
+ inherit_cache = True
1183
+ """:meta private:"""