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,829 @@
1
+ # orm/path_registry.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
+ """Path tracking utilities, representing mapper graph traversals."""
8
+
9
+ from __future__ import annotations
10
+
11
+ from functools import reduce
12
+ from itertools import chain
13
+ import logging
14
+ import operator
15
+ from typing import Any
16
+ from typing import cast
17
+ from typing import Dict
18
+ from typing import Iterator
19
+ from typing import List
20
+ from typing import Optional
21
+ from typing import overload
22
+ from typing import Sequence
23
+ from typing import Tuple
24
+ from typing import TYPE_CHECKING
25
+ from typing import Union
26
+
27
+ from . import base as orm_base
28
+ from ._typing import insp_is_mapper_property
29
+ from .. import exc
30
+ from .. import util
31
+ from ..sql import visitors
32
+ from ..sql.cache_key import HasCacheKey
33
+
34
+ if TYPE_CHECKING:
35
+ from typing import TypeGuard
36
+
37
+ from ._typing import _InternalEntityType
38
+ from .interfaces import StrategizedProperty
39
+ from .mapper import Mapper
40
+ from .relationships import RelationshipProperty
41
+ from .util import AliasedInsp
42
+ from ..sql.cache_key import _CacheKeyTraversalType
43
+ from ..sql.elements import BindParameter
44
+ from ..sql.visitors import anon_map
45
+ from ..util.typing import _LiteralStar
46
+
47
+ def is_root(path: PathRegistry) -> TypeGuard[RootRegistry]: ...
48
+
49
+ def is_entity(
50
+ path: PathRegistry,
51
+ ) -> TypeGuard[_AbstractEntityRegistry]: ...
52
+
53
+ else:
54
+ is_root = operator.attrgetter("is_root")
55
+ is_entity = operator.attrgetter("is_entity")
56
+
57
+
58
+ _SerializedPath = List[Any]
59
+ _StrPathToken = str
60
+ _PathElementType = Union[
61
+ _StrPathToken, "_InternalEntityType[Any]", "StrategizedProperty[Any]"
62
+ ]
63
+
64
+ # the representation is in fact
65
+ # a tuple with alternating:
66
+ # [_InternalEntityType[Any], Union[str, StrategizedProperty[Any]],
67
+ # _InternalEntityType[Any], Union[str, StrategizedProperty[Any]], ...]
68
+ # this might someday be a tuple of 2-tuples instead, but paths can be
69
+ # chopped at odd intervals as well so this is less flexible
70
+ _PathRepresentation = Tuple[_PathElementType, ...]
71
+
72
+ # NOTE: these names are weird since the array is 0-indexed,
73
+ # the "_Odd" entries are at 0, 2, 4, etc
74
+ _OddPathRepresentation = Sequence["_InternalEntityType[Any]"]
75
+ _EvenPathRepresentation = Sequence[Union["StrategizedProperty[Any]", str]]
76
+
77
+
78
+ log = logging.getLogger(__name__)
79
+
80
+
81
+ def _unreduce_path(path: _SerializedPath) -> PathRegistry:
82
+ return PathRegistry.deserialize(path)
83
+
84
+
85
+ _WILDCARD_TOKEN: _LiteralStar = "*"
86
+ _DEFAULT_TOKEN = "_sa_default"
87
+
88
+
89
+ class PathRegistry(HasCacheKey):
90
+ """Represent query load paths and registry functions.
91
+
92
+ Basically represents structures like:
93
+
94
+ (<User mapper>, "orders", <Order mapper>, "items", <Item mapper>)
95
+
96
+ These structures are generated by things like
97
+ query options (joinedload(), subqueryload(), etc.) and are
98
+ used to compose keys stored in the query._attributes dictionary
99
+ for various options.
100
+
101
+ They are then re-composed at query compile/result row time as
102
+ the query is formed and as rows are fetched, where they again
103
+ serve to compose keys to look up options in the context.attributes
104
+ dictionary, which is copied from query._attributes.
105
+
106
+ The path structure has a limited amount of caching, where each
107
+ "root" ultimately pulls from a fixed registry associated with
108
+ the first mapper, that also contains elements for each of its
109
+ property keys. However paths longer than two elements, which
110
+ are the exception rather than the rule, are generated on an
111
+ as-needed basis.
112
+
113
+ """
114
+
115
+ __slots__ = ()
116
+
117
+ is_token = False
118
+ is_root = False
119
+ has_entity = False
120
+ is_property = False
121
+ is_entity = False
122
+
123
+ is_unnatural: bool
124
+
125
+ path: _PathRepresentation
126
+ natural_path: _PathRepresentation
127
+ parent: Optional[PathRegistry]
128
+ root: RootRegistry
129
+
130
+ _cache_key_traversal: _CacheKeyTraversalType = [
131
+ ("path", visitors.ExtendedInternalTraversal.dp_has_cache_key_list)
132
+ ]
133
+
134
+ def __eq__(self, other: Any) -> bool:
135
+ try:
136
+ return other is not None and self.path == other._path_for_compare
137
+ except AttributeError:
138
+ util.warn(
139
+ "Comparison of PathRegistry to %r is not supported"
140
+ % (type(other))
141
+ )
142
+ return False
143
+
144
+ def __ne__(self, other: Any) -> bool:
145
+ try:
146
+ return other is None or self.path != other._path_for_compare
147
+ except AttributeError:
148
+ util.warn(
149
+ "Comparison of PathRegistry to %r is not supported"
150
+ % (type(other))
151
+ )
152
+ return True
153
+
154
+ @property
155
+ def _path_for_compare(self) -> Optional[_PathRepresentation]:
156
+ return self.path
157
+
158
+ def odd_element(self, index: int) -> _InternalEntityType[Any]:
159
+ return self.path[index] # type: ignore
160
+
161
+ def set(self, attributes: Dict[Any, Any], key: Any, value: Any) -> None:
162
+ log.debug("set '%s' on path '%s' to '%s'", key, self, value)
163
+ attributes[(key, self.natural_path)] = value
164
+
165
+ def setdefault(
166
+ self, attributes: Dict[Any, Any], key: Any, value: Any
167
+ ) -> None:
168
+ log.debug("setdefault '%s' on path '%s' to '%s'", key, self, value)
169
+ attributes.setdefault((key, self.natural_path), value)
170
+
171
+ def get(
172
+ self, attributes: Dict[Any, Any], key: Any, value: Optional[Any] = None
173
+ ) -> Any:
174
+ key = (key, self.natural_path)
175
+ if key in attributes:
176
+ return attributes[key]
177
+ else:
178
+ return value
179
+
180
+ def __len__(self) -> int:
181
+ return len(self.path)
182
+
183
+ def __hash__(self) -> int:
184
+ return id(self)
185
+
186
+ @overload
187
+ def __getitem__(self, entity: _StrPathToken) -> _TokenRegistry: ...
188
+
189
+ @overload
190
+ def __getitem__(self, entity: int) -> _PathElementType: ...
191
+
192
+ @overload
193
+ def __getitem__(self, entity: slice) -> _PathRepresentation: ...
194
+
195
+ @overload
196
+ def __getitem__(
197
+ self, entity: _InternalEntityType[Any]
198
+ ) -> _AbstractEntityRegistry: ...
199
+
200
+ @overload
201
+ def __getitem__(
202
+ self, entity: StrategizedProperty[Any]
203
+ ) -> _PropRegistry: ...
204
+
205
+ def __getitem__(
206
+ self,
207
+ entity: Union[
208
+ _StrPathToken,
209
+ int,
210
+ slice,
211
+ _InternalEntityType[Any],
212
+ StrategizedProperty[Any],
213
+ ],
214
+ ) -> Union[
215
+ _TokenRegistry,
216
+ _PathElementType,
217
+ _PathRepresentation,
218
+ _PropRegistry,
219
+ _AbstractEntityRegistry,
220
+ ]:
221
+ raise NotImplementedError()
222
+
223
+ # TODO: what are we using this for?
224
+ @property
225
+ def length(self) -> int:
226
+ return len(self.path)
227
+
228
+ def pairs(
229
+ self,
230
+ ) -> Iterator[
231
+ Tuple[_InternalEntityType[Any], Union[str, StrategizedProperty[Any]]]
232
+ ]:
233
+ odd_path = cast(_OddPathRepresentation, self.path)
234
+ even_path = cast(_EvenPathRepresentation, odd_path)
235
+ for i in range(0, len(odd_path), 2):
236
+ yield odd_path[i], even_path[i + 1]
237
+
238
+ def contains_mapper(self, mapper: Mapper[Any]) -> bool:
239
+ _m_path = cast(_OddPathRepresentation, self.path)
240
+ for path_mapper in [_m_path[i] for i in range(0, len(_m_path), 2)]:
241
+ if path_mapper.mapper.isa(mapper):
242
+ return True
243
+ else:
244
+ return False
245
+
246
+ def contains(self, attributes: Dict[Any, Any], key: Any) -> bool:
247
+ return (key, self.path) in attributes
248
+
249
+ def __reduce__(self) -> Any:
250
+ return _unreduce_path, (self.serialize(),)
251
+
252
+ @classmethod
253
+ def _serialize_path(cls, path: _PathRepresentation) -> _SerializedPath:
254
+ _m_path = cast(_OddPathRepresentation, path)
255
+ _p_path = cast(_EvenPathRepresentation, path)
256
+
257
+ return list(
258
+ zip(
259
+ tuple(
260
+ m.class_ if (m.is_mapper or m.is_aliased_class) else str(m)
261
+ for m in [_m_path[i] for i in range(0, len(_m_path), 2)]
262
+ ),
263
+ tuple(
264
+ p.key if insp_is_mapper_property(p) else str(p)
265
+ for p in [_p_path[i] for i in range(1, len(_p_path), 2)]
266
+ )
267
+ + (None,),
268
+ )
269
+ )
270
+
271
+ @classmethod
272
+ def _deserialize_path(cls, path: _SerializedPath) -> _PathRepresentation:
273
+ def _deserialize_mapper_token(mcls: Any) -> Any:
274
+ return (
275
+ # note: we likely dont want configure=True here however
276
+ # this is maintained at the moment for backwards compatibility
277
+ orm_base._inspect_mapped_class(mcls, configure=True)
278
+ if mcls not in PathToken._intern
279
+ else PathToken._intern[mcls]
280
+ )
281
+
282
+ def _deserialize_key_token(mcls: Any, key: Any) -> Any:
283
+ if key is None:
284
+ return None
285
+ elif key in PathToken._intern:
286
+ return PathToken._intern[key]
287
+ else:
288
+ mp = orm_base._inspect_mapped_class(mcls, configure=True)
289
+ assert mp is not None
290
+ return mp.attrs[key]
291
+
292
+ p = tuple(
293
+ chain(
294
+ *[
295
+ (
296
+ _deserialize_mapper_token(mcls),
297
+ _deserialize_key_token(mcls, key),
298
+ )
299
+ for mcls, key in path
300
+ ]
301
+ )
302
+ )
303
+ if p and p[-1] is None:
304
+ p = p[0:-1]
305
+ return p
306
+
307
+ def serialize(self) -> _SerializedPath:
308
+ path = self.path
309
+ return self._serialize_path(path)
310
+
311
+ @classmethod
312
+ def deserialize(cls, path: _SerializedPath) -> PathRegistry:
313
+ assert path is not None
314
+ p = cls._deserialize_path(path)
315
+ return cls.coerce(p)
316
+
317
+ @overload
318
+ @classmethod
319
+ def per_mapper(cls, mapper: Mapper[Any]) -> _CachingEntityRegistry: ...
320
+
321
+ @overload
322
+ @classmethod
323
+ def per_mapper(cls, mapper: AliasedInsp[Any]) -> _SlotsEntityRegistry: ...
324
+
325
+ @classmethod
326
+ def per_mapper(
327
+ cls, mapper: _InternalEntityType[Any]
328
+ ) -> _AbstractEntityRegistry:
329
+ if mapper.is_mapper:
330
+ return _CachingEntityRegistry(cls.root, mapper)
331
+ else:
332
+ return _SlotsEntityRegistry(cls.root, mapper)
333
+
334
+ @classmethod
335
+ def coerce(cls, raw: _PathRepresentation) -> PathRegistry:
336
+ def _red(prev: PathRegistry, next_: _PathElementType) -> PathRegistry:
337
+ return prev[next_]
338
+
339
+ # can't quite get mypy to appreciate this one :)
340
+ return reduce(_red, raw, cls.root) # type: ignore
341
+
342
+ def __add__(self, other: PathRegistry) -> PathRegistry:
343
+ def _red(prev: PathRegistry, next_: _PathElementType) -> PathRegistry:
344
+ return prev[next_]
345
+
346
+ return reduce(_red, other.path, self)
347
+
348
+ def __str__(self) -> str:
349
+ return f"ORM Path[{' -> '.join(str(elem) for elem in self.path)}]"
350
+
351
+ def __repr__(self) -> str:
352
+ return f"{self.__class__.__name__}({self.path!r})"
353
+
354
+
355
+ class _CreatesToken(PathRegistry):
356
+ __slots__ = ()
357
+
358
+ is_aliased_class: bool
359
+ is_root: bool
360
+
361
+ def token(self, token: _StrPathToken) -> _TokenRegistry:
362
+ if token.endswith(f":{_WILDCARD_TOKEN}"):
363
+ return _TokenRegistry(self, token)
364
+ elif token.endswith(f":{_DEFAULT_TOKEN}"):
365
+ return _TokenRegistry(self.root, token)
366
+ else:
367
+ raise exc.ArgumentError(f"invalid token: {token}")
368
+
369
+
370
+ class RootRegistry(_CreatesToken):
371
+ """Root registry, defers to mappers so that
372
+ paths are maintained per-root-mapper.
373
+
374
+ """
375
+
376
+ __slots__ = ()
377
+
378
+ inherit_cache = True
379
+
380
+ path = natural_path = ()
381
+ has_entity = False
382
+ is_aliased_class = False
383
+ is_root = True
384
+ is_unnatural = False
385
+
386
+ def _getitem(
387
+ self, entity: Any
388
+ ) -> Union[_TokenRegistry, _AbstractEntityRegistry]:
389
+ if entity in PathToken._intern:
390
+ if TYPE_CHECKING:
391
+ assert isinstance(entity, _StrPathToken)
392
+ return _TokenRegistry(self, PathToken._intern[entity])
393
+ else:
394
+ try:
395
+ return entity._path_registry # type: ignore
396
+ except AttributeError:
397
+ raise IndexError(
398
+ f"invalid argument for RootRegistry.__getitem__: {entity}"
399
+ )
400
+
401
+ def _truncate_recursive(self) -> RootRegistry:
402
+ return self
403
+
404
+ if not TYPE_CHECKING:
405
+ __getitem__ = _getitem
406
+
407
+
408
+ PathRegistry.root = RootRegistry()
409
+
410
+
411
+ class PathToken(orm_base.InspectionAttr, HasCacheKey, str):
412
+ """cacheable string token"""
413
+
414
+ _intern: Dict[str, PathToken] = {}
415
+
416
+ def _gen_cache_key(
417
+ self, anon_map: anon_map, bindparams: List[BindParameter[Any]]
418
+ ) -> Tuple[Any, ...]:
419
+ return (str(self),)
420
+
421
+ @property
422
+ def _path_for_compare(self) -> Optional[_PathRepresentation]:
423
+ return None
424
+
425
+ @classmethod
426
+ def intern(cls, strvalue: str) -> PathToken:
427
+ if strvalue in cls._intern:
428
+ return cls._intern[strvalue]
429
+ else:
430
+ cls._intern[strvalue] = result = PathToken(strvalue)
431
+ return result
432
+
433
+
434
+ class _TokenRegistry(PathRegistry):
435
+ __slots__ = ("token", "parent", "path", "natural_path")
436
+
437
+ inherit_cache = True
438
+
439
+ token: _StrPathToken
440
+ parent: _CreatesToken
441
+
442
+ def __init__(self, parent: _CreatesToken, token: _StrPathToken):
443
+ token = PathToken.intern(token)
444
+
445
+ self.token = token
446
+ self.parent = parent
447
+ self.path = parent.path + (token,)
448
+ self.natural_path = parent.natural_path + (token,)
449
+
450
+ has_entity = False
451
+
452
+ is_token = True
453
+
454
+ def generate_for_superclasses(self) -> Iterator[PathRegistry]:
455
+ # NOTE: this method is no longer used. consider removal
456
+ parent = self.parent
457
+ if is_root(parent):
458
+ yield self
459
+ return
460
+
461
+ if TYPE_CHECKING:
462
+ assert isinstance(parent, _AbstractEntityRegistry)
463
+ if not parent.is_aliased_class:
464
+ for mp_ent in parent.mapper.iterate_to_root():
465
+ yield _TokenRegistry(parent.parent[mp_ent], self.token)
466
+ elif (
467
+ parent.is_aliased_class
468
+ and cast(
469
+ "AliasedInsp[Any]",
470
+ parent.entity,
471
+ )._is_with_polymorphic
472
+ ):
473
+ yield self
474
+ for ent in cast(
475
+ "AliasedInsp[Any]", parent.entity
476
+ )._with_polymorphic_entities:
477
+ yield _TokenRegistry(parent.parent[ent], self.token)
478
+ else:
479
+ yield self
480
+
481
+ def _generate_natural_for_superclasses(
482
+ self,
483
+ ) -> Iterator[_PathRepresentation]:
484
+ parent = self.parent
485
+ if is_root(parent):
486
+ yield self.natural_path
487
+ return
488
+
489
+ if TYPE_CHECKING:
490
+ assert isinstance(parent, _AbstractEntityRegistry)
491
+ for mp_ent in parent.mapper.iterate_to_root():
492
+ yield _TokenRegistry(
493
+ parent.parent[mp_ent], self.token
494
+ ).natural_path
495
+ if (
496
+ parent.is_aliased_class
497
+ and cast(
498
+ "AliasedInsp[Any]",
499
+ parent.entity,
500
+ )._is_with_polymorphic
501
+ ):
502
+ yield self.natural_path
503
+ for ent in cast(
504
+ "AliasedInsp[Any]", parent.entity
505
+ )._with_polymorphic_entities:
506
+ yield (
507
+ _TokenRegistry(parent.parent[ent], self.token).natural_path
508
+ )
509
+ else:
510
+ yield self.natural_path
511
+
512
+ def _getitem(self, entity: Any) -> Any:
513
+ try:
514
+ return self.path[entity]
515
+ except TypeError as err:
516
+ raise IndexError(f"{entity}") from err
517
+
518
+ if not TYPE_CHECKING:
519
+ __getitem__ = _getitem
520
+
521
+
522
+ class _PropRegistry(PathRegistry):
523
+ __slots__ = (
524
+ "prop",
525
+ "parent",
526
+ "path",
527
+ "natural_path",
528
+ "has_entity",
529
+ "entity",
530
+ "mapper",
531
+ "_wildcard_path_loader_key",
532
+ "_default_path_loader_key",
533
+ "_loader_key",
534
+ "is_unnatural",
535
+ )
536
+ inherit_cache = True
537
+ is_property = True
538
+
539
+ prop: StrategizedProperty[Any]
540
+ mapper: Optional[Mapper[Any]]
541
+ entity: Optional[_InternalEntityType[Any]]
542
+
543
+ def __init__(
544
+ self, parent: _AbstractEntityRegistry, prop: StrategizedProperty[Any]
545
+ ):
546
+
547
+ # restate this path in terms of the
548
+ # given StrategizedProperty's parent.
549
+ insp = cast("_InternalEntityType[Any]", parent[-1])
550
+ natural_parent: _AbstractEntityRegistry = parent
551
+
552
+ # inherit "is_unnatural" from the parent
553
+ self.is_unnatural = parent.parent.is_unnatural or bool(
554
+ parent.mapper.inherits
555
+ )
556
+
557
+ if not insp.is_aliased_class or insp._use_mapper_path: # type: ignore
558
+ parent = natural_parent = parent.parent[prop.parent]
559
+ elif (
560
+ insp.is_aliased_class
561
+ and insp.with_polymorphic_mappers
562
+ and prop.parent in insp.with_polymorphic_mappers
563
+ ):
564
+ subclass_entity: _InternalEntityType[Any] = parent[-1]._entity_for_mapper(prop.parent) # type: ignore # noqa: E501
565
+ parent = parent.parent[subclass_entity]
566
+
567
+ # when building a path where with_polymorphic() is in use,
568
+ # special logic to determine the "natural path" when subclass
569
+ # entities are used.
570
+ #
571
+ # here we are trying to distinguish between a path that starts
572
+ # on a with_polymorphic entity vs. one that starts on a
573
+ # normal entity that introduces a with_polymorphic() in the
574
+ # middle using of_type():
575
+ #
576
+ # # as in test_polymorphic_rel->
577
+ # # test_subqueryload_on_subclass_uses_path_correctly
578
+ # wp = with_polymorphic(RegularEntity, "*")
579
+ # sess.query(wp).options(someload(wp.SomeSubEntity.foos))
580
+ #
581
+ # vs
582
+ #
583
+ # # as in test_relationship->JoinedloadWPolyOfTypeContinued
584
+ # wp = with_polymorphic(SomeFoo, "*")
585
+ # sess.query(RegularEntity).options(
586
+ # someload(RegularEntity.foos.of_type(wp))
587
+ # .someload(wp.SubFoo.bar)
588
+ # )
589
+ #
590
+ # in the former case, the Query as it generates a path that we
591
+ # want to match will be in terms of the with_polymorphic at the
592
+ # beginning. in the latter case, Query will generate simple
593
+ # paths that don't know about this with_polymorphic, so we must
594
+ # use a separate natural path.
595
+ #
596
+ #
597
+ if parent.parent:
598
+ natural_parent = parent.parent[subclass_entity.mapper]
599
+ self.is_unnatural = True
600
+ else:
601
+ natural_parent = parent
602
+ elif (
603
+ natural_parent.parent
604
+ and insp.is_aliased_class
605
+ and prop.parent # this should always be the case here
606
+ is not insp.mapper
607
+ and insp.mapper.isa(prop.parent)
608
+ ):
609
+ natural_parent = parent.parent[prop.parent]
610
+
611
+ self.prop = prop
612
+ self.parent = parent
613
+ self.path = parent.path + (prop,)
614
+ self.natural_path = natural_parent.natural_path + (prop,)
615
+
616
+ self.has_entity = prop._links_to_entity
617
+ if prop._is_relationship:
618
+ if TYPE_CHECKING:
619
+ assert isinstance(prop, RelationshipProperty)
620
+ self.entity = prop.entity
621
+ self.mapper = prop.mapper
622
+ else:
623
+ self.entity = None
624
+ self.mapper = None
625
+
626
+ self._wildcard_path_loader_key = (
627
+ "loader",
628
+ parent.natural_path + self.prop._wildcard_token,
629
+ )
630
+ self._default_path_loader_key = self.prop._default_path_loader_key
631
+ self._loader_key = ("loader", self.natural_path)
632
+
633
+ def _truncate_recursive(self) -> _PropRegistry:
634
+ earliest = None
635
+ for i, token in enumerate(reversed(self.path[:-1])):
636
+ if token is self.prop:
637
+ earliest = i
638
+
639
+ if earliest is None:
640
+ return self
641
+ else:
642
+ return self.coerce(self.path[0 : -(earliest + 1)]) # type: ignore
643
+
644
+ @property
645
+ def entity_path(self) -> _AbstractEntityRegistry:
646
+ assert self.entity is not None
647
+ return self[self.entity]
648
+
649
+ def _getitem(
650
+ self, entity: Union[int, slice, _InternalEntityType[Any]]
651
+ ) -> Union[_AbstractEntityRegistry, _PathElementType, _PathRepresentation]:
652
+ if isinstance(entity, (int, slice)):
653
+ return self.path[entity]
654
+ else:
655
+ return _SlotsEntityRegistry(self, entity)
656
+
657
+ if not TYPE_CHECKING:
658
+ __getitem__ = _getitem
659
+
660
+
661
+ class _AbstractEntityRegistry(_CreatesToken):
662
+ __slots__ = (
663
+ "key",
664
+ "parent",
665
+ "is_aliased_class",
666
+ "path",
667
+ "entity",
668
+ "natural_path",
669
+ )
670
+
671
+ has_entity = True
672
+ is_entity = True
673
+
674
+ parent: Union[RootRegistry, _PropRegistry]
675
+ key: _InternalEntityType[Any]
676
+ entity: _InternalEntityType[Any]
677
+ is_aliased_class: bool
678
+
679
+ def __init__(
680
+ self,
681
+ parent: Union[RootRegistry, _PropRegistry],
682
+ entity: _InternalEntityType[Any],
683
+ ):
684
+ self.key = entity
685
+ self.parent = parent
686
+ self.is_aliased_class = entity.is_aliased_class
687
+ self.entity = entity
688
+ self.path = parent.path + (entity,)
689
+
690
+ # the "natural path" is the path that we get when Query is traversing
691
+ # from the lead entities into the various relationships; it corresponds
692
+ # to the structure of mappers and relationships. when we are given a
693
+ # path that comes from loader options, as of 1.3 it can have ac-hoc
694
+ # with_polymorphic() and other AliasedInsp objects inside of it, which
695
+ # are usually not present in mappings. So here we track both the
696
+ # "enhanced" path in self.path and the "natural" path that doesn't
697
+ # include those objects so these two traversals can be matched up.
698
+
699
+ # the test here for "(self.is_aliased_class or parent.is_unnatural)"
700
+ # are to avoid the more expensive conditional logic that follows if we
701
+ # know we don't have to do it. This conditional can just as well be
702
+ # "if parent.path:", it just is more function calls.
703
+ #
704
+ # This is basically the only place that the "is_unnatural" flag
705
+ # actually changes behavior.
706
+ if parent.path and (self.is_aliased_class or parent.is_unnatural):
707
+ # this is an infrequent code path used for loader strategies that
708
+ # also make use of of_type() or other intricate polymorphic
709
+ # base/subclass combinations
710
+ parent_natural_entity = parent.natural_path[-1]
711
+
712
+ if entity.mapper.isa(
713
+ parent_natural_entity.mapper # type: ignore
714
+ ) or parent_natural_entity.mapper.isa( # type: ignore
715
+ entity.mapper
716
+ ):
717
+ # when the entity mapper and parent mapper are in an
718
+ # inheritance relationship, use entity.mapper in natural_path.
719
+ # First case: entity.mapper inherits from parent mapper (e.g.,
720
+ # accessing a subclass mapper through parent path). Second case
721
+ # (issue #13193): parent mapper inherits from entity.mapper
722
+ # (e.g., parent path has Sub(Base) but we're accessing with
723
+ # Base where Base.related is declared, so use Base in
724
+ # natural_path).
725
+ self.natural_path = parent.natural_path + (entity.mapper,)
726
+ else:
727
+ self.natural_path = parent.natural_path + (
728
+ parent_natural_entity.entity, # type: ignore
729
+ )
730
+ # it seems to make sense that since these paths get mixed up
731
+ # with statements that are cached or not, we should make
732
+ # sure the natural path is cacheable across different occurrences
733
+ # of equivalent AliasedClass objects. however, so far this
734
+ # does not seem to be needed for whatever reason.
735
+ # elif not parent.path and self.is_aliased_class:
736
+ # self.natural_path = (self.entity._generate_cache_key()[0], )
737
+ else:
738
+ self.natural_path = self.path
739
+
740
+ def _truncate_recursive(self) -> _AbstractEntityRegistry:
741
+ return self.parent._truncate_recursive()[self.entity]
742
+
743
+ @property
744
+ def root_entity(self) -> _InternalEntityType[Any]:
745
+ return self.odd_element(0)
746
+
747
+ @property
748
+ def entity_path(self) -> PathRegistry:
749
+ return self
750
+
751
+ @property
752
+ def mapper(self) -> Mapper[Any]:
753
+ return self.entity.mapper
754
+
755
+ def __bool__(self) -> bool:
756
+ return True
757
+
758
+ def _getitem(
759
+ self, entity: Any
760
+ ) -> Union[_PathElementType, _PathRepresentation, PathRegistry]:
761
+ if isinstance(entity, (int, slice)):
762
+ return self.path[entity]
763
+ elif entity in PathToken._intern:
764
+ return _TokenRegistry(self, PathToken._intern[entity])
765
+ else:
766
+ return _PropRegistry(self, entity)
767
+
768
+ if not TYPE_CHECKING:
769
+ __getitem__ = _getitem
770
+
771
+
772
+ class _SlotsEntityRegistry(_AbstractEntityRegistry):
773
+ # for aliased class, return lightweight, no-cycles created
774
+ # version
775
+ inherit_cache = True
776
+
777
+
778
+ class _ERDict(Dict[Any, Any]):
779
+ def __init__(self, registry: _CachingEntityRegistry):
780
+ self.registry = registry
781
+
782
+ def __missing__(self, key: Any) -> _PropRegistry:
783
+ self[key] = item = _PropRegistry(self.registry, key)
784
+
785
+ return item
786
+
787
+
788
+ class _CachingEntityRegistry(_AbstractEntityRegistry):
789
+ # for long lived mapper, return dict based caching
790
+ # version that creates reference cycles
791
+
792
+ __slots__ = ("_cache",)
793
+
794
+ inherit_cache = True
795
+
796
+ def __init__(
797
+ self,
798
+ parent: Union[RootRegistry, _PropRegistry],
799
+ entity: _InternalEntityType[Any],
800
+ ):
801
+ super().__init__(parent, entity)
802
+ self._cache = _ERDict(self)
803
+
804
+ def pop(self, key: Any, default: Any) -> Any:
805
+ return self._cache.pop(key, default)
806
+
807
+ def _getitem(self, entity: Any) -> Any:
808
+ if isinstance(entity, (int, slice)):
809
+ return self.path[entity]
810
+ elif isinstance(entity, PathToken):
811
+ return _TokenRegistry(self, entity)
812
+ else:
813
+ return self._cache[entity]
814
+
815
+ if not TYPE_CHECKING:
816
+ __getitem__ = _getitem
817
+
818
+
819
+ if TYPE_CHECKING:
820
+
821
+ def path_is_entity(
822
+ path: PathRegistry,
823
+ ) -> TypeGuard[_AbstractEntityRegistry]: ...
824
+
825
+ def path_is_property(path: PathRegistry) -> TypeGuard[_PropRegistry]: ...
826
+
827
+ else:
828
+ path_is_entity = operator.attrgetter("is_entity")
829
+ path_is_property = operator.attrgetter("is_property")