SQLAlchemy 2.0.47__cp313-cp313t-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. sqlalchemy/__init__.py +283 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +184 -0
  4. sqlalchemy/connectors/asyncio.py +429 -0
  5. sqlalchemy/connectors/pyodbc.py +250 -0
  6. sqlalchemy/cyextension/__init__.py +6 -0
  7. sqlalchemy/cyextension/collections.cp313t-win_amd64.pyd +0 -0
  8. sqlalchemy/cyextension/collections.pyx +409 -0
  9. sqlalchemy/cyextension/immutabledict.cp313t-win_amd64.pyd +0 -0
  10. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  11. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  12. sqlalchemy/cyextension/processors.cp313t-win_amd64.pyd +0 -0
  13. sqlalchemy/cyextension/processors.pyx +68 -0
  14. sqlalchemy/cyextension/resultproxy.cp313t-win_amd64.pyd +0 -0
  15. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  16. sqlalchemy/cyextension/util.cp313t-win_amd64.pyd +0 -0
  17. sqlalchemy/cyextension/util.pyx +90 -0
  18. sqlalchemy/dialects/__init__.py +62 -0
  19. sqlalchemy/dialects/_typing.py +30 -0
  20. sqlalchemy/dialects/mssql/__init__.py +88 -0
  21. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  22. sqlalchemy/dialects/mssql/base.py +4093 -0
  23. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  24. sqlalchemy/dialects/mssql/json.py +129 -0
  25. sqlalchemy/dialects/mssql/provision.py +185 -0
  26. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  27. sqlalchemy/dialects/mssql/pyodbc.py +760 -0
  28. sqlalchemy/dialects/mysql/__init__.py +104 -0
  29. sqlalchemy/dialects/mysql/aiomysql.py +250 -0
  30. sqlalchemy/dialects/mysql/asyncmy.py +231 -0
  31. sqlalchemy/dialects/mysql/base.py +3949 -0
  32. sqlalchemy/dialects/mysql/cymysql.py +106 -0
  33. sqlalchemy/dialects/mysql/dml.py +225 -0
  34. sqlalchemy/dialects/mysql/enumerated.py +282 -0
  35. sqlalchemy/dialects/mysql/expression.py +146 -0
  36. sqlalchemy/dialects/mysql/json.py +91 -0
  37. sqlalchemy/dialects/mysql/mariadb.py +72 -0
  38. sqlalchemy/dialects/mysql/mariadbconnector.py +322 -0
  39. sqlalchemy/dialects/mysql/mysqlconnector.py +302 -0
  40. sqlalchemy/dialects/mysql/mysqldb.py +314 -0
  41. sqlalchemy/dialects/mysql/provision.py +153 -0
  42. sqlalchemy/dialects/mysql/pymysql.py +158 -0
  43. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  44. sqlalchemy/dialects/mysql/reflection.py +727 -0
  45. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  46. sqlalchemy/dialects/mysql/types.py +835 -0
  47. sqlalchemy/dialects/oracle/__init__.py +81 -0
  48. sqlalchemy/dialects/oracle/base.py +3802 -0
  49. sqlalchemy/dialects/oracle/cx_oracle.py +1555 -0
  50. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  51. sqlalchemy/dialects/oracle/oracledb.py +941 -0
  52. sqlalchemy/dialects/oracle/provision.py +297 -0
  53. sqlalchemy/dialects/oracle/types.py +316 -0
  54. sqlalchemy/dialects/oracle/vector.py +365 -0
  55. sqlalchemy/dialects/postgresql/__init__.py +167 -0
  56. sqlalchemy/dialects/postgresql/_psycopg_common.py +189 -0
  57. sqlalchemy/dialects/postgresql/array.py +519 -0
  58. sqlalchemy/dialects/postgresql/asyncpg.py +1284 -0
  59. sqlalchemy/dialects/postgresql/base.py +5378 -0
  60. sqlalchemy/dialects/postgresql/dml.py +339 -0
  61. sqlalchemy/dialects/postgresql/ext.py +540 -0
  62. sqlalchemy/dialects/postgresql/hstore.py +406 -0
  63. sqlalchemy/dialects/postgresql/json.py +404 -0
  64. sqlalchemy/dialects/postgresql/named_types.py +524 -0
  65. sqlalchemy/dialects/postgresql/operators.py +129 -0
  66. sqlalchemy/dialects/postgresql/pg8000.py +669 -0
  67. sqlalchemy/dialects/postgresql/pg_catalog.py +326 -0
  68. sqlalchemy/dialects/postgresql/provision.py +183 -0
  69. sqlalchemy/dialects/postgresql/psycopg.py +862 -0
  70. sqlalchemy/dialects/postgresql/psycopg2.py +892 -0
  71. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  72. sqlalchemy/dialects/postgresql/ranges.py +1031 -0
  73. sqlalchemy/dialects/postgresql/types.py +313 -0
  74. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  75. sqlalchemy/dialects/sqlite/aiosqlite.py +482 -0
  76. sqlalchemy/dialects/sqlite/base.py +3056 -0
  77. sqlalchemy/dialects/sqlite/dml.py +263 -0
  78. sqlalchemy/dialects/sqlite/json.py +92 -0
  79. sqlalchemy/dialects/sqlite/provision.py +229 -0
  80. sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
  81. sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
  82. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  83. sqlalchemy/engine/__init__.py +62 -0
  84. sqlalchemy/engine/_py_processors.py +136 -0
  85. sqlalchemy/engine/_py_row.py +128 -0
  86. sqlalchemy/engine/_py_util.py +74 -0
  87. sqlalchemy/engine/base.py +3390 -0
  88. sqlalchemy/engine/characteristics.py +155 -0
  89. sqlalchemy/engine/create.py +893 -0
  90. sqlalchemy/engine/cursor.py +2298 -0
  91. sqlalchemy/engine/default.py +2394 -0
  92. sqlalchemy/engine/events.py +965 -0
  93. sqlalchemy/engine/interfaces.py +3471 -0
  94. sqlalchemy/engine/mock.py +134 -0
  95. sqlalchemy/engine/processors.py +61 -0
  96. sqlalchemy/engine/reflection.py +2102 -0
  97. sqlalchemy/engine/result.py +2399 -0
  98. sqlalchemy/engine/row.py +400 -0
  99. sqlalchemy/engine/strategies.py +16 -0
  100. sqlalchemy/engine/url.py +924 -0
  101. sqlalchemy/engine/util.py +167 -0
  102. sqlalchemy/event/__init__.py +26 -0
  103. sqlalchemy/event/api.py +220 -0
  104. sqlalchemy/event/attr.py +676 -0
  105. sqlalchemy/event/base.py +472 -0
  106. sqlalchemy/event/legacy.py +258 -0
  107. sqlalchemy/event/registry.py +390 -0
  108. sqlalchemy/events.py +17 -0
  109. sqlalchemy/exc.py +832 -0
  110. sqlalchemy/ext/__init__.py +11 -0
  111. sqlalchemy/ext/associationproxy.py +2027 -0
  112. sqlalchemy/ext/asyncio/__init__.py +25 -0
  113. sqlalchemy/ext/asyncio/base.py +281 -0
  114. sqlalchemy/ext/asyncio/engine.py +1471 -0
  115. sqlalchemy/ext/asyncio/exc.py +21 -0
  116. sqlalchemy/ext/asyncio/result.py +965 -0
  117. sqlalchemy/ext/asyncio/scoping.py +1599 -0
  118. sqlalchemy/ext/asyncio/session.py +1947 -0
  119. sqlalchemy/ext/automap.py +1701 -0
  120. sqlalchemy/ext/baked.py +570 -0
  121. sqlalchemy/ext/compiler.py +600 -0
  122. sqlalchemy/ext/declarative/__init__.py +65 -0
  123. sqlalchemy/ext/declarative/extensions.py +564 -0
  124. sqlalchemy/ext/horizontal_shard.py +478 -0
  125. sqlalchemy/ext/hybrid.py +1535 -0
  126. sqlalchemy/ext/indexable.py +364 -0
  127. sqlalchemy/ext/instrumentation.py +450 -0
  128. sqlalchemy/ext/mutable.py +1085 -0
  129. sqlalchemy/ext/mypy/__init__.py +6 -0
  130. sqlalchemy/ext/mypy/apply.py +324 -0
  131. sqlalchemy/ext/mypy/decl_class.py +515 -0
  132. sqlalchemy/ext/mypy/infer.py +590 -0
  133. sqlalchemy/ext/mypy/names.py +335 -0
  134. sqlalchemy/ext/mypy/plugin.py +303 -0
  135. sqlalchemy/ext/mypy/util.py +357 -0
  136. sqlalchemy/ext/orderinglist.py +439 -0
  137. sqlalchemy/ext/serializer.py +185 -0
  138. sqlalchemy/future/__init__.py +16 -0
  139. sqlalchemy/future/engine.py +15 -0
  140. sqlalchemy/inspection.py +174 -0
  141. sqlalchemy/log.py +288 -0
  142. sqlalchemy/orm/__init__.py +171 -0
  143. sqlalchemy/orm/_orm_constructors.py +2661 -0
  144. sqlalchemy/orm/_typing.py +179 -0
  145. sqlalchemy/orm/attributes.py +2845 -0
  146. sqlalchemy/orm/base.py +971 -0
  147. sqlalchemy/orm/bulk_persistence.py +2135 -0
  148. sqlalchemy/orm/clsregistry.py +571 -0
  149. sqlalchemy/orm/collections.py +1627 -0
  150. sqlalchemy/orm/context.py +3334 -0
  151. sqlalchemy/orm/decl_api.py +2004 -0
  152. sqlalchemy/orm/decl_base.py +2192 -0
  153. sqlalchemy/orm/dependency.py +1302 -0
  154. sqlalchemy/orm/descriptor_props.py +1092 -0
  155. sqlalchemy/orm/dynamic.py +300 -0
  156. sqlalchemy/orm/evaluator.py +379 -0
  157. sqlalchemy/orm/events.py +3252 -0
  158. sqlalchemy/orm/exc.py +237 -0
  159. sqlalchemy/orm/identity.py +302 -0
  160. sqlalchemy/orm/instrumentation.py +754 -0
  161. sqlalchemy/orm/interfaces.py +1496 -0
  162. sqlalchemy/orm/loading.py +1686 -0
  163. sqlalchemy/orm/mapped_collection.py +557 -0
  164. sqlalchemy/orm/mapper.py +4444 -0
  165. sqlalchemy/orm/path_registry.py +809 -0
  166. sqlalchemy/orm/persistence.py +1788 -0
  167. sqlalchemy/orm/properties.py +935 -0
  168. sqlalchemy/orm/query.py +3459 -0
  169. sqlalchemy/orm/relationships.py +3508 -0
  170. sqlalchemy/orm/scoping.py +2148 -0
  171. sqlalchemy/orm/session.py +5280 -0
  172. sqlalchemy/orm/state.py +1168 -0
  173. sqlalchemy/orm/state_changes.py +196 -0
  174. sqlalchemy/orm/strategies.py +3470 -0
  175. sqlalchemy/orm/strategy_options.py +2568 -0
  176. sqlalchemy/orm/sync.py +164 -0
  177. sqlalchemy/orm/unitofwork.py +796 -0
  178. sqlalchemy/orm/util.py +2403 -0
  179. sqlalchemy/orm/writeonly.py +674 -0
  180. sqlalchemy/pool/__init__.py +44 -0
  181. sqlalchemy/pool/base.py +1524 -0
  182. sqlalchemy/pool/events.py +375 -0
  183. sqlalchemy/pool/impl.py +588 -0
  184. sqlalchemy/py.typed +0 -0
  185. sqlalchemy/schema.py +69 -0
  186. sqlalchemy/sql/__init__.py +145 -0
  187. sqlalchemy/sql/_dml_constructors.py +132 -0
  188. sqlalchemy/sql/_elements_constructors.py +1872 -0
  189. sqlalchemy/sql/_orm_types.py +20 -0
  190. sqlalchemy/sql/_py_util.py +75 -0
  191. sqlalchemy/sql/_selectable_constructors.py +763 -0
  192. sqlalchemy/sql/_typing.py +482 -0
  193. sqlalchemy/sql/annotation.py +587 -0
  194. sqlalchemy/sql/base.py +2293 -0
  195. sqlalchemy/sql/cache_key.py +1057 -0
  196. sqlalchemy/sql/coercions.py +1404 -0
  197. sqlalchemy/sql/compiler.py +8081 -0
  198. sqlalchemy/sql/crud.py +1752 -0
  199. sqlalchemy/sql/ddl.py +1444 -0
  200. sqlalchemy/sql/default_comparator.py +551 -0
  201. sqlalchemy/sql/dml.py +1850 -0
  202. sqlalchemy/sql/elements.py +5589 -0
  203. sqlalchemy/sql/events.py +458 -0
  204. sqlalchemy/sql/expression.py +159 -0
  205. sqlalchemy/sql/functions.py +2158 -0
  206. sqlalchemy/sql/lambdas.py +1442 -0
  207. sqlalchemy/sql/naming.py +209 -0
  208. sqlalchemy/sql/operators.py +2623 -0
  209. sqlalchemy/sql/roles.py +323 -0
  210. sqlalchemy/sql/schema.py +6222 -0
  211. sqlalchemy/sql/selectable.py +7265 -0
  212. sqlalchemy/sql/sqltypes.py +3930 -0
  213. sqlalchemy/sql/traversals.py +1024 -0
  214. sqlalchemy/sql/type_api.py +2368 -0
  215. sqlalchemy/sql/util.py +1485 -0
  216. sqlalchemy/sql/visitors.py +1164 -0
  217. sqlalchemy/testing/__init__.py +96 -0
  218. sqlalchemy/testing/assertions.py +994 -0
  219. sqlalchemy/testing/assertsql.py +520 -0
  220. sqlalchemy/testing/asyncio.py +135 -0
  221. sqlalchemy/testing/config.py +434 -0
  222. sqlalchemy/testing/engines.py +483 -0
  223. sqlalchemy/testing/entities.py +117 -0
  224. sqlalchemy/testing/exclusions.py +476 -0
  225. sqlalchemy/testing/fixtures/__init__.py +28 -0
  226. sqlalchemy/testing/fixtures/base.py +384 -0
  227. sqlalchemy/testing/fixtures/mypy.py +332 -0
  228. sqlalchemy/testing/fixtures/orm.py +227 -0
  229. sqlalchemy/testing/fixtures/sql.py +482 -0
  230. sqlalchemy/testing/pickleable.py +155 -0
  231. sqlalchemy/testing/plugin/__init__.py +6 -0
  232. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  233. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  234. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  235. sqlalchemy/testing/profiling.py +329 -0
  236. sqlalchemy/testing/provision.py +603 -0
  237. sqlalchemy/testing/requirements.py +1945 -0
  238. sqlalchemy/testing/schema.py +198 -0
  239. sqlalchemy/testing/suite/__init__.py +19 -0
  240. sqlalchemy/testing/suite/test_cte.py +237 -0
  241. sqlalchemy/testing/suite/test_ddl.py +389 -0
  242. sqlalchemy/testing/suite/test_deprecations.py +153 -0
  243. sqlalchemy/testing/suite/test_dialect.py +776 -0
  244. sqlalchemy/testing/suite/test_insert.py +630 -0
  245. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  246. sqlalchemy/testing/suite/test_results.py +504 -0
  247. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  248. sqlalchemy/testing/suite/test_select.py +2010 -0
  249. sqlalchemy/testing/suite/test_sequence.py +317 -0
  250. sqlalchemy/testing/suite/test_types.py +2147 -0
  251. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  252. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  253. sqlalchemy/testing/util.py +535 -0
  254. sqlalchemy/testing/warnings.py +52 -0
  255. sqlalchemy/types.py +74 -0
  256. sqlalchemy/util/__init__.py +162 -0
  257. sqlalchemy/util/_collections.py +712 -0
  258. sqlalchemy/util/_concurrency_py3k.py +288 -0
  259. sqlalchemy/util/_has_cy.py +40 -0
  260. sqlalchemy/util/_py_collections.py +541 -0
  261. sqlalchemy/util/compat.py +421 -0
  262. sqlalchemy/util/concurrency.py +110 -0
  263. sqlalchemy/util/deprecations.py +401 -0
  264. sqlalchemy/util/langhelpers.py +2203 -0
  265. sqlalchemy/util/preloaded.py +150 -0
  266. sqlalchemy/util/queue.py +322 -0
  267. sqlalchemy/util/tool_support.py +201 -0
  268. sqlalchemy/util/topological.py +120 -0
  269. sqlalchemy/util/typing.py +734 -0
  270. sqlalchemy-2.0.47.dist-info/METADATA +243 -0
  271. sqlalchemy-2.0.47.dist-info/RECORD +274 -0
  272. sqlalchemy-2.0.47.dist-info/WHEEL +5 -0
  273. sqlalchemy-2.0.47.dist-info/licenses/LICENSE +19 -0
  274. sqlalchemy-2.0.47.dist-info/top_level.txt +1 -0
@@ -0,0 +1,809 @@
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 _InternalEntityType
36
+ from .interfaces import StrategizedProperty
37
+ from .mapper import Mapper
38
+ from .relationships import RelationshipProperty
39
+ from .util import AliasedInsp
40
+ from ..sql.cache_key import _CacheKeyTraversalType
41
+ from ..sql.elements import BindParameter
42
+ from ..sql.visitors import anon_map
43
+ from ..util.typing import _LiteralStar
44
+ from ..util.typing import TypeGuard
45
+
46
+ def is_root(path: PathRegistry) -> TypeGuard[RootRegistry]: ...
47
+
48
+ def is_entity(path: PathRegistry) -> TypeGuard[AbstractEntityRegistry]: ...
49
+
50
+ else:
51
+ is_root = operator.attrgetter("is_root")
52
+ is_entity = operator.attrgetter("is_entity")
53
+
54
+
55
+ _SerializedPath = List[Any]
56
+ _StrPathToken = str
57
+ _PathElementType = Union[
58
+ _StrPathToken, "_InternalEntityType[Any]", "StrategizedProperty[Any]"
59
+ ]
60
+
61
+ # the representation is in fact
62
+ # a tuple with alternating:
63
+ # [_InternalEntityType[Any], Union[str, StrategizedProperty[Any]],
64
+ # _InternalEntityType[Any], Union[str, StrategizedProperty[Any]], ...]
65
+ # this might someday be a tuple of 2-tuples instead, but paths can be
66
+ # chopped at odd intervals as well so this is less flexible
67
+ _PathRepresentation = Tuple[_PathElementType, ...]
68
+
69
+ # NOTE: these names are weird since the array is 0-indexed,
70
+ # the "_Odd" entries are at 0, 2, 4, etc
71
+ _OddPathRepresentation = Sequence["_InternalEntityType[Any]"]
72
+ _EvenPathRepresentation = Sequence[Union["StrategizedProperty[Any]", str]]
73
+
74
+
75
+ log = logging.getLogger(__name__)
76
+
77
+
78
+ def _unreduce_path(path: _SerializedPath) -> PathRegistry:
79
+ return PathRegistry.deserialize(path)
80
+
81
+
82
+ _WILDCARD_TOKEN: _LiteralStar = "*"
83
+ _DEFAULT_TOKEN = "_sa_default"
84
+
85
+
86
+ class PathRegistry(HasCacheKey):
87
+ """Represent query load paths and registry functions.
88
+
89
+ Basically represents structures like:
90
+
91
+ (<User mapper>, "orders", <Order mapper>, "items", <Item mapper>)
92
+
93
+ These structures are generated by things like
94
+ query options (joinedload(), subqueryload(), etc.) and are
95
+ used to compose keys stored in the query._attributes dictionary
96
+ for various options.
97
+
98
+ They are then re-composed at query compile/result row time as
99
+ the query is formed and as rows are fetched, where they again
100
+ serve to compose keys to look up options in the context.attributes
101
+ dictionary, which is copied from query._attributes.
102
+
103
+ The path structure has a limited amount of caching, where each
104
+ "root" ultimately pulls from a fixed registry associated with
105
+ the first mapper, that also contains elements for each of its
106
+ property keys. However paths longer than two elements, which
107
+ are the exception rather than the rule, are generated on an
108
+ as-needed basis.
109
+
110
+ """
111
+
112
+ __slots__ = ()
113
+
114
+ is_token = False
115
+ is_root = False
116
+ has_entity = False
117
+ is_property = False
118
+ is_entity = False
119
+
120
+ is_unnatural: bool
121
+
122
+ path: _PathRepresentation
123
+ natural_path: _PathRepresentation
124
+ parent: Optional[PathRegistry]
125
+ root: RootRegistry
126
+
127
+ _cache_key_traversal: _CacheKeyTraversalType = [
128
+ ("path", visitors.ExtendedInternalTraversal.dp_has_cache_key_list)
129
+ ]
130
+
131
+ def __eq__(self, other: Any) -> bool:
132
+ try:
133
+ return other is not None and self.path == other._path_for_compare
134
+ except AttributeError:
135
+ util.warn(
136
+ "Comparison of PathRegistry to %r is not supported"
137
+ % (type(other))
138
+ )
139
+ return False
140
+
141
+ def __ne__(self, other: Any) -> bool:
142
+ try:
143
+ return other is None or self.path != other._path_for_compare
144
+ except AttributeError:
145
+ util.warn(
146
+ "Comparison of PathRegistry to %r is not supported"
147
+ % (type(other))
148
+ )
149
+ return True
150
+
151
+ @property
152
+ def _path_for_compare(self) -> Optional[_PathRepresentation]:
153
+ return self.path
154
+
155
+ def odd_element(self, index: int) -> _InternalEntityType[Any]:
156
+ return self.path[index] # type: ignore
157
+
158
+ def set(self, attributes: Dict[Any, Any], key: Any, value: Any) -> None:
159
+ log.debug("set '%s' on path '%s' to '%s'", key, self, value)
160
+ attributes[(key, self.natural_path)] = value
161
+
162
+ def setdefault(
163
+ self, attributes: Dict[Any, Any], key: Any, value: Any
164
+ ) -> None:
165
+ log.debug("setdefault '%s' on path '%s' to '%s'", key, self, value)
166
+ attributes.setdefault((key, self.natural_path), value)
167
+
168
+ def get(
169
+ self, attributes: Dict[Any, Any], key: Any, value: Optional[Any] = None
170
+ ) -> Any:
171
+ key = (key, self.natural_path)
172
+ if key in attributes:
173
+ return attributes[key]
174
+ else:
175
+ return value
176
+
177
+ def __len__(self) -> int:
178
+ return len(self.path)
179
+
180
+ def __hash__(self) -> int:
181
+ return id(self)
182
+
183
+ @overload
184
+ def __getitem__(self, entity: _StrPathToken) -> TokenRegistry: ...
185
+
186
+ @overload
187
+ def __getitem__(self, entity: int) -> _PathElementType: ...
188
+
189
+ @overload
190
+ def __getitem__(self, entity: slice) -> _PathRepresentation: ...
191
+
192
+ @overload
193
+ def __getitem__(
194
+ self, entity: _InternalEntityType[Any]
195
+ ) -> AbstractEntityRegistry: ...
196
+
197
+ @overload
198
+ def __getitem__(
199
+ self, entity: StrategizedProperty[Any]
200
+ ) -> PropRegistry: ...
201
+
202
+ def __getitem__(
203
+ self,
204
+ entity: Union[
205
+ _StrPathToken,
206
+ int,
207
+ slice,
208
+ _InternalEntityType[Any],
209
+ StrategizedProperty[Any],
210
+ ],
211
+ ) -> Union[
212
+ TokenRegistry,
213
+ _PathElementType,
214
+ _PathRepresentation,
215
+ PropRegistry,
216
+ AbstractEntityRegistry,
217
+ ]:
218
+ raise NotImplementedError()
219
+
220
+ # TODO: what are we using this for?
221
+ @property
222
+ def length(self) -> int:
223
+ return len(self.path)
224
+
225
+ def pairs(
226
+ self,
227
+ ) -> Iterator[
228
+ Tuple[_InternalEntityType[Any], Union[str, StrategizedProperty[Any]]]
229
+ ]:
230
+ odd_path = cast(_OddPathRepresentation, self.path)
231
+ even_path = cast(_EvenPathRepresentation, odd_path)
232
+ for i in range(0, len(odd_path), 2):
233
+ yield odd_path[i], even_path[i + 1]
234
+
235
+ def contains_mapper(self, mapper: Mapper[Any]) -> bool:
236
+ _m_path = cast(_OddPathRepresentation, self.path)
237
+ for path_mapper in [_m_path[i] for i in range(0, len(_m_path), 2)]:
238
+ if path_mapper.mapper.isa(mapper):
239
+ return True
240
+ else:
241
+ return False
242
+
243
+ def contains(self, attributes: Dict[Any, Any], key: Any) -> bool:
244
+ return (key, self.path) in attributes
245
+
246
+ def __reduce__(self) -> Any:
247
+ return _unreduce_path, (self.serialize(),)
248
+
249
+ @classmethod
250
+ def _serialize_path(cls, path: _PathRepresentation) -> _SerializedPath:
251
+ _m_path = cast(_OddPathRepresentation, path)
252
+ _p_path = cast(_EvenPathRepresentation, path)
253
+
254
+ return list(
255
+ zip(
256
+ tuple(
257
+ m.class_ if (m.is_mapper or m.is_aliased_class) else str(m)
258
+ for m in [_m_path[i] for i in range(0, len(_m_path), 2)]
259
+ ),
260
+ tuple(
261
+ p.key if insp_is_mapper_property(p) else str(p)
262
+ for p in [_p_path[i] for i in range(1, len(_p_path), 2)]
263
+ )
264
+ + (None,),
265
+ )
266
+ )
267
+
268
+ @classmethod
269
+ def _deserialize_path(cls, path: _SerializedPath) -> _PathRepresentation:
270
+ def _deserialize_mapper_token(mcls: Any) -> Any:
271
+ return (
272
+ # note: we likely dont want configure=True here however
273
+ # this is maintained at the moment for backwards compatibility
274
+ orm_base._inspect_mapped_class(mcls, configure=True)
275
+ if mcls not in PathToken._intern
276
+ else PathToken._intern[mcls]
277
+ )
278
+
279
+ def _deserialize_key_token(mcls: Any, key: Any) -> Any:
280
+ if key is None:
281
+ return None
282
+ elif key in PathToken._intern:
283
+ return PathToken._intern[key]
284
+ else:
285
+ mp = orm_base._inspect_mapped_class(mcls, configure=True)
286
+ assert mp is not None
287
+ return mp.attrs[key]
288
+
289
+ p = tuple(
290
+ chain(
291
+ *[
292
+ (
293
+ _deserialize_mapper_token(mcls),
294
+ _deserialize_key_token(mcls, key),
295
+ )
296
+ for mcls, key in path
297
+ ]
298
+ )
299
+ )
300
+ if p and p[-1] is None:
301
+ p = p[0:-1]
302
+ return p
303
+
304
+ def serialize(self) -> _SerializedPath:
305
+ path = self.path
306
+ return self._serialize_path(path)
307
+
308
+ @classmethod
309
+ def deserialize(cls, path: _SerializedPath) -> PathRegistry:
310
+ assert path is not None
311
+ p = cls._deserialize_path(path)
312
+ return cls.coerce(p)
313
+
314
+ @overload
315
+ @classmethod
316
+ def per_mapper(cls, mapper: Mapper[Any]) -> CachingEntityRegistry: ...
317
+
318
+ @overload
319
+ @classmethod
320
+ def per_mapper(cls, mapper: AliasedInsp[Any]) -> SlotsEntityRegistry: ...
321
+
322
+ @classmethod
323
+ def per_mapper(
324
+ cls, mapper: _InternalEntityType[Any]
325
+ ) -> AbstractEntityRegistry:
326
+ if mapper.is_mapper:
327
+ return CachingEntityRegistry(cls.root, mapper)
328
+ else:
329
+ return SlotsEntityRegistry(cls.root, mapper)
330
+
331
+ @classmethod
332
+ def coerce(cls, raw: _PathRepresentation) -> PathRegistry:
333
+ def _red(prev: PathRegistry, next_: _PathElementType) -> PathRegistry:
334
+ return prev[next_]
335
+
336
+ # can't quite get mypy to appreciate this one :)
337
+ return reduce(_red, raw, cls.root) # type: ignore
338
+
339
+ def __add__(self, other: PathRegistry) -> PathRegistry:
340
+ def _red(prev: PathRegistry, next_: _PathElementType) -> PathRegistry:
341
+ return prev[next_]
342
+
343
+ return reduce(_red, other.path, self)
344
+
345
+ def __str__(self) -> str:
346
+ return f"ORM Path[{' -> '.join(str(elem) for elem in self.path)}]"
347
+
348
+ def __repr__(self) -> str:
349
+ return f"{self.__class__.__name__}({self.path!r})"
350
+
351
+
352
+ class CreatesToken(PathRegistry):
353
+ __slots__ = ()
354
+
355
+ is_aliased_class: bool
356
+ is_root: bool
357
+
358
+ def token(self, token: _StrPathToken) -> TokenRegistry:
359
+ if token.endswith(f":{_WILDCARD_TOKEN}"):
360
+ return TokenRegistry(self, token)
361
+ elif token.endswith(f":{_DEFAULT_TOKEN}"):
362
+ return TokenRegistry(self.root, token)
363
+ else:
364
+ raise exc.ArgumentError(f"invalid token: {token}")
365
+
366
+
367
+ class RootRegistry(CreatesToken):
368
+ """Root registry, defers to mappers so that
369
+ paths are maintained per-root-mapper.
370
+
371
+ """
372
+
373
+ __slots__ = ()
374
+
375
+ inherit_cache = True
376
+
377
+ path = natural_path = ()
378
+ has_entity = False
379
+ is_aliased_class = False
380
+ is_root = True
381
+ is_unnatural = False
382
+
383
+ def _getitem(
384
+ self, entity: Any
385
+ ) -> Union[TokenRegistry, AbstractEntityRegistry]:
386
+ if entity in PathToken._intern:
387
+ if TYPE_CHECKING:
388
+ assert isinstance(entity, _StrPathToken)
389
+ return TokenRegistry(self, PathToken._intern[entity])
390
+ else:
391
+ try:
392
+ return entity._path_registry # type: ignore
393
+ except AttributeError:
394
+ raise IndexError(
395
+ f"invalid argument for RootRegistry.__getitem__: {entity}"
396
+ )
397
+
398
+ def _truncate_recursive(self) -> RootRegistry:
399
+ return self
400
+
401
+ if not TYPE_CHECKING:
402
+ __getitem__ = _getitem
403
+
404
+
405
+ PathRegistry.root = RootRegistry()
406
+
407
+
408
+ class PathToken(orm_base.InspectionAttr, HasCacheKey, str):
409
+ """cacheable string token"""
410
+
411
+ _intern: Dict[str, PathToken] = {}
412
+
413
+ def _gen_cache_key(
414
+ self, anon_map: anon_map, bindparams: List[BindParameter[Any]]
415
+ ) -> Tuple[Any, ...]:
416
+ return (str(self),)
417
+
418
+ @property
419
+ def _path_for_compare(self) -> Optional[_PathRepresentation]:
420
+ return None
421
+
422
+ @classmethod
423
+ def intern(cls, strvalue: str) -> PathToken:
424
+ if strvalue in cls._intern:
425
+ return cls._intern[strvalue]
426
+ else:
427
+ cls._intern[strvalue] = result = PathToken(strvalue)
428
+ return result
429
+
430
+
431
+ class TokenRegistry(PathRegistry):
432
+ __slots__ = ("token", "parent", "path", "natural_path")
433
+
434
+ inherit_cache = True
435
+
436
+ token: _StrPathToken
437
+ parent: CreatesToken
438
+
439
+ def __init__(self, parent: CreatesToken, token: _StrPathToken):
440
+ token = PathToken.intern(token)
441
+
442
+ self.token = token
443
+ self.parent = parent
444
+ self.path = parent.path + (token,)
445
+ self.natural_path = parent.natural_path + (token,)
446
+
447
+ has_entity = False
448
+
449
+ is_token = True
450
+
451
+ def generate_for_superclasses(self) -> Iterator[PathRegistry]:
452
+ # NOTE: this method is no longer used. consider removal
453
+ parent = self.parent
454
+ if is_root(parent):
455
+ yield self
456
+ return
457
+
458
+ if TYPE_CHECKING:
459
+ assert isinstance(parent, AbstractEntityRegistry)
460
+ if not parent.is_aliased_class:
461
+ for mp_ent in parent.mapper.iterate_to_root():
462
+ yield TokenRegistry(parent.parent[mp_ent], self.token)
463
+ elif (
464
+ parent.is_aliased_class
465
+ and cast(
466
+ "AliasedInsp[Any]",
467
+ parent.entity,
468
+ )._is_with_polymorphic
469
+ ):
470
+ yield self
471
+ for ent in cast(
472
+ "AliasedInsp[Any]", parent.entity
473
+ )._with_polymorphic_entities:
474
+ yield TokenRegistry(parent.parent[ent], self.token)
475
+ else:
476
+ yield self
477
+
478
+ def _generate_natural_for_superclasses(
479
+ self,
480
+ ) -> Iterator[_PathRepresentation]:
481
+ parent = self.parent
482
+ if is_root(parent):
483
+ yield self.natural_path
484
+ return
485
+
486
+ if TYPE_CHECKING:
487
+ assert isinstance(parent, AbstractEntityRegistry)
488
+ for mp_ent in parent.mapper.iterate_to_root():
489
+ yield TokenRegistry(parent.parent[mp_ent], self.token).natural_path
490
+ if (
491
+ parent.is_aliased_class
492
+ and cast(
493
+ "AliasedInsp[Any]",
494
+ parent.entity,
495
+ )._is_with_polymorphic
496
+ ):
497
+ yield self.natural_path
498
+ for ent in cast(
499
+ "AliasedInsp[Any]", parent.entity
500
+ )._with_polymorphic_entities:
501
+ yield (
502
+ TokenRegistry(parent.parent[ent], self.token).natural_path
503
+ )
504
+ else:
505
+ yield self.natural_path
506
+
507
+ def _getitem(self, entity: Any) -> Any:
508
+ try:
509
+ return self.path[entity]
510
+ except TypeError as err:
511
+ raise IndexError(f"{entity}") from err
512
+
513
+ if not TYPE_CHECKING:
514
+ __getitem__ = _getitem
515
+
516
+
517
+ class PropRegistry(PathRegistry):
518
+ __slots__ = (
519
+ "prop",
520
+ "parent",
521
+ "path",
522
+ "natural_path",
523
+ "has_entity",
524
+ "entity",
525
+ "mapper",
526
+ "_wildcard_path_loader_key",
527
+ "_default_path_loader_key",
528
+ "_loader_key",
529
+ "is_unnatural",
530
+ )
531
+ inherit_cache = True
532
+ is_property = True
533
+
534
+ prop: StrategizedProperty[Any]
535
+ mapper: Optional[Mapper[Any]]
536
+ entity: Optional[_InternalEntityType[Any]]
537
+
538
+ def __init__(
539
+ self, parent: AbstractEntityRegistry, prop: StrategizedProperty[Any]
540
+ ):
541
+
542
+ # restate this path in terms of the
543
+ # given StrategizedProperty's parent.
544
+ insp = cast("_InternalEntityType[Any]", parent[-1])
545
+ natural_parent: AbstractEntityRegistry = parent
546
+
547
+ # inherit "is_unnatural" from the parent
548
+ self.is_unnatural = parent.parent.is_unnatural or bool(
549
+ parent.mapper.inherits
550
+ )
551
+
552
+ if not insp.is_aliased_class or insp._use_mapper_path: # type: ignore
553
+ parent = natural_parent = parent.parent[prop.parent]
554
+ elif (
555
+ insp.is_aliased_class
556
+ and insp.with_polymorphic_mappers
557
+ and prop.parent in insp.with_polymorphic_mappers
558
+ ):
559
+ subclass_entity: _InternalEntityType[Any] = parent[-1]._entity_for_mapper(prop.parent) # type: ignore # noqa: E501
560
+ parent = parent.parent[subclass_entity]
561
+
562
+ # when building a path where with_polymorphic() is in use,
563
+ # special logic to determine the "natural path" when subclass
564
+ # entities are used.
565
+ #
566
+ # here we are trying to distinguish between a path that starts
567
+ # on a with_polymorphic entity vs. one that starts on a
568
+ # normal entity that introduces a with_polymorphic() in the
569
+ # middle using of_type():
570
+ #
571
+ # # as in test_polymorphic_rel->
572
+ # # test_subqueryload_on_subclass_uses_path_correctly
573
+ # wp = with_polymorphic(RegularEntity, "*")
574
+ # sess.query(wp).options(someload(wp.SomeSubEntity.foos))
575
+ #
576
+ # vs
577
+ #
578
+ # # as in test_relationship->JoinedloadWPolyOfTypeContinued
579
+ # wp = with_polymorphic(SomeFoo, "*")
580
+ # sess.query(RegularEntity).options(
581
+ # someload(RegularEntity.foos.of_type(wp))
582
+ # .someload(wp.SubFoo.bar)
583
+ # )
584
+ #
585
+ # in the former case, the Query as it generates a path that we
586
+ # want to match will be in terms of the with_polymorphic at the
587
+ # beginning. in the latter case, Query will generate simple
588
+ # paths that don't know about this with_polymorphic, so we must
589
+ # use a separate natural path.
590
+ #
591
+ #
592
+ if parent.parent:
593
+ natural_parent = parent.parent[subclass_entity.mapper]
594
+ self.is_unnatural = True
595
+ else:
596
+ natural_parent = parent
597
+ elif (
598
+ natural_parent.parent
599
+ and insp.is_aliased_class
600
+ and prop.parent # this should always be the case here
601
+ is not insp.mapper
602
+ and insp.mapper.isa(prop.parent)
603
+ ):
604
+ natural_parent = parent.parent[prop.parent]
605
+
606
+ self.prop = prop
607
+ self.parent = parent
608
+ self.path = parent.path + (prop,)
609
+ self.natural_path = natural_parent.natural_path + (prop,)
610
+
611
+ self.has_entity = prop._links_to_entity
612
+ if prop._is_relationship:
613
+ if TYPE_CHECKING:
614
+ assert isinstance(prop, RelationshipProperty)
615
+ self.entity = prop.entity
616
+ self.mapper = prop.mapper
617
+ else:
618
+ self.entity = None
619
+ self.mapper = None
620
+
621
+ self._wildcard_path_loader_key = (
622
+ "loader",
623
+ parent.natural_path + self.prop._wildcard_token,
624
+ )
625
+ self._default_path_loader_key = self.prop._default_path_loader_key
626
+ self._loader_key = ("loader", self.natural_path)
627
+
628
+ def _truncate_recursive(self) -> PropRegistry:
629
+ earliest = None
630
+ for i, token in enumerate(reversed(self.path[:-1])):
631
+ if token is self.prop:
632
+ earliest = i
633
+
634
+ if earliest is None:
635
+ return self
636
+ else:
637
+ return self.coerce(self.path[0 : -(earliest + 1)]) # type: ignore
638
+
639
+ @property
640
+ def entity_path(self) -> AbstractEntityRegistry:
641
+ assert self.entity is not None
642
+ return self[self.entity]
643
+
644
+ def _getitem(
645
+ self, entity: Union[int, slice, _InternalEntityType[Any]]
646
+ ) -> Union[AbstractEntityRegistry, _PathElementType, _PathRepresentation]:
647
+ if isinstance(entity, (int, slice)):
648
+ return self.path[entity]
649
+ else:
650
+ return SlotsEntityRegistry(self, entity)
651
+
652
+ if not TYPE_CHECKING:
653
+ __getitem__ = _getitem
654
+
655
+
656
+ class AbstractEntityRegistry(CreatesToken):
657
+ __slots__ = (
658
+ "key",
659
+ "parent",
660
+ "is_aliased_class",
661
+ "path",
662
+ "entity",
663
+ "natural_path",
664
+ )
665
+
666
+ has_entity = True
667
+ is_entity = True
668
+
669
+ parent: Union[RootRegistry, PropRegistry]
670
+ key: _InternalEntityType[Any]
671
+ entity: _InternalEntityType[Any]
672
+ is_aliased_class: bool
673
+
674
+ def __init__(
675
+ self,
676
+ parent: Union[RootRegistry, PropRegistry],
677
+ entity: _InternalEntityType[Any],
678
+ ):
679
+ self.key = entity
680
+ self.parent = parent
681
+ self.is_aliased_class = entity.is_aliased_class
682
+ self.entity = entity
683
+ self.path = parent.path + (entity,)
684
+
685
+ # the "natural path" is the path that we get when Query is traversing
686
+ # from the lead entities into the various relationships; it corresponds
687
+ # to the structure of mappers and relationships. when we are given a
688
+ # path that comes from loader options, as of 1.3 it can have ac-hoc
689
+ # with_polymorphic() and other AliasedInsp objects inside of it, which
690
+ # are usually not present in mappings. So here we track both the
691
+ # "enhanced" path in self.path and the "natural" path that doesn't
692
+ # include those objects so these two traversals can be matched up.
693
+
694
+ # the test here for "(self.is_aliased_class or parent.is_unnatural)"
695
+ # are to avoid the more expensive conditional logic that follows if we
696
+ # know we don't have to do it. This conditional can just as well be
697
+ # "if parent.path:", it just is more function calls.
698
+ #
699
+ # This is basically the only place that the "is_unnatural" flag
700
+ # actually changes behavior.
701
+ if parent.path and (self.is_aliased_class or parent.is_unnatural):
702
+ # this is an infrequent code path used only for loader strategies
703
+ # that also make use of of_type().
704
+ if entity.mapper.isa(parent.natural_path[-1].mapper): # type: ignore # noqa: E501
705
+ self.natural_path = parent.natural_path + (entity.mapper,)
706
+ else:
707
+ self.natural_path = parent.natural_path + (
708
+ parent.natural_path[-1].entity, # type: ignore
709
+ )
710
+ # it seems to make sense that since these paths get mixed up
711
+ # with statements that are cached or not, we should make
712
+ # sure the natural path is cacheable across different occurrences
713
+ # of equivalent AliasedClass objects. however, so far this
714
+ # does not seem to be needed for whatever reason.
715
+ # elif not parent.path and self.is_aliased_class:
716
+ # self.natural_path = (self.entity._generate_cache_key()[0], )
717
+ else:
718
+ self.natural_path = self.path
719
+
720
+ def _truncate_recursive(self) -> AbstractEntityRegistry:
721
+ return self.parent._truncate_recursive()[self.entity]
722
+
723
+ @property
724
+ def root_entity(self) -> _InternalEntityType[Any]:
725
+ return self.odd_element(0)
726
+
727
+ @property
728
+ def entity_path(self) -> PathRegistry:
729
+ return self
730
+
731
+ @property
732
+ def mapper(self) -> Mapper[Any]:
733
+ return self.entity.mapper
734
+
735
+ def __bool__(self) -> bool:
736
+ return True
737
+
738
+ def _getitem(
739
+ self, entity: Any
740
+ ) -> Union[_PathElementType, _PathRepresentation, PathRegistry]:
741
+ if isinstance(entity, (int, slice)):
742
+ return self.path[entity]
743
+ elif entity in PathToken._intern:
744
+ return TokenRegistry(self, PathToken._intern[entity])
745
+ else:
746
+ return PropRegistry(self, entity)
747
+
748
+ if not TYPE_CHECKING:
749
+ __getitem__ = _getitem
750
+
751
+
752
+ class SlotsEntityRegistry(AbstractEntityRegistry):
753
+ # for aliased class, return lightweight, no-cycles created
754
+ # version
755
+ inherit_cache = True
756
+
757
+
758
+ class _ERDict(Dict[Any, Any]):
759
+ def __init__(self, registry: CachingEntityRegistry):
760
+ self.registry = registry
761
+
762
+ def __missing__(self, key: Any) -> PropRegistry:
763
+ self[key] = item = PropRegistry(self.registry, key)
764
+
765
+ return item
766
+
767
+
768
+ class CachingEntityRegistry(AbstractEntityRegistry):
769
+ # for long lived mapper, return dict based caching
770
+ # version that creates reference cycles
771
+
772
+ __slots__ = ("_cache",)
773
+
774
+ inherit_cache = True
775
+
776
+ def __init__(
777
+ self,
778
+ parent: Union[RootRegistry, PropRegistry],
779
+ entity: _InternalEntityType[Any],
780
+ ):
781
+ super().__init__(parent, entity)
782
+ self._cache = _ERDict(self)
783
+
784
+ def pop(self, key: Any, default: Any) -> Any:
785
+ return self._cache.pop(key, default)
786
+
787
+ def _getitem(self, entity: Any) -> Any:
788
+ if isinstance(entity, (int, slice)):
789
+ return self.path[entity]
790
+ elif isinstance(entity, PathToken):
791
+ return TokenRegistry(self, entity)
792
+ else:
793
+ return self._cache[entity]
794
+
795
+ if not TYPE_CHECKING:
796
+ __getitem__ = _getitem
797
+
798
+
799
+ if TYPE_CHECKING:
800
+
801
+ def path_is_entity(
802
+ path: PathRegistry,
803
+ ) -> TypeGuard[AbstractEntityRegistry]: ...
804
+
805
+ def path_is_property(path: PathRegistry) -> TypeGuard[PropRegistry]: ...
806
+
807
+ else:
808
+ path_is_entity = operator.attrgetter("is_entity")
809
+ path_is_property = operator.attrgetter("is_property")