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,2072 @@
1
+ # ext/associationproxy.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
+ """Contain the ``AssociationProxy`` class.
9
+
10
+ The ``AssociationProxy`` is a Python property object which provides
11
+ transparent proxied access to the endpoint of an association object.
12
+
13
+ See the example ``examples/association/proxied_association.py``.
14
+
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import operator
19
+ import typing
20
+ from typing import AbstractSet
21
+ from typing import Any
22
+ from typing import Callable
23
+ from typing import cast
24
+ from typing import Collection
25
+ from typing import Dict
26
+ from typing import Generic
27
+ from typing import ItemsView
28
+ from typing import Iterable
29
+ from typing import Iterator
30
+ from typing import KeysView
31
+ from typing import List
32
+ from typing import Literal
33
+ from typing import Mapping
34
+ from typing import MutableMapping
35
+ from typing import MutableSequence
36
+ from typing import MutableSet
37
+ from typing import NoReturn
38
+ from typing import Optional
39
+ from typing import overload
40
+ from typing import Protocol
41
+ from typing import Set
42
+ from typing import SupportsIndex
43
+ from typing import Tuple
44
+ from typing import Type
45
+ from typing import TypeVar
46
+ from typing import Union
47
+ from typing import ValuesView
48
+
49
+ from .. import ColumnElement
50
+ from .. import exc
51
+ from .. import inspect
52
+ from .. import orm
53
+ from .. import util
54
+ from ..orm import collections
55
+ from ..orm import InspectionAttrExtensionType
56
+ from ..orm import interfaces
57
+ from ..orm import ORMDescriptor
58
+ from ..orm.base import SQLORMOperations
59
+ from ..orm.interfaces import _AttributeOptions
60
+ from ..orm.interfaces import _DCAttributeOptions
61
+ from ..orm.interfaces import _DEFAULT_ATTRIBUTE_OPTIONS
62
+ from ..sql import operators
63
+ from ..sql import or_
64
+ from ..sql.base import _NoArg
65
+ from ..util.typing import Self
66
+ from ..util.typing import SupportsKeysAndGetItem
67
+
68
+ if typing.TYPE_CHECKING:
69
+ from ..orm.interfaces import MapperProperty
70
+ from ..orm.interfaces import PropComparator
71
+ from ..orm.mapper import Mapper
72
+ from ..orm.util import AliasedInsp
73
+ from ..sql._typing import _ColumnExpressionArgument
74
+ from ..sql._typing import _InfoType
75
+
76
+
77
+ _T = TypeVar("_T", bound=Any)
78
+ _T_co = TypeVar("_T_co", bound=Any, covariant=True)
79
+ _T_con = TypeVar("_T_con", bound=Any, contravariant=True)
80
+ _S = TypeVar("_S", bound=Any)
81
+ _KT = TypeVar("_KT", bound=Any)
82
+ _VT = TypeVar("_VT", bound=Any)
83
+
84
+
85
+ def association_proxy(
86
+ target_collection: str,
87
+ attr: str,
88
+ *,
89
+ creator: Optional[_CreatorProtocol] = None,
90
+ getset_factory: Optional[_GetSetFactoryProtocol] = None,
91
+ proxy_factory: Optional[_ProxyFactoryProtocol] = None,
92
+ proxy_bulk_set: Optional[_ProxyBulkSetProtocol] = None,
93
+ info: Optional[_InfoType] = None,
94
+ cascade_scalar_deletes: bool = False,
95
+ create_on_none_assignment: bool = False,
96
+ init: Union[_NoArg, bool] = _NoArg.NO_ARG,
97
+ repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
98
+ default: Optional[Any] = _NoArg.NO_ARG,
99
+ default_factory: Union[_NoArg, Callable[[], _T]] = _NoArg.NO_ARG,
100
+ compare: Union[_NoArg, bool] = _NoArg.NO_ARG,
101
+ kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG,
102
+ hash: Union[_NoArg, bool, None] = _NoArg.NO_ARG, # noqa: A002
103
+ dataclass_metadata: Union[_NoArg, Mapping[Any, Any], None] = _NoArg.NO_ARG,
104
+ ) -> AssociationProxy[Any]:
105
+ r"""Return a Python property implementing a view of a target
106
+ attribute which references an attribute on members of the
107
+ target.
108
+
109
+ The returned value is an instance of :class:`.AssociationProxy`.
110
+
111
+ Implements a Python property representing a relationship as a collection
112
+ of simpler values, or a scalar value. The proxied property will mimic
113
+ the collection type of the target (list, dict or set), or, in the case of
114
+ a one to one relationship, a simple scalar value.
115
+
116
+ :param target_collection: Name of the attribute that is the immediate
117
+ target. This attribute is typically mapped by
118
+ :func:`~sqlalchemy.orm.relationship` to link to a target collection, but
119
+ can also be a many-to-one or non-scalar relationship.
120
+
121
+ :param attr: Attribute on the associated instance or instances that
122
+ are available on instances of the target object.
123
+
124
+ :param creator: optional.
125
+
126
+ Defines custom behavior when new items are added to the proxied
127
+ collection.
128
+
129
+ By default, adding new items to the collection will trigger a
130
+ construction of an instance of the target object, passing the given
131
+ item as a positional argument to the target constructor. For cases
132
+ where this isn't sufficient, :paramref:`.association_proxy.creator`
133
+ can supply a callable that will construct the object in the
134
+ appropriate way, given the item that was passed.
135
+
136
+ For list- and set- oriented collections, a single argument is
137
+ passed to the callable. For dictionary oriented collections, two
138
+ arguments are passed, corresponding to the key and value.
139
+
140
+ The :paramref:`.association_proxy.creator` callable is also invoked
141
+ for scalar (i.e. many-to-one, one-to-one) relationships. If the
142
+ current value of the target relationship attribute is ``None``, the
143
+ callable is used to construct a new object. If an object value already
144
+ exists, the given attribute value is populated onto that object.
145
+
146
+ .. seealso::
147
+
148
+ :ref:`associationproxy_creator`
149
+
150
+ :param cascade_scalar_deletes: when True, indicates that setting
151
+ the proxied value to ``None``, or deleting it via ``del``, should
152
+ also remove the source object. Only applies to scalar attributes.
153
+ Normally, removing the proxied target will not remove the proxy
154
+ source, as this object may have other state that is still to be
155
+ kept.
156
+
157
+ .. seealso::
158
+
159
+ :ref:`cascade_scalar_deletes` - complete usage example
160
+
161
+ :param create_on_none_assignment: when True, indicates that setting
162
+ the proxied value to ``None`` should **create** the source object
163
+ if it does not exist, using the creator. Only applies to scalar
164
+ attributes. This is mutually exclusive
165
+ vs. the :paramref:`.association_proxy.cascade_scalar_deletes`.
166
+
167
+ .. versionadded:: 2.0.18
168
+
169
+ :param init: Specific to :ref:`orm_declarative_native_dataclasses`,
170
+ specifies if the mapped attribute should be part of the ``__init__()``
171
+ method as generated by the dataclass process.
172
+
173
+ .. versionadded:: 2.0.0b4
174
+
175
+ :param repr: Specific to :ref:`orm_declarative_native_dataclasses`,
176
+ specifies if the attribute established by this :class:`.AssociationProxy`
177
+ should be part of the ``__repr__()`` method as generated by the dataclass
178
+ process.
179
+
180
+ .. versionadded:: 2.0.0b4
181
+
182
+ :param default_factory: Specific to
183
+ :ref:`orm_declarative_native_dataclasses`, specifies a default-value
184
+ generation function that will take place as part of the ``__init__()``
185
+ method as generated by the dataclass process.
186
+
187
+ .. versionadded:: 2.0.0b4
188
+
189
+ :param compare: Specific to
190
+ :ref:`orm_declarative_native_dataclasses`, indicates if this field
191
+ should be included in comparison operations when generating the
192
+ ``__eq__()`` and ``__ne__()`` methods for the mapped class.
193
+
194
+ .. versionadded:: 2.0.0b4
195
+
196
+ :param kw_only: Specific to :ref:`orm_declarative_native_dataclasses`,
197
+ indicates if this field should be marked as keyword-only when generating
198
+ the ``__init__()`` method as generated by the dataclass process.
199
+
200
+ .. versionadded:: 2.0.0b4
201
+
202
+ :param hash: Specific to
203
+ :ref:`orm_declarative_native_dataclasses`, controls if this field
204
+ is included when generating the ``__hash__()`` method for the mapped
205
+ class.
206
+
207
+ .. versionadded:: 2.0.36
208
+
209
+ :param dataclass_metadata: Specific to
210
+ :ref:`orm_declarative_native_dataclasses`, supplies metadata
211
+ to be attached to the generated dataclass field.
212
+
213
+ .. versionadded:: 2.0.42
214
+
215
+ :param info: optional, will be assigned to
216
+ :attr:`.AssociationProxy.info` if present.
217
+
218
+
219
+ The following additional parameters involve injection of custom behaviors
220
+ within the :class:`.AssociationProxy` object and are for advanced use
221
+ only:
222
+
223
+ :param getset_factory: Optional. Proxied attribute access is
224
+ automatically handled by routines that get and set values based on
225
+ the `attr` argument for this proxy.
226
+
227
+ If you would like to customize this behavior, you may supply a
228
+ `getset_factory` callable that produces a tuple of `getter` and
229
+ `setter` functions. The factory is called with two arguments, the
230
+ abstract type of the underlying collection and this proxy instance.
231
+
232
+ :param proxy_factory: Optional. The type of collection to emulate is
233
+ determined by sniffing the target collection. If your collection
234
+ type can't be determined by duck typing or you'd like to use a
235
+ different collection implementation, you may supply a factory
236
+ function to produce those collections. Only applicable to
237
+ non-scalar relationships.
238
+
239
+ :param proxy_bulk_set: Optional, use with proxy_factory.
240
+
241
+
242
+ """
243
+ return AssociationProxy(
244
+ target_collection,
245
+ attr,
246
+ creator=creator,
247
+ getset_factory=getset_factory,
248
+ proxy_factory=proxy_factory,
249
+ proxy_bulk_set=proxy_bulk_set,
250
+ info=info,
251
+ cascade_scalar_deletes=cascade_scalar_deletes,
252
+ create_on_none_assignment=create_on_none_assignment,
253
+ attribute_options=_AttributeOptions(
254
+ init,
255
+ repr,
256
+ default,
257
+ default_factory,
258
+ compare,
259
+ kw_only,
260
+ hash,
261
+ dataclass_metadata,
262
+ ),
263
+ )
264
+
265
+
266
+ class AssociationProxyExtensionType(InspectionAttrExtensionType):
267
+ ASSOCIATION_PROXY = "ASSOCIATION_PROXY"
268
+ """Symbol indicating an :class:`.InspectionAttr` that's
269
+ of type :class:`.AssociationProxy`.
270
+
271
+ Is assigned to the :attr:`.InspectionAttr.extension_type`
272
+ attribute.
273
+
274
+ """
275
+
276
+
277
+ class _GetterProtocol(Protocol[_T_co]):
278
+ def __call__(self, instance: Any) -> _T_co: ...
279
+
280
+
281
+ # mypy 0.990 we are no longer allowed to make this Protocol[_T_con]
282
+ class _SetterProtocol(Protocol): ...
283
+
284
+
285
+ class _PlainSetterProtocol(_SetterProtocol, Protocol[_T_con]):
286
+ def __call__(self, instance: Any, value: _T_con) -> None: ...
287
+
288
+
289
+ class _DictSetterProtocol(_SetterProtocol, Protocol[_T_con]):
290
+ def __call__(self, instance: Any, key: Any, value: _T_con) -> None: ...
291
+
292
+
293
+ # mypy 0.990 we are no longer allowed to make this Protocol[_T_con]
294
+ class _CreatorProtocol(Protocol): ...
295
+
296
+
297
+ class _PlainCreatorProtocol(_CreatorProtocol, Protocol[_T_con]):
298
+ def __call__(self, value: _T_con) -> Any: ...
299
+
300
+
301
+ class _KeyCreatorProtocol(_CreatorProtocol, Protocol[_T_con]):
302
+ def __call__(self, key: Any, value: Optional[_T_con]) -> Any: ...
303
+
304
+
305
+ class _LazyCollectionProtocol(Protocol[_T]):
306
+ def __call__(
307
+ self,
308
+ ) -> Union[
309
+ MutableSet[_T], MutableMapping[Any, _T], MutableSequence[_T]
310
+ ]: ...
311
+
312
+
313
+ class _GetSetFactoryProtocol(Protocol):
314
+ def __call__(
315
+ self,
316
+ collection_class: Optional[Type[Any]],
317
+ assoc_instance: AssociationProxyInstance[Any],
318
+ ) -> Tuple[_GetterProtocol[Any], _SetterProtocol]: ...
319
+
320
+
321
+ class _ProxyFactoryProtocol(Protocol):
322
+ def __call__(
323
+ self,
324
+ lazy_collection: _LazyCollectionProtocol[Any],
325
+ creator: _CreatorProtocol,
326
+ value_attr: str,
327
+ parent: AssociationProxyInstance[Any],
328
+ ) -> Any: ...
329
+
330
+
331
+ class _ProxyBulkSetProtocol(Protocol):
332
+ def __call__(
333
+ self, proxy: _AssociationCollection[Any], collection: Iterable[Any]
334
+ ) -> None: ...
335
+
336
+
337
+ class _AssociationProxyProtocol(Protocol[_T]):
338
+ """describes the interface of :class:`.AssociationProxy`
339
+ without including descriptor methods in the interface."""
340
+
341
+ creator: Optional[_CreatorProtocol]
342
+ key: str
343
+ target_collection: str
344
+ value_attr: str
345
+ cascade_scalar_deletes: bool
346
+ create_on_none_assignment: bool
347
+ getset_factory: Optional[_GetSetFactoryProtocol]
348
+ proxy_factory: Optional[_ProxyFactoryProtocol]
349
+ proxy_bulk_set: Optional[_ProxyBulkSetProtocol]
350
+
351
+ @util.ro_memoized_property
352
+ def info(self) -> _InfoType: ...
353
+
354
+ def for_class(
355
+ self, class_: Type[Any], obj: Optional[object] = None
356
+ ) -> AssociationProxyInstance[_T]: ...
357
+
358
+ def _default_getset(
359
+ self, collection_class: Any
360
+ ) -> Tuple[_GetterProtocol[Any], _SetterProtocol]: ...
361
+
362
+
363
+ class AssociationProxy(
364
+ interfaces.InspectionAttrInfo,
365
+ ORMDescriptor[_T],
366
+ _DCAttributeOptions,
367
+ _AssociationProxyProtocol[_T],
368
+ ):
369
+ """A descriptor that presents a read/write view of an object attribute."""
370
+
371
+ is_attribute = True
372
+ extension_type = AssociationProxyExtensionType.ASSOCIATION_PROXY
373
+
374
+ def __init__(
375
+ self,
376
+ target_collection: str,
377
+ attr: str,
378
+ *,
379
+ creator: Optional[_CreatorProtocol] = None,
380
+ getset_factory: Optional[_GetSetFactoryProtocol] = None,
381
+ proxy_factory: Optional[_ProxyFactoryProtocol] = None,
382
+ proxy_bulk_set: Optional[_ProxyBulkSetProtocol] = None,
383
+ info: Optional[_InfoType] = None,
384
+ cascade_scalar_deletes: bool = False,
385
+ create_on_none_assignment: bool = False,
386
+ attribute_options: Optional[_AttributeOptions] = None,
387
+ ):
388
+ """Construct a new :class:`.AssociationProxy`.
389
+
390
+ The :class:`.AssociationProxy` object is typically constructed using
391
+ the :func:`.association_proxy` constructor function. See the
392
+ description of :func:`.association_proxy` for a description of all
393
+ parameters.
394
+
395
+
396
+ """
397
+ self.target_collection = target_collection
398
+ self.value_attr = attr
399
+ self.creator = creator
400
+ self.getset_factory = getset_factory
401
+ self.proxy_factory = proxy_factory
402
+ self.proxy_bulk_set = proxy_bulk_set
403
+
404
+ if cascade_scalar_deletes and create_on_none_assignment:
405
+ raise exc.ArgumentError(
406
+ "The cascade_scalar_deletes and create_on_none_assignment "
407
+ "parameters are mutually exclusive."
408
+ )
409
+ self.cascade_scalar_deletes = cascade_scalar_deletes
410
+ self.create_on_none_assignment = create_on_none_assignment
411
+
412
+ self.key = "_%s_%s_%s" % (
413
+ type(self).__name__,
414
+ target_collection,
415
+ id(self),
416
+ )
417
+ if info:
418
+ self.info = info # type: ignore
419
+
420
+ if (
421
+ attribute_options
422
+ and attribute_options != _DEFAULT_ATTRIBUTE_OPTIONS
423
+ ):
424
+ self._has_dataclass_arguments = True
425
+ self._attribute_options = attribute_options
426
+ else:
427
+ self._has_dataclass_arguments = False
428
+ self._attribute_options = _DEFAULT_ATTRIBUTE_OPTIONS
429
+
430
+ @overload
431
+ def __get__(
432
+ self, instance: Literal[None], owner: Literal[None]
433
+ ) -> Self: ...
434
+
435
+ @overload
436
+ def __get__(
437
+ self, instance: Literal[None], owner: Any
438
+ ) -> AssociationProxyInstance[_T]: ...
439
+
440
+ @overload
441
+ def __get__(self, instance: object, owner: Any) -> _T: ...
442
+
443
+ def __get__(
444
+ self, instance: object, owner: Any
445
+ ) -> Union[AssociationProxyInstance[_T], _T, AssociationProxy[_T]]:
446
+ if owner is None:
447
+ return self
448
+ inst = self._as_instance(owner, instance)
449
+ if inst:
450
+ return inst.get(instance)
451
+
452
+ assert instance is None
453
+
454
+ return self
455
+
456
+ def __set__(self, instance: object, values: _T) -> None:
457
+ class_ = type(instance)
458
+ self._as_instance(class_, instance).set(instance, values)
459
+
460
+ def __delete__(self, instance: object) -> None:
461
+ class_ = type(instance)
462
+ self._as_instance(class_, instance).delete(instance)
463
+
464
+ def for_class(
465
+ self, class_: Type[Any], obj: Optional[object] = None
466
+ ) -> AssociationProxyInstance[_T]:
467
+ r"""Return the internal state local to a specific mapped class.
468
+
469
+ E.g., given a class ``User``::
470
+
471
+ class User(Base):
472
+ # ...
473
+
474
+ keywords = association_proxy("kws", "keyword")
475
+
476
+ If we access this :class:`.AssociationProxy` from
477
+ :attr:`_orm.Mapper.all_orm_descriptors`, and we want to view the
478
+ target class for this proxy as mapped by ``User``::
479
+
480
+ inspect(User).all_orm_descriptors["keywords"].for_class(User).target_class
481
+
482
+ This returns an instance of :class:`.AssociationProxyInstance` that
483
+ is specific to the ``User`` class. The :class:`.AssociationProxy`
484
+ object remains agnostic of its parent class.
485
+
486
+ :param class\_: the class that we are returning state for.
487
+
488
+ :param obj: optional, an instance of the class that is required
489
+ if the attribute refers to a polymorphic target, e.g. where we have
490
+ to look at the type of the actual destination object to get the
491
+ complete path.
492
+
493
+ """
494
+ return self._as_instance(class_, obj)
495
+
496
+ def _as_instance(
497
+ self, class_: Any, obj: Any
498
+ ) -> AssociationProxyInstance[_T]:
499
+ try:
500
+ inst = class_.__dict__[self.key + "_inst"]
501
+ except KeyError:
502
+ inst = None
503
+
504
+ # avoid exception context
505
+ if inst is None:
506
+ owner = self._calc_owner(class_)
507
+ if owner is not None:
508
+ inst = AssociationProxyInstance.for_proxy(self, owner, obj)
509
+ setattr(class_, self.key + "_inst", inst)
510
+ else:
511
+ inst = None
512
+
513
+ if inst is not None and not inst._is_canonical:
514
+ # the AssociationProxyInstance can't be generalized
515
+ # since the proxied attribute is not on the targeted
516
+ # class, only on subclasses of it, which might be
517
+ # different. only return for the specific
518
+ # object's current value
519
+ return inst._non_canonical_get_for_object(obj) # type: ignore
520
+ else:
521
+ return inst # type: ignore # TODO
522
+
523
+ def _calc_owner(self, target_cls: Any) -> Any:
524
+ # we might be getting invoked for a subclass
525
+ # that is not mapped yet, in some declarative situations.
526
+ # save until we are mapped
527
+ try:
528
+ insp = inspect(target_cls)
529
+ except exc.NoInspectionAvailable:
530
+ # can't find a mapper, don't set owner. if we are a not-yet-mapped
531
+ # subclass, we can also scan through __mro__ to find a mapped
532
+ # class, but instead just wait for us to be called again against a
533
+ # mapped class normally.
534
+ return None
535
+ else:
536
+ return insp.mapper.class_manager.class_
537
+
538
+ def _default_getset(
539
+ self, collection_class: Any
540
+ ) -> Tuple[_GetterProtocol[Any], _SetterProtocol]:
541
+ attr = self.value_attr
542
+ _getter = operator.attrgetter(attr)
543
+
544
+ def getter(instance: Any) -> Optional[Any]:
545
+ return _getter(instance) if instance is not None else None
546
+
547
+ if collection_class is dict:
548
+
549
+ def dict_setter(instance: Any, k: Any, value: Any) -> None:
550
+ setattr(instance, attr, value)
551
+
552
+ return getter, dict_setter
553
+
554
+ else:
555
+
556
+ def plain_setter(o: Any, v: Any) -> None:
557
+ setattr(o, attr, v)
558
+
559
+ return getter, plain_setter
560
+
561
+ def __repr__(self) -> str:
562
+ return "AssociationProxy(%r, %r)" % (
563
+ self.target_collection,
564
+ self.value_attr,
565
+ )
566
+
567
+
568
+ # the pep-673 Self type does not work in Mypy for a "hybrid"
569
+ # style method that returns type or Self, so for one specific case
570
+ # we still need to use the pre-pep-673 workaround.
571
+ _Self = TypeVar("_Self", bound="AssociationProxyInstance[Any]")
572
+
573
+
574
+ class AssociationProxyInstance(SQLORMOperations[_T]):
575
+ """A per-class object that serves class- and object-specific results.
576
+
577
+ This is used by :class:`.AssociationProxy` when it is invoked
578
+ in terms of a specific class or instance of a class, i.e. when it is
579
+ used as a regular Python descriptor.
580
+
581
+ When referring to the :class:`.AssociationProxy` as a normal Python
582
+ descriptor, the :class:`.AssociationProxyInstance` is the object that
583
+ actually serves the information. Under normal circumstances, its presence
584
+ is transparent::
585
+
586
+ >>> User.keywords.scalar
587
+ False
588
+
589
+ In the special case that the :class:`.AssociationProxy` object is being
590
+ accessed directly, in order to get an explicit handle to the
591
+ :class:`.AssociationProxyInstance`, use the
592
+ :meth:`.AssociationProxy.for_class` method::
593
+
594
+ proxy_state = inspect(User).all_orm_descriptors["keywords"].for_class(User)
595
+
596
+ # view if proxy object is scalar or not
597
+ >>> proxy_state.scalar
598
+ False
599
+
600
+ """ # noqa
601
+
602
+ collection_class: Optional[Type[Any]]
603
+ parent: _AssociationProxyProtocol[_T]
604
+
605
+ def __init__(
606
+ self,
607
+ parent: _AssociationProxyProtocol[_T],
608
+ owning_class: Type[Any],
609
+ target_class: Type[Any],
610
+ value_attr: str,
611
+ ):
612
+ self.parent = parent
613
+ self.key = parent.key
614
+ self.owning_class = owning_class
615
+ self.target_collection = parent.target_collection
616
+ self.collection_class = None
617
+ self.target_class = target_class
618
+ self.value_attr = value_attr
619
+
620
+ target_class: Type[Any]
621
+ """The intermediary class handled by this
622
+ :class:`.AssociationProxyInstance`.
623
+
624
+ Intercepted append/set/assignment events will result
625
+ in the generation of new instances of this class.
626
+
627
+ """
628
+
629
+ @classmethod
630
+ def for_proxy(
631
+ cls,
632
+ parent: AssociationProxy[_T],
633
+ owning_class: Type[Any],
634
+ parent_instance: Any,
635
+ ) -> AssociationProxyInstance[_T]:
636
+ target_collection = parent.target_collection
637
+ value_attr = parent.value_attr
638
+ prop = cast(
639
+ "orm.RelationshipProperty[_T]",
640
+ orm.class_mapper(owning_class).get_property(target_collection),
641
+ )
642
+
643
+ # this was never asserted before but this should be made clear.
644
+ if not isinstance(prop, orm.RelationshipProperty):
645
+ raise NotImplementedError(
646
+ "association proxy to a non-relationship "
647
+ "intermediary is not supported"
648
+ ) from None
649
+
650
+ target_class = prop.mapper.class_
651
+
652
+ try:
653
+ target_assoc = cast(
654
+ "AssociationProxyInstance[_T]",
655
+ cls._cls_unwrap_target_assoc_proxy(target_class, value_attr),
656
+ )
657
+ except AttributeError:
658
+ # the proxied attribute doesn't exist on the target class;
659
+ # return an "ambiguous" instance that will work on a per-object
660
+ # basis
661
+ return AmbiguousAssociationProxyInstance(
662
+ parent, owning_class, target_class, value_attr
663
+ )
664
+ except Exception as err:
665
+ raise exc.InvalidRequestError(
666
+ f"Association proxy received an unexpected error when "
667
+ f"trying to retrieve attribute "
668
+ f'"{target_class.__name__}.{parent.value_attr}" from '
669
+ f'class "{target_class.__name__}": {err}'
670
+ ) from err
671
+ else:
672
+ return cls._construct_for_assoc(
673
+ target_assoc, parent, owning_class, target_class, value_attr
674
+ )
675
+
676
+ @classmethod
677
+ def _construct_for_assoc(
678
+ cls,
679
+ target_assoc: Optional[AssociationProxyInstance[_T]],
680
+ parent: _AssociationProxyProtocol[_T],
681
+ owning_class: Type[Any],
682
+ target_class: Type[Any],
683
+ value_attr: str,
684
+ ) -> AssociationProxyInstance[_T]:
685
+ if target_assoc is not None:
686
+ return ObjectAssociationProxyInstance(
687
+ parent, owning_class, target_class, value_attr
688
+ )
689
+
690
+ attr = getattr(target_class, value_attr)
691
+ if not hasattr(attr, "_is_internal_proxy"):
692
+ return AmbiguousAssociationProxyInstance(
693
+ parent, owning_class, target_class, value_attr
694
+ )
695
+ is_object = attr._impl_uses_objects
696
+ if is_object:
697
+ return ObjectAssociationProxyInstance(
698
+ parent, owning_class, target_class, value_attr
699
+ )
700
+ else:
701
+ return ColumnAssociationProxyInstance(
702
+ parent, owning_class, target_class, value_attr
703
+ )
704
+
705
+ def _get_property(self) -> MapperProperty[Any]:
706
+ return orm.class_mapper(self.owning_class).get_property(
707
+ self.target_collection
708
+ )
709
+
710
+ @property
711
+ def _comparator(self) -> PropComparator[Any]:
712
+ return getattr( # type: ignore
713
+ self.owning_class, self.target_collection
714
+ ).comparator
715
+
716
+ def __clause_element__(self) -> NoReturn:
717
+ raise NotImplementedError(
718
+ "The association proxy can't be used as a plain column "
719
+ "expression; it only works inside of a comparison expression"
720
+ )
721
+
722
+ @classmethod
723
+ def _cls_unwrap_target_assoc_proxy(
724
+ cls, target_class: Any, value_attr: str
725
+ ) -> Optional[AssociationProxyInstance[_T]]:
726
+ attr = getattr(target_class, value_attr)
727
+ assert not isinstance(attr, AssociationProxy)
728
+ if isinstance(attr, AssociationProxyInstance):
729
+ return attr
730
+ return None
731
+
732
+ @util.memoized_property
733
+ def _unwrap_target_assoc_proxy(
734
+ self,
735
+ ) -> Optional[AssociationProxyInstance[_T]]:
736
+ return self._cls_unwrap_target_assoc_proxy(
737
+ self.target_class, self.value_attr
738
+ )
739
+
740
+ @property
741
+ def remote_attr(self) -> SQLORMOperations[_T]:
742
+ """The 'remote' class attribute referenced by this
743
+ :class:`.AssociationProxyInstance`.
744
+
745
+ .. seealso::
746
+
747
+ :attr:`.AssociationProxyInstance.attr`
748
+
749
+ :attr:`.AssociationProxyInstance.local_attr`
750
+
751
+ """
752
+ return cast(
753
+ "SQLORMOperations[_T]", getattr(self.target_class, self.value_attr)
754
+ )
755
+
756
+ @property
757
+ def local_attr(self) -> SQLORMOperations[Any]:
758
+ """The 'local' class attribute referenced by this
759
+ :class:`.AssociationProxyInstance`.
760
+
761
+ .. seealso::
762
+
763
+ :attr:`.AssociationProxyInstance.attr`
764
+
765
+ :attr:`.AssociationProxyInstance.remote_attr`
766
+
767
+ """
768
+ return cast(
769
+ "SQLORMOperations[Any]",
770
+ getattr(self.owning_class, self.target_collection),
771
+ )
772
+
773
+ @property
774
+ def attr(self) -> Tuple[SQLORMOperations[Any], SQLORMOperations[_T]]:
775
+ """Return a tuple of ``(local_attr, remote_attr)``.
776
+
777
+ This attribute was originally intended to facilitate using the
778
+ :meth:`_query.Query.join` method to join across the two relationships
779
+ at once, however this makes use of a deprecated calling style.
780
+
781
+ To use :meth:`_sql.select.join` or :meth:`_orm.Query.join` with
782
+ an association proxy, the current method is to make use of the
783
+ :attr:`.AssociationProxyInstance.local_attr` and
784
+ :attr:`.AssociationProxyInstance.remote_attr` attributes separately::
785
+
786
+ stmt = (
787
+ select(Parent)
788
+ .join(Parent.proxied.local_attr)
789
+ .join(Parent.proxied.remote_attr)
790
+ )
791
+
792
+ A future release may seek to provide a more succinct join pattern
793
+ for association proxy attributes.
794
+
795
+ .. seealso::
796
+
797
+ :attr:`.AssociationProxyInstance.local_attr`
798
+
799
+ :attr:`.AssociationProxyInstance.remote_attr`
800
+
801
+ """
802
+ return (self.local_attr, self.remote_attr)
803
+
804
+ @util.memoized_property
805
+ def scalar(self) -> bool:
806
+ """Return ``True`` if this :class:`.AssociationProxyInstance`
807
+ proxies a scalar relationship on the local side."""
808
+
809
+ scalar = not self._get_property().uselist
810
+ if scalar:
811
+ self._initialize_scalar_accessors()
812
+ return scalar
813
+
814
+ @util.memoized_property
815
+ def _value_is_scalar(self) -> bool:
816
+ return (
817
+ not self._get_property()
818
+ .mapper.get_property(self.value_attr)
819
+ .uselist
820
+ )
821
+
822
+ @property
823
+ def _target_is_object(self) -> bool:
824
+ raise NotImplementedError()
825
+
826
+ _scalar_get: _GetterProtocol[_T]
827
+ _scalar_set: _PlainSetterProtocol[_T]
828
+
829
+ def _initialize_scalar_accessors(self) -> None:
830
+ if self.parent.getset_factory:
831
+ get, set_ = self.parent.getset_factory(None, self)
832
+ else:
833
+ get, set_ = self.parent._default_getset(None)
834
+ self._scalar_get, self._scalar_set = get, cast(
835
+ "_PlainSetterProtocol[_T]", set_
836
+ )
837
+
838
+ def _default_getset(
839
+ self, collection_class: Any
840
+ ) -> Tuple[_GetterProtocol[Any], _SetterProtocol]:
841
+ attr = self.value_attr
842
+ _getter = operator.attrgetter(attr)
843
+
844
+ def getter(instance: Any) -> Optional[_T]:
845
+ return _getter(instance) if instance is not None else None
846
+
847
+ if collection_class is dict:
848
+
849
+ def dict_setter(instance: Any, k: Any, value: _T) -> None:
850
+ setattr(instance, attr, value)
851
+
852
+ return getter, dict_setter
853
+ else:
854
+
855
+ def plain_setter(o: Any, v: _T) -> None:
856
+ setattr(o, attr, v)
857
+
858
+ return getter, plain_setter
859
+
860
+ @util.ro_non_memoized_property
861
+ def info(self) -> _InfoType:
862
+ return self.parent.info
863
+
864
+ @overload
865
+ def get(self: _Self, obj: Literal[None]) -> _Self: ...
866
+
867
+ @overload
868
+ def get(self, obj: Any) -> _T: ...
869
+
870
+ def get(
871
+ self, obj: Any
872
+ ) -> Union[Optional[_T], AssociationProxyInstance[_T]]:
873
+ if obj is None:
874
+ return self
875
+
876
+ proxy: _T
877
+
878
+ if self.scalar:
879
+ target = getattr(obj, self.target_collection)
880
+ return self._scalar_get(target)
881
+ else:
882
+ try:
883
+ # If the owning instance is reborn (orm session resurrect,
884
+ # etc.), refresh the proxy cache.
885
+ creator_id, self_id, proxy = cast(
886
+ "Tuple[int, int, _T]", getattr(obj, self.key)
887
+ )
888
+ except AttributeError:
889
+ pass
890
+ else:
891
+ if id(obj) == creator_id and id(self) == self_id:
892
+ assert self.collection_class is not None
893
+ return proxy
894
+
895
+ self.collection_class, proxy = self._new(
896
+ _lazy_collection(obj, self.target_collection)
897
+ )
898
+ setattr(obj, self.key, (id(obj), id(self), proxy))
899
+ return proxy
900
+
901
+ def set(self, obj: Any, values: _T) -> None:
902
+ if self.scalar:
903
+ creator = cast(
904
+ "_PlainCreatorProtocol[_T]",
905
+ (
906
+ self.parent.creator
907
+ if self.parent.creator
908
+ else self.target_class
909
+ ),
910
+ )
911
+ target = getattr(obj, self.target_collection)
912
+ if target is None:
913
+ if (
914
+ values is None
915
+ and not self.parent.create_on_none_assignment
916
+ ):
917
+ return
918
+ setattr(obj, self.target_collection, creator(values))
919
+ else:
920
+ self._scalar_set(target, values)
921
+ if values is None and self.parent.cascade_scalar_deletes:
922
+ setattr(obj, self.target_collection, None)
923
+ else:
924
+ proxy = self.get(obj)
925
+ assert self.collection_class is not None
926
+ if proxy is not values:
927
+ proxy._bulk_replace(self, values)
928
+
929
+ def delete(self, obj: Any) -> None:
930
+ if self.owning_class is None:
931
+ self._calc_owner(obj, None)
932
+
933
+ if self.scalar:
934
+ target = getattr(obj, self.target_collection)
935
+ if target is not None:
936
+ delattr(target, self.value_attr)
937
+ delattr(obj, self.target_collection)
938
+
939
+ def _new(
940
+ self, lazy_collection: _LazyCollectionProtocol[_T]
941
+ ) -> Tuple[Type[Any], _T]:
942
+ creator = (
943
+ self.parent.creator
944
+ if self.parent.creator is not None
945
+ else cast("_CreatorProtocol", self.target_class)
946
+ )
947
+ collection_class = util.duck_type_collection(lazy_collection())
948
+
949
+ if collection_class is None:
950
+ raise exc.InvalidRequestError(
951
+ f"lazy collection factory did not return a "
952
+ f"valid collection type, got {collection_class}"
953
+ )
954
+ if self.parent.proxy_factory:
955
+ return (
956
+ collection_class,
957
+ self.parent.proxy_factory(
958
+ lazy_collection, creator, self.value_attr, self
959
+ ),
960
+ )
961
+
962
+ if self.parent.getset_factory:
963
+ getter, setter = self.parent.getset_factory(collection_class, self)
964
+ else:
965
+ getter, setter = self.parent._default_getset(collection_class)
966
+
967
+ if collection_class is list:
968
+ return (
969
+ collection_class,
970
+ cast(
971
+ _T,
972
+ _AssociationList(
973
+ lazy_collection, creator, getter, setter, self
974
+ ),
975
+ ),
976
+ )
977
+ elif collection_class is dict:
978
+ return (
979
+ collection_class,
980
+ cast(
981
+ _T,
982
+ _AssociationDict(
983
+ lazy_collection, creator, getter, setter, self
984
+ ),
985
+ ),
986
+ )
987
+ elif collection_class is set:
988
+ return (
989
+ collection_class,
990
+ cast(
991
+ _T,
992
+ _AssociationSet(
993
+ lazy_collection, creator, getter, setter, self
994
+ ),
995
+ ),
996
+ )
997
+ else:
998
+ raise exc.ArgumentError(
999
+ "could not guess which interface to use for "
1000
+ 'collection_class "%s" backing "%s"; specify a '
1001
+ "proxy_factory and proxy_bulk_set manually"
1002
+ % (self.collection_class, self.target_collection)
1003
+ )
1004
+
1005
+ def _set(
1006
+ self, proxy: _AssociationCollection[Any], values: Iterable[Any]
1007
+ ) -> None:
1008
+ if self.parent.proxy_bulk_set:
1009
+ self.parent.proxy_bulk_set(proxy, values)
1010
+ elif self.collection_class is list:
1011
+ cast("_AssociationList[Any]", proxy).extend(values)
1012
+ elif self.collection_class is dict:
1013
+ cast("_AssociationDict[Any, Any]", proxy).update(values)
1014
+ elif self.collection_class is set:
1015
+ cast("_AssociationSet[Any]", proxy).update(values)
1016
+ else:
1017
+ raise exc.ArgumentError(
1018
+ "no proxy_bulk_set supplied for custom "
1019
+ "collection_class implementation"
1020
+ )
1021
+
1022
+ def _inflate(self, proxy: _AssociationCollection[Any]) -> None:
1023
+ creator = (
1024
+ self.parent.creator
1025
+ and self.parent.creator
1026
+ or cast(_CreatorProtocol, self.target_class)
1027
+ )
1028
+
1029
+ if self.parent.getset_factory:
1030
+ getter, setter = self.parent.getset_factory(
1031
+ self.collection_class, self
1032
+ )
1033
+ else:
1034
+ getter, setter = self.parent._default_getset(self.collection_class)
1035
+
1036
+ proxy.creator = creator
1037
+ proxy.getter = getter
1038
+ proxy.setter = setter
1039
+
1040
+ def _criterion_exists(
1041
+ self,
1042
+ criterion: Optional[_ColumnExpressionArgument[bool]] = None,
1043
+ **kwargs: Any,
1044
+ ) -> ColumnElement[bool]:
1045
+ is_has = kwargs.pop("is_has", None)
1046
+
1047
+ target_assoc = self._unwrap_target_assoc_proxy
1048
+ if target_assoc is not None:
1049
+ inner = target_assoc._criterion_exists(
1050
+ criterion=criterion, **kwargs
1051
+ )
1052
+ return self._comparator._criterion_exists(inner)
1053
+
1054
+ if self._target_is_object:
1055
+ attr = getattr(self.target_class, self.value_attr)
1056
+ value_expr = attr.comparator._criterion_exists(criterion, **kwargs)
1057
+ else:
1058
+ if kwargs:
1059
+ raise exc.ArgumentError(
1060
+ "Can't apply keyword arguments to column-targeted "
1061
+ "association proxy; use =="
1062
+ )
1063
+ elif is_has and criterion is not None:
1064
+ raise exc.ArgumentError(
1065
+ "Non-empty has() not allowed for "
1066
+ "column-targeted association proxy; use =="
1067
+ )
1068
+
1069
+ value_expr = criterion
1070
+
1071
+ return self._comparator._criterion_exists(value_expr)
1072
+
1073
+ def any(
1074
+ self,
1075
+ criterion: Optional[_ColumnExpressionArgument[bool]] = None,
1076
+ **kwargs: Any,
1077
+ ) -> ColumnElement[bool]:
1078
+ """Produce a proxied 'any' expression using EXISTS.
1079
+
1080
+ This expression will be a composed product
1081
+ using the :meth:`.Relationship.Comparator.any`
1082
+ and/or :meth:`.Relationship.Comparator.has`
1083
+ operators of the underlying proxied attributes.
1084
+
1085
+ """
1086
+ if self._unwrap_target_assoc_proxy is None and (
1087
+ self.scalar
1088
+ and (not self._target_is_object or self._value_is_scalar)
1089
+ ):
1090
+ raise exc.InvalidRequestError(
1091
+ "'any()' not implemented for scalar attributes. Use has()."
1092
+ )
1093
+ return self._criterion_exists(
1094
+ criterion=criterion, is_has=False, **kwargs
1095
+ )
1096
+
1097
+ def has(
1098
+ self,
1099
+ criterion: Optional[_ColumnExpressionArgument[bool]] = None,
1100
+ **kwargs: Any,
1101
+ ) -> ColumnElement[bool]:
1102
+ """Produce a proxied 'has' expression using EXISTS.
1103
+
1104
+ This expression will be a composed product
1105
+ using the :meth:`.Relationship.Comparator.any`
1106
+ and/or :meth:`.Relationship.Comparator.has`
1107
+ operators of the underlying proxied attributes.
1108
+
1109
+ """
1110
+ if self._unwrap_target_assoc_proxy is None and (
1111
+ not self.scalar
1112
+ or (self._target_is_object and not self._value_is_scalar)
1113
+ ):
1114
+ raise exc.InvalidRequestError(
1115
+ "'has()' not implemented for collections. Use any()."
1116
+ )
1117
+ return self._criterion_exists(
1118
+ criterion=criterion, is_has=True, **kwargs
1119
+ )
1120
+
1121
+ def __repr__(self) -> str:
1122
+ return "%s(%r)" % (self.__class__.__name__, self.parent)
1123
+
1124
+
1125
+ class AmbiguousAssociationProxyInstance(AssociationProxyInstance[_T]):
1126
+ """an :class:`.AssociationProxyInstance` where we cannot determine
1127
+ the type of target object.
1128
+ """
1129
+
1130
+ _is_canonical = False
1131
+
1132
+ def _ambiguous(self) -> NoReturn:
1133
+ raise AttributeError(
1134
+ "Association proxy %s.%s refers to an attribute '%s' that is not "
1135
+ "directly mapped on class %s; therefore this operation cannot "
1136
+ "proceed since we don't know what type of object is referred "
1137
+ "towards"
1138
+ % (
1139
+ self.owning_class.__name__,
1140
+ self.target_collection,
1141
+ self.value_attr,
1142
+ self.target_class,
1143
+ )
1144
+ )
1145
+
1146
+ def get(self, obj: Any) -> Any:
1147
+ if obj is None:
1148
+ return self
1149
+ else:
1150
+ return super().get(obj)
1151
+
1152
+ def __eq__(self, obj: object) -> NoReturn:
1153
+ self._ambiguous()
1154
+
1155
+ def __ne__(self, obj: object) -> NoReturn:
1156
+ self._ambiguous()
1157
+
1158
+ def any(
1159
+ self,
1160
+ criterion: Optional[_ColumnExpressionArgument[bool]] = None,
1161
+ **kwargs: Any,
1162
+ ) -> NoReturn:
1163
+ self._ambiguous()
1164
+
1165
+ def has(
1166
+ self,
1167
+ criterion: Optional[_ColumnExpressionArgument[bool]] = None,
1168
+ **kwargs: Any,
1169
+ ) -> NoReturn:
1170
+ self._ambiguous()
1171
+
1172
+ @util.memoized_property
1173
+ def _lookup_cache(self) -> Dict[Type[Any], AssociationProxyInstance[_T]]:
1174
+ # mapping of <subclass>->AssociationProxyInstance.
1175
+ # e.g. proxy is A-> A.b -> B -> B.b_attr, but B.b_attr doesn't exist;
1176
+ # only B1(B) and B2(B) have "b_attr", keys in here would be B1, B2
1177
+ return {}
1178
+
1179
+ def _non_canonical_get_for_object(
1180
+ self, parent_instance: Any
1181
+ ) -> AssociationProxyInstance[_T]:
1182
+ if parent_instance is not None:
1183
+ actual_obj = getattr(parent_instance, self.target_collection)
1184
+ if actual_obj is not None:
1185
+ try:
1186
+ insp = inspect(actual_obj)
1187
+ except exc.NoInspectionAvailable:
1188
+ pass
1189
+ else:
1190
+ mapper = insp.mapper
1191
+ instance_class = mapper.class_
1192
+ if instance_class not in self._lookup_cache:
1193
+ self._populate_cache(instance_class, mapper)
1194
+
1195
+ try:
1196
+ return self._lookup_cache[instance_class]
1197
+ except KeyError:
1198
+ pass
1199
+
1200
+ # no object or ambiguous object given, so return "self", which
1201
+ # is a proxy with generally only instance-level functionality
1202
+ return self
1203
+
1204
+ def _populate_cache(
1205
+ self, instance_class: Any, mapper: Mapper[Any]
1206
+ ) -> None:
1207
+ prop = orm.class_mapper(self.owning_class).get_property(
1208
+ self.target_collection
1209
+ )
1210
+
1211
+ if mapper.isa(prop.mapper):
1212
+ target_class = instance_class
1213
+ try:
1214
+ target_assoc = self._cls_unwrap_target_assoc_proxy(
1215
+ target_class, self.value_attr
1216
+ )
1217
+ except AttributeError:
1218
+ pass
1219
+ else:
1220
+ self._lookup_cache[instance_class] = self._construct_for_assoc(
1221
+ cast("AssociationProxyInstance[_T]", target_assoc),
1222
+ self.parent,
1223
+ self.owning_class,
1224
+ target_class,
1225
+ self.value_attr,
1226
+ )
1227
+
1228
+
1229
+ class ObjectAssociationProxyInstance(AssociationProxyInstance[_T]):
1230
+ """an :class:`.AssociationProxyInstance` that has an object as a target."""
1231
+
1232
+ _target_is_object: bool = True
1233
+ _is_canonical = True
1234
+
1235
+ def adapt_to_entity(
1236
+ self, aliased_insp: AliasedInsp[Any]
1237
+ ) -> AliasedAssociationProxyInstance[_T]:
1238
+ return AliasedAssociationProxyInstance(self, aliased_insp)
1239
+
1240
+ def contains(self, other: Any, **kw: Any) -> ColumnElement[bool]:
1241
+ """Produce a proxied 'contains' expression using EXISTS.
1242
+
1243
+ This expression will be a composed product
1244
+ using the :meth:`.Relationship.Comparator.any`,
1245
+ :meth:`.Relationship.Comparator.has`,
1246
+ and/or :meth:`.Relationship.Comparator.contains`
1247
+ operators of the underlying proxied attributes.
1248
+ """
1249
+
1250
+ target_assoc = self._unwrap_target_assoc_proxy
1251
+ if target_assoc is not None:
1252
+ return self._comparator._criterion_exists(
1253
+ target_assoc.contains(other)
1254
+ if not target_assoc.scalar
1255
+ else target_assoc == other
1256
+ )
1257
+ elif (
1258
+ self._target_is_object
1259
+ and self.scalar
1260
+ and not self._value_is_scalar
1261
+ ):
1262
+ return self._comparator.has(
1263
+ getattr(self.target_class, self.value_attr).contains(other)
1264
+ )
1265
+ elif self._target_is_object and self.scalar and self._value_is_scalar:
1266
+ raise exc.InvalidRequestError(
1267
+ "contains() doesn't apply to a scalar object endpoint; use =="
1268
+ )
1269
+ else:
1270
+ return self._comparator._criterion_exists(
1271
+ **{self.value_attr: other}
1272
+ )
1273
+
1274
+ def __eq__(self, obj: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
1275
+ # note the has() here will fail for collections; eq_()
1276
+ # is only allowed with a scalar.
1277
+ if obj is None:
1278
+ return or_(
1279
+ self._comparator.has(**{self.value_attr: obj}),
1280
+ self._comparator == None,
1281
+ )
1282
+ else:
1283
+ return self._comparator.has(**{self.value_attr: obj})
1284
+
1285
+ def __ne__(self, obj: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
1286
+ # note the has() here will fail for collections; eq_()
1287
+ # is only allowed with a scalar.
1288
+ return self._comparator.has(
1289
+ getattr(self.target_class, self.value_attr) != obj
1290
+ )
1291
+
1292
+
1293
+ class AliasedAssociationProxyInstance(ObjectAssociationProxyInstance[_T]):
1294
+ def __init__(
1295
+ self,
1296
+ parent_instance: ObjectAssociationProxyInstance[_T],
1297
+ aliased_insp: AliasedInsp[Any],
1298
+ ) -> None:
1299
+ self.parent = parent_instance.parent
1300
+ self.owning_class = parent_instance.owning_class
1301
+ self.aliased_insp = aliased_insp
1302
+ self.target_collection = parent_instance.target_collection
1303
+ self.collection_class = None
1304
+ self.target_class = parent_instance.target_class
1305
+ self.value_attr = parent_instance.value_attr
1306
+
1307
+ @property
1308
+ def _comparator(self) -> PropComparator[Any]:
1309
+ return getattr( # type: ignore
1310
+ self.aliased_insp.entity, self.target_collection
1311
+ ).comparator
1312
+
1313
+ @property
1314
+ def local_attr(self) -> SQLORMOperations[Any]:
1315
+ """The 'local' class attribute referenced by this
1316
+ :class:`.AssociationProxyInstance`.
1317
+
1318
+ .. seealso::
1319
+
1320
+ :attr:`.AssociationProxyInstance.attr`
1321
+
1322
+ :attr:`.AssociationProxyInstance.remote_attr`
1323
+
1324
+ """
1325
+ return cast(
1326
+ "SQLORMOperations[Any]",
1327
+ getattr(self.aliased_insp.entity, self.target_collection),
1328
+ )
1329
+
1330
+
1331
+ class ColumnAssociationProxyInstance(AssociationProxyInstance[_T]):
1332
+ """an :class:`.AssociationProxyInstance` that has a database column as a
1333
+ target.
1334
+ """
1335
+
1336
+ _target_is_object: bool = False
1337
+ _is_canonical = True
1338
+
1339
+ def __eq__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
1340
+ # special case "is None" to check for no related row as well
1341
+ expr = self._criterion_exists(
1342
+ self.remote_attr.operate(operators.eq, other)
1343
+ )
1344
+ if other is None:
1345
+ return or_(expr, self._comparator == None)
1346
+ else:
1347
+ return expr
1348
+
1349
+ def operate(
1350
+ self, op: operators.OperatorType, *other: Any, **kwargs: Any
1351
+ ) -> ColumnElement[Any]:
1352
+ return self._criterion_exists(
1353
+ self.remote_attr.operate(op, *other, **kwargs)
1354
+ )
1355
+
1356
+
1357
+ class _lazy_collection(_LazyCollectionProtocol[_T]):
1358
+ def __init__(self, obj: Any, target: str):
1359
+ self.parent = obj
1360
+ self.target = target
1361
+
1362
+ def __call__(
1363
+ self,
1364
+ ) -> Union[MutableSet[_T], MutableMapping[Any, _T], MutableSequence[_T]]:
1365
+ return getattr(self.parent, self.target) # type: ignore[no-any-return]
1366
+
1367
+ def __getstate__(self) -> Any:
1368
+ return {"obj": self.parent, "target": self.target}
1369
+
1370
+ def __setstate__(self, state: Any) -> None:
1371
+ self.parent = state["obj"]
1372
+ self.target = state["target"]
1373
+
1374
+
1375
+ _IT = TypeVar("_IT", bound="Any")
1376
+ """instance type - this is the type of object inside a collection.
1377
+
1378
+ this is not the same as the _T of AssociationProxy and
1379
+ AssociationProxyInstance itself, which will often refer to the
1380
+ collection[_IT] type.
1381
+
1382
+ """
1383
+
1384
+
1385
+ class _AssociationCollection(Generic[_IT]):
1386
+ getter: _GetterProtocol[_IT]
1387
+ """A function. Given an associated object, return the 'value'."""
1388
+
1389
+ creator: _CreatorProtocol
1390
+ """
1391
+ A function that creates new target entities. Given one parameter:
1392
+ value. This assertion is assumed::
1393
+
1394
+ obj = creator(somevalue)
1395
+ assert getter(obj) == somevalue
1396
+ """
1397
+
1398
+ parent: AssociationProxyInstance[_IT]
1399
+ setter: _SetterProtocol
1400
+ """A function. Given an associated object and a value, store that
1401
+ value on the object.
1402
+ """
1403
+
1404
+ lazy_collection: _LazyCollectionProtocol[_IT]
1405
+ """A callable returning a list-based collection of entities (usually an
1406
+ object attribute managed by a SQLAlchemy relationship())"""
1407
+
1408
+ def __init__(
1409
+ self,
1410
+ lazy_collection: _LazyCollectionProtocol[_IT],
1411
+ creator: _CreatorProtocol,
1412
+ getter: _GetterProtocol[_IT],
1413
+ setter: _SetterProtocol,
1414
+ parent: AssociationProxyInstance[_IT],
1415
+ ):
1416
+ """Constructs an _AssociationCollection.
1417
+
1418
+ This will always be a subclass of either _AssociationList,
1419
+ _AssociationSet, or _AssociationDict.
1420
+
1421
+ """
1422
+ self.lazy_collection = lazy_collection
1423
+ self.creator = creator
1424
+ self.getter = getter
1425
+ self.setter = setter
1426
+ self.parent = parent
1427
+
1428
+ if typing.TYPE_CHECKING:
1429
+ col: Collection[_IT]
1430
+ else:
1431
+ col = property(lambda self: self.lazy_collection())
1432
+
1433
+ def __len__(self) -> int:
1434
+ return len(self.col)
1435
+
1436
+ def __bool__(self) -> bool:
1437
+ return bool(self.col)
1438
+
1439
+ def __getstate__(self) -> Any:
1440
+ return {"parent": self.parent, "lazy_collection": self.lazy_collection}
1441
+
1442
+ def __setstate__(self, state: Any) -> None:
1443
+ self.parent = state["parent"]
1444
+ self.lazy_collection = state["lazy_collection"]
1445
+ self.parent._inflate(self)
1446
+
1447
+ def clear(self) -> None:
1448
+ raise NotImplementedError()
1449
+
1450
+
1451
+ class _AssociationSingleItem(_AssociationCollection[_T]):
1452
+ setter: _PlainSetterProtocol[_T]
1453
+ creator: _PlainCreatorProtocol[_T]
1454
+
1455
+ def _create(self, value: _T) -> Any:
1456
+ return self.creator(value)
1457
+
1458
+ def _get(self, object_: Any) -> _T:
1459
+ return self.getter(object_)
1460
+
1461
+ def _bulk_replace(
1462
+ self, assoc_proxy: AssociationProxyInstance[Any], values: Iterable[_IT]
1463
+ ) -> None:
1464
+ self.clear()
1465
+ assoc_proxy._set(self, values)
1466
+
1467
+
1468
+ class _AssociationList(_AssociationSingleItem[_T], MutableSequence[_T]):
1469
+ """Generic, converting, list-to-list proxy."""
1470
+
1471
+ col: MutableSequence[_T]
1472
+
1473
+ def _set(self, object_: Any, value: _T) -> None:
1474
+ self.setter(object_, value)
1475
+
1476
+ @overload
1477
+ def __getitem__(self, index: int) -> _T: ...
1478
+
1479
+ @overload
1480
+ def __getitem__(self, index: slice) -> MutableSequence[_T]: ...
1481
+
1482
+ def __getitem__(
1483
+ self, index: Union[int, slice]
1484
+ ) -> Union[_T, MutableSequence[_T]]:
1485
+ if not isinstance(index, slice):
1486
+ return self._get(self.col[index])
1487
+ else:
1488
+ return [self._get(member) for member in self.col[index]]
1489
+
1490
+ @overload
1491
+ def __setitem__(self, index: int, value: _T) -> None: ...
1492
+
1493
+ @overload
1494
+ def __setitem__(self, index: slice, value: Iterable[_T]) -> None: ...
1495
+
1496
+ def __setitem__(
1497
+ self, index: Union[int, slice], value: Union[_T, Iterable[_T]]
1498
+ ) -> None:
1499
+ if not isinstance(index, slice):
1500
+ self._set(self.col[index], cast("_T", value))
1501
+ else:
1502
+ if index.stop is None:
1503
+ stop = len(self)
1504
+ elif index.stop < 0:
1505
+ stop = len(self) + index.stop
1506
+ else:
1507
+ stop = index.stop
1508
+ step = index.step or 1
1509
+
1510
+ start = index.start or 0
1511
+ rng = list(range(index.start or 0, stop, step))
1512
+
1513
+ sized_value = list(value)
1514
+
1515
+ if step == 1:
1516
+ for i in rng:
1517
+ del self[start]
1518
+ i = start
1519
+ for item in sized_value:
1520
+ self.insert(i, item)
1521
+ i += 1
1522
+ else:
1523
+ if len(sized_value) != len(rng):
1524
+ raise ValueError(
1525
+ "attempt to assign sequence of size %s to "
1526
+ "extended slice of size %s"
1527
+ % (len(sized_value), len(rng))
1528
+ )
1529
+ for i, item in zip(rng, value):
1530
+ self._set(self.col[i], item)
1531
+
1532
+ @overload
1533
+ def __delitem__(self, index: int) -> None: ...
1534
+
1535
+ @overload
1536
+ def __delitem__(self, index: slice) -> None: ...
1537
+
1538
+ def __delitem__(self, index: Union[slice, int]) -> None:
1539
+ del self.col[index]
1540
+
1541
+ def __contains__(self, value: object) -> bool:
1542
+ for member in self.col:
1543
+ # testlib.pragma exempt:__eq__
1544
+ if self._get(member) == value:
1545
+ return True
1546
+ return False
1547
+
1548
+ def __iter__(self) -> Iterator[_T]:
1549
+ """Iterate over proxied values.
1550
+
1551
+ For the actual domain objects, iterate over .col instead or
1552
+ just use the underlying collection directly from its property
1553
+ on the parent.
1554
+ """
1555
+
1556
+ for member in self.col:
1557
+ yield self._get(member)
1558
+ return
1559
+
1560
+ def append(self, value: _T) -> None:
1561
+ col = self.col
1562
+ item = self._create(value)
1563
+ col.append(item)
1564
+
1565
+ def count(self, value: Any) -> int:
1566
+ count = 0
1567
+ for v in self:
1568
+ if v == value:
1569
+ count += 1
1570
+ return count
1571
+
1572
+ def extend(self, values: Iterable[_T]) -> None:
1573
+ for v in values:
1574
+ self.append(v)
1575
+
1576
+ def insert(self, index: int, value: _T) -> None:
1577
+ self.col[index:index] = [self._create(value)]
1578
+
1579
+ def pop(self, index: int = -1) -> _T:
1580
+ return self.getter(self.col.pop(index))
1581
+
1582
+ def remove(self, value: _T) -> None:
1583
+ for i, val in enumerate(self):
1584
+ if val == value:
1585
+ del self.col[i]
1586
+ return
1587
+ raise ValueError("value not in list")
1588
+
1589
+ def reverse(self) -> NoReturn:
1590
+ """Not supported, use reversed(mylist)"""
1591
+
1592
+ raise NotImplementedError()
1593
+
1594
+ def sort(self) -> NoReturn:
1595
+ """Not supported, use sorted(mylist)"""
1596
+
1597
+ raise NotImplementedError()
1598
+
1599
+ def clear(self) -> None:
1600
+ del self.col[0 : len(self.col)]
1601
+
1602
+ def __eq__(self, other: object) -> bool:
1603
+ return list(self) == other
1604
+
1605
+ def __ne__(self, other: object) -> bool:
1606
+ return list(self) != other
1607
+
1608
+ def __lt__(self, other: List[_T]) -> bool:
1609
+ return list(self) < other
1610
+
1611
+ def __le__(self, other: List[_T]) -> bool:
1612
+ return list(self) <= other
1613
+
1614
+ def __gt__(self, other: List[_T]) -> bool:
1615
+ return list(self) > other
1616
+
1617
+ def __ge__(self, other: List[_T]) -> bool:
1618
+ return list(self) >= other
1619
+
1620
+ def __add__(self, other: List[_T]) -> List[_T]:
1621
+ try:
1622
+ other = list(other)
1623
+ except TypeError:
1624
+ return NotImplemented
1625
+ return list(self) + other
1626
+
1627
+ def __radd__(self, other: List[_T]) -> List[_T]:
1628
+ try:
1629
+ other = list(other)
1630
+ except TypeError:
1631
+ return NotImplemented
1632
+ return other + list(self)
1633
+
1634
+ def __mul__(self, n: SupportsIndex) -> List[_T]:
1635
+ if not isinstance(n, int):
1636
+ return NotImplemented
1637
+ return list(self) * n
1638
+
1639
+ def __rmul__(self, n: SupportsIndex) -> List[_T]:
1640
+ if not isinstance(n, int):
1641
+ return NotImplemented
1642
+ return n * list(self)
1643
+
1644
+ def __iadd__(self, iterable: Iterable[_T]) -> Self:
1645
+ self.extend(iterable)
1646
+ return self
1647
+
1648
+ def __imul__(self, n: SupportsIndex) -> Self:
1649
+ # unlike a regular list *=, proxied __imul__ will generate unique
1650
+ # backing objects for each copy. *= on proxied lists is a bit of
1651
+ # a stretch anyhow, and this interpretation of the __imul__ contract
1652
+ # is more plausibly useful than copying the backing objects.
1653
+ if not isinstance(n, int):
1654
+ raise NotImplementedError()
1655
+ if n == 0:
1656
+ self.clear()
1657
+ elif n > 1:
1658
+ self.extend(list(self) * (n - 1))
1659
+ return self
1660
+
1661
+ if typing.TYPE_CHECKING:
1662
+ # TODO: no idea how to do this without separate "stub"
1663
+ def index(
1664
+ self, value: Any, start: int = ..., stop: int = ...
1665
+ ) -> int: ...
1666
+
1667
+ else:
1668
+
1669
+ def index(self, value: Any, *arg) -> int:
1670
+ ls = list(self)
1671
+ return ls.index(value, *arg)
1672
+
1673
+ def copy(self) -> List[_T]:
1674
+ return list(self)
1675
+
1676
+ def __repr__(self) -> str:
1677
+ return repr(list(self))
1678
+
1679
+ def __hash__(self) -> NoReturn:
1680
+ raise TypeError("%s objects are unhashable" % type(self).__name__)
1681
+
1682
+ if not typing.TYPE_CHECKING:
1683
+ for func_name, func in list(locals().items()):
1684
+ if (
1685
+ callable(func)
1686
+ and func.__name__ == func_name
1687
+ and not func.__doc__
1688
+ and hasattr(list, func_name)
1689
+ ):
1690
+ func.__doc__ = getattr(list, func_name).__doc__
1691
+ del func_name, func
1692
+
1693
+
1694
+ class _AssociationDict(_AssociationCollection[_VT], MutableMapping[_KT, _VT]):
1695
+ """Generic, converting, dict-to-dict proxy."""
1696
+
1697
+ setter: _DictSetterProtocol[_VT]
1698
+ creator: _KeyCreatorProtocol[_VT]
1699
+ col: MutableMapping[_KT, Optional[_VT]]
1700
+
1701
+ def _create(self, key: _KT, value: Optional[_VT]) -> Any:
1702
+ return self.creator(key, value)
1703
+
1704
+ def _get(self, object_: Any) -> _VT:
1705
+ return self.getter(object_)
1706
+
1707
+ def _set(self, object_: Any, key: _KT, value: _VT) -> None:
1708
+ return self.setter(object_, key, value)
1709
+
1710
+ def __getitem__(self, key: _KT) -> _VT:
1711
+ return self._get(self.col[key])
1712
+
1713
+ def __setitem__(self, key: _KT, value: _VT) -> None:
1714
+ if key in self.col:
1715
+ self._set(self.col[key], key, value)
1716
+ else:
1717
+ self.col[key] = self._create(key, value)
1718
+
1719
+ def __delitem__(self, key: _KT) -> None:
1720
+ del self.col[key]
1721
+
1722
+ def __contains__(self, key: object) -> bool:
1723
+ return key in self.col
1724
+
1725
+ def __iter__(self) -> Iterator[_KT]:
1726
+ return iter(self.col.keys())
1727
+
1728
+ def clear(self) -> None:
1729
+ self.col.clear()
1730
+
1731
+ def __eq__(self, other: object) -> bool:
1732
+ return dict(self) == other
1733
+
1734
+ def __ne__(self, other: object) -> bool:
1735
+ return dict(self) != other
1736
+
1737
+ def __repr__(self) -> str:
1738
+ return repr(dict(self))
1739
+
1740
+ @overload
1741
+ def get(self, __key: _KT, /) -> Optional[_VT]: ...
1742
+
1743
+ @overload
1744
+ def get(
1745
+ self, __key: _KT, /, default: Union[_VT, _T]
1746
+ ) -> Union[_VT, _T]: ...
1747
+
1748
+ def get(
1749
+ self, __key: _KT, /, default: Optional[Union[_VT, _T]] = None
1750
+ ) -> Union[_VT, _T, None]:
1751
+ try:
1752
+ return self[__key]
1753
+ except KeyError:
1754
+ return default
1755
+
1756
+ def setdefault(self, key: _KT, default: Optional[_VT] = None) -> _VT:
1757
+ # TODO: again, no idea how to create an actual MutableMapping.
1758
+ # default must allow None, return type can't include None,
1759
+ # the stub explicitly allows for default of None with a cryptic message
1760
+ # "This overload should be allowed only if the value type is
1761
+ # compatible with None.".
1762
+ if key not in self.col:
1763
+ self.col[key] = self._create(key, default)
1764
+ return default # type: ignore
1765
+ else:
1766
+ return self[key]
1767
+
1768
+ def keys(self) -> KeysView[_KT]:
1769
+ return self.col.keys()
1770
+
1771
+ def items(self) -> ItemsView[_KT, _VT]:
1772
+ return ItemsView(self)
1773
+
1774
+ def values(self) -> ValuesView[_VT]:
1775
+ return ValuesView(self)
1776
+
1777
+ @overload
1778
+ def pop(self, __key: _KT, /) -> _VT: ...
1779
+
1780
+ @overload
1781
+ def pop(
1782
+ self, __key: _KT, /, default: Union[_VT, _T] = ...
1783
+ ) -> Union[_VT, _T]: ...
1784
+
1785
+ def pop(self, __key: _KT, /, *arg: Any, **kw: Any) -> Union[_VT, _T]:
1786
+ member = self.col.pop(__key, *arg, **kw)
1787
+ return self._get(member)
1788
+
1789
+ def popitem(self) -> Tuple[_KT, _VT]:
1790
+ item = self.col.popitem()
1791
+ return (item[0], self._get(item[1]))
1792
+
1793
+ @overload
1794
+ def update(
1795
+ self, __m: SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT
1796
+ ) -> None: ...
1797
+
1798
+ @overload
1799
+ def update(
1800
+ self, __m: Iterable[tuple[_KT, _VT]], **kwargs: _VT
1801
+ ) -> None: ...
1802
+
1803
+ @overload
1804
+ def update(self, **kwargs: _VT) -> None: ...
1805
+
1806
+ def update(self, *a: Any, **kw: Any) -> None:
1807
+ up: Dict[_KT, _VT] = {}
1808
+ up.update(*a, **kw)
1809
+
1810
+ for key, value in up.items():
1811
+ self[key] = value
1812
+
1813
+ def _bulk_replace(
1814
+ self,
1815
+ assoc_proxy: AssociationProxyInstance[Any],
1816
+ values: Mapping[_KT, _VT],
1817
+ ) -> None:
1818
+ existing = set(self)
1819
+ constants = existing.intersection(values or ())
1820
+ additions = set(values or ()).difference(constants)
1821
+ removals = existing.difference(constants)
1822
+
1823
+ for key, member in values.items() or ():
1824
+ if key in additions:
1825
+ self[key] = member
1826
+ elif key in constants:
1827
+ self[key] = member
1828
+
1829
+ for key in removals:
1830
+ del self[key]
1831
+
1832
+ def copy(self) -> Dict[_KT, _VT]:
1833
+ return dict(self.items())
1834
+
1835
+ def __hash__(self) -> NoReturn:
1836
+ raise TypeError("%s objects are unhashable" % type(self).__name__)
1837
+
1838
+ if not typing.TYPE_CHECKING:
1839
+ for func_name, func in list(locals().items()):
1840
+ if (
1841
+ callable(func)
1842
+ and func.__name__ == func_name
1843
+ and not func.__doc__
1844
+ and hasattr(dict, func_name)
1845
+ ):
1846
+ func.__doc__ = getattr(dict, func_name).__doc__
1847
+ del func_name, func
1848
+
1849
+
1850
+ class _AssociationSet(_AssociationSingleItem[_T], MutableSet[_T]):
1851
+ """Generic, converting, set-to-set proxy."""
1852
+
1853
+ col: MutableSet[_T]
1854
+
1855
+ def __len__(self) -> int:
1856
+ return len(self.col)
1857
+
1858
+ def __bool__(self) -> bool:
1859
+ if self.col:
1860
+ return True
1861
+ else:
1862
+ return False
1863
+
1864
+ def __contains__(self, __o: object) -> bool:
1865
+ for member in self.col:
1866
+ if self._get(member) == __o:
1867
+ return True
1868
+ return False
1869
+
1870
+ def __iter__(self) -> Iterator[_T]:
1871
+ """Iterate over proxied values.
1872
+
1873
+ For the actual domain objects, iterate over .col instead or just use
1874
+ the underlying collection directly from its property on the parent.
1875
+
1876
+ """
1877
+ for member in self.col:
1878
+ yield self._get(member)
1879
+ return
1880
+
1881
+ def add(self, __element: _T, /) -> None:
1882
+ if __element not in self:
1883
+ self.col.add(self._create(__element))
1884
+
1885
+ # for discard and remove, choosing a more expensive check strategy rather
1886
+ # than call self.creator()
1887
+ def discard(self, __element: _T, /) -> None:
1888
+ for member in self.col:
1889
+ if self._get(member) == __element:
1890
+ self.col.discard(member)
1891
+ break
1892
+
1893
+ def remove(self, __element: _T, /) -> None:
1894
+ for member in self.col:
1895
+ if self._get(member) == __element:
1896
+ self.col.discard(member)
1897
+ return
1898
+ raise KeyError(__element)
1899
+
1900
+ def pop(self) -> _T:
1901
+ if not self.col:
1902
+ raise KeyError("pop from an empty set")
1903
+ member = self.col.pop()
1904
+ return self._get(member)
1905
+
1906
+ def update(self, *s: Iterable[_T]) -> None:
1907
+ for iterable in s:
1908
+ for value in iterable:
1909
+ self.add(value)
1910
+
1911
+ def _bulk_replace(self, assoc_proxy: Any, values: Iterable[_T]) -> None:
1912
+ existing = set(self)
1913
+ constants = existing.intersection(values or ())
1914
+ additions = set(values or ()).difference(constants)
1915
+ removals = existing.difference(constants)
1916
+
1917
+ appender = self.add
1918
+ remover = self.remove
1919
+
1920
+ for member in values or ():
1921
+ if member in additions:
1922
+ appender(member)
1923
+ elif member in constants:
1924
+ appender(member)
1925
+
1926
+ for member in removals:
1927
+ remover(member)
1928
+
1929
+ def __ior__( # type: ignore
1930
+ self, other: AbstractSet[_S]
1931
+ ) -> MutableSet[Union[_T, _S]]:
1932
+ if not collections._set_binops_check_strict(self, other):
1933
+ return NotImplemented
1934
+ for value in other:
1935
+ self.add(value)
1936
+ return self
1937
+
1938
+ def _set(self) -> Set[_T]:
1939
+ return set(iter(self))
1940
+
1941
+ def union(self, *s: Iterable[_S]) -> MutableSet[Union[_T, _S]]:
1942
+ return set(self).union(*s)
1943
+
1944
+ def __or__(self, __s: AbstractSet[_S]) -> MutableSet[Union[_T, _S]]:
1945
+ if not collections._set_binops_check_strict(self, __s):
1946
+ return NotImplemented
1947
+ return self.union(__s)
1948
+
1949
+ def difference(self, *s: Iterable[Any]) -> MutableSet[_T]:
1950
+ return set(self).difference(*s)
1951
+
1952
+ def __sub__(self, s: AbstractSet[Any]) -> MutableSet[_T]:
1953
+ if not collections._set_binops_check_strict(self, s):
1954
+ return NotImplemented
1955
+ return self.difference(s)
1956
+
1957
+ def difference_update(self, *s: Iterable[Any]) -> None:
1958
+ for other in s:
1959
+ for value in other:
1960
+ self.discard(value)
1961
+
1962
+ def __isub__(self, s: AbstractSet[Any]) -> Self:
1963
+ if not collections._set_binops_check_strict(self, s):
1964
+ return NotImplemented
1965
+ for value in s:
1966
+ self.discard(value)
1967
+ return self
1968
+
1969
+ def intersection(self, *s: Iterable[Any]) -> MutableSet[_T]:
1970
+ return set(self).intersection(*s)
1971
+
1972
+ def __and__(self, s: AbstractSet[Any]) -> MutableSet[_T]:
1973
+ if not collections._set_binops_check_strict(self, s):
1974
+ return NotImplemented
1975
+ return self.intersection(s)
1976
+
1977
+ def intersection_update(self, *s: Iterable[Any]) -> None:
1978
+ for other in s:
1979
+ want, have = self.intersection(other), set(self)
1980
+
1981
+ remove, add = have - want, want - have
1982
+
1983
+ for value in remove:
1984
+ self.remove(value)
1985
+ for value in add:
1986
+ self.add(value)
1987
+
1988
+ def __iand__(self, s: AbstractSet[Any]) -> Self:
1989
+ if not collections._set_binops_check_strict(self, s):
1990
+ return NotImplemented
1991
+ want = self.intersection(s)
1992
+ have: Set[_T] = set(self)
1993
+
1994
+ remove, add = have - want, want - have
1995
+
1996
+ for value in remove:
1997
+ self.remove(value)
1998
+ for value in add:
1999
+ self.add(value)
2000
+ return self
2001
+
2002
+ def symmetric_difference(self, __s: Iterable[_T]) -> MutableSet[_T]:
2003
+ return set(self).symmetric_difference(__s)
2004
+
2005
+ def __xor__(self, s: AbstractSet[_S]) -> MutableSet[Union[_T, _S]]:
2006
+ if not collections._set_binops_check_strict(self, s):
2007
+ return NotImplemented
2008
+ return self.symmetric_difference(s)
2009
+
2010
+ def symmetric_difference_update(self, other: Iterable[Any]) -> None:
2011
+ want, have = self.symmetric_difference(other), set(self)
2012
+
2013
+ remove, add = have - want, want - have
2014
+
2015
+ for value in remove:
2016
+ self.remove(value)
2017
+ for value in add:
2018
+ self.add(value)
2019
+
2020
+ def __ixor__(self, other: AbstractSet[_S]) -> MutableSet[Union[_T, _S]]: # type: ignore # noqa: E501
2021
+ if not collections._set_binops_check_strict(self, other):
2022
+ return NotImplemented
2023
+
2024
+ self.symmetric_difference_update(other)
2025
+ return self
2026
+
2027
+ def issubset(self, __s: Iterable[Any]) -> bool:
2028
+ return set(self).issubset(__s)
2029
+
2030
+ def issuperset(self, __s: Iterable[Any]) -> bool:
2031
+ return set(self).issuperset(__s)
2032
+
2033
+ def clear(self) -> None:
2034
+ self.col.clear()
2035
+
2036
+ def copy(self) -> AbstractSet[_T]:
2037
+ return set(self)
2038
+
2039
+ def __eq__(self, other: object) -> bool:
2040
+ return set(self) == other
2041
+
2042
+ def __ne__(self, other: object) -> bool:
2043
+ return set(self) != other
2044
+
2045
+ def __lt__(self, other: AbstractSet[Any]) -> bool:
2046
+ return set(self) < other
2047
+
2048
+ def __le__(self, other: AbstractSet[Any]) -> bool:
2049
+ return set(self) <= other
2050
+
2051
+ def __gt__(self, other: AbstractSet[Any]) -> bool:
2052
+ return set(self) > other
2053
+
2054
+ def __ge__(self, other: AbstractSet[Any]) -> bool:
2055
+ return set(self) >= other
2056
+
2057
+ def __repr__(self) -> str:
2058
+ return repr(set(self))
2059
+
2060
+ def __hash__(self) -> NoReturn:
2061
+ raise TypeError("%s objects are unhashable" % type(self).__name__)
2062
+
2063
+ if not typing.TYPE_CHECKING:
2064
+ for func_name, func in list(locals().items()):
2065
+ if (
2066
+ callable(func)
2067
+ and func.__name__ == func_name
2068
+ and not func.__doc__
2069
+ and hasattr(set, func_name)
2070
+ ):
2071
+ func.__doc__ = getattr(set, func_name).__doc__
2072
+ del func_name, func