snake-orm 0.1.0b1__py3-none-any.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 (198) hide show
  1. snake_orm-0.1.0b1.dist-info/METADATA +366 -0
  2. snake_orm-0.1.0b1.dist-info/RECORD +198 -0
  3. snake_orm-0.1.0b1.dist-info/WHEEL +4 -0
  4. snake_orm-0.1.0b1.dist-info/entry_points.txt +2 -0
  5. snake_orm-0.1.0b1.dist-info/licenses/LICENSE +21 -0
  6. snakeorm/__init__.py +214 -0
  7. snakeorm/advisor.py +109 -0
  8. snakeorm/cli/__init__.py +3 -0
  9. snakeorm/cli/app.py +992 -0
  10. snakeorm/cli/discovery.py +179 -0
  11. snakeorm/cli/hooks.py +86 -0
  12. snakeorm/compiler/__init__.py +3 -0
  13. snakeorm/compiler/compiler.py +330 -0
  14. snakeorm/connection.py +228 -0
  15. snakeorm/contrib/__init__.py +17 -0
  16. snakeorm/contrib/asgi.py +192 -0
  17. snakeorm/contrib/config.py +264 -0
  18. snakeorm/contrib/deliver.py +256 -0
  19. snakeorm/contrib/django.py +245 -0
  20. snakeorm/contrib/sidecar.py +44 -0
  21. snakeorm/contrib/wsgi.py +176 -0
  22. snakeorm/core/__init__.py +1 -0
  23. snakeorm/core/config.py +173 -0
  24. snakeorm/core/converters.py +172 -0
  25. snakeorm/core/exceptions.py +175 -0
  26. snakeorm/core/placement.py +15 -0
  27. snakeorm/core/sentinels.py +37 -0
  28. snakeorm/core/signals.py +114 -0
  29. snakeorm/debug/__init__.py +42 -0
  30. snakeorm/debug/assets/css/panel.css +616 -0
  31. snakeorm/debug/assets/css/snake_orm_app.css +293 -0
  32. snakeorm/debug/assets/img/icon.svg +5 -0
  33. snakeorm/debug/assets/js/history.js +425 -0
  34. snakeorm/debug/assets/js/language.js +283 -0
  35. snakeorm/debug/assets/js/panel.js +241 -0
  36. snakeorm/debug/assets/js/snake_orm_app.js +82 -0
  37. snakeorm/debug/assets/pages/cli.html +28 -0
  38. snakeorm/debug/assets/pages/config.html +34 -0
  39. snakeorm/debug/assets/pages/database_first.html +29 -0
  40. snakeorm/debug/assets/pages/help.html +21 -0
  41. snakeorm/debug/assets/pages/history.html +27 -0
  42. snakeorm/debug/capture.py +234 -0
  43. snakeorm/debug/channel.py +131 -0
  44. snakeorm/debug/collector.py +144 -0
  45. snakeorm/debug/config.py +149 -0
  46. snakeorm/debug/html.py +578 -0
  47. snakeorm/debug/matrix.py +60 -0
  48. snakeorm/debug/origin.py +42 -0
  49. snakeorm/debug/otel/__init__.py +68 -0
  50. snakeorm/debug/otel/context.py +43 -0
  51. snakeorm/debug/otel/exporter.py +277 -0
  52. snakeorm/debug/otel/payload.py +97 -0
  53. snakeorm/debug/otel/report.py +29 -0
  54. snakeorm/debug/otel/spans.py +393 -0
  55. snakeorm/debug/otel/summary.py +59 -0
  56. snakeorm/debug/record.py +54 -0
  57. snakeorm/debug/report.py +311 -0
  58. snakeorm/debug/testing.py +23 -0
  59. snakeorm/decorators/__init__.py +13 -0
  60. snakeorm/decorators/abstract.py +26 -0
  61. snakeorm/decorators/db_first.py +104 -0
  62. snakeorm/decorators/function.py +28 -0
  63. snakeorm/decorators/model.py +268 -0
  64. snakeorm/decorators/polymorphic.py +140 -0
  65. snakeorm/decorators/result.py +167 -0
  66. snakeorm/decorators/row.py +90 -0
  67. snakeorm/decorators/trigger.py +47 -0
  68. snakeorm/decorators/view.py +149 -0
  69. snakeorm/dialects/__init__.py +6 -0
  70. snakeorm/dialects/base.py +373 -0
  71. snakeorm/dialects/capabilities.py +571 -0
  72. snakeorm/dialects/literals.py +46 -0
  73. snakeorm/dialects/matrix.py +384 -0
  74. snakeorm/dialects/mysql.py +519 -0
  75. snakeorm/dialects/postgres.py +504 -0
  76. snakeorm/dialects/sqlite.py +417 -0
  77. snakeorm/drivers/__init__.py +18 -0
  78. snakeorm/drivers/asyncbase.py +73 -0
  79. snakeorm/drivers/asyncdecorators.py +244 -0
  80. snakeorm/drivers/asyncpool.py +228 -0
  81. snakeorm/drivers/asyncpsycopg.py +124 -0
  82. snakeorm/drivers/asyncpymysql.py +50 -0
  83. snakeorm/drivers/asyncsqlite.py +30 -0
  84. snakeorm/drivers/base.py +76 -0
  85. snakeorm/drivers/failures.py +181 -0
  86. snakeorm/drivers/logging.py +167 -0
  87. snakeorm/drivers/pool.py +352 -0
  88. snakeorm/drivers/psycopg.py +188 -0
  89. snakeorm/drivers/pymysql.py +176 -0
  90. snakeorm/drivers/savepoints.py +46 -0
  91. snakeorm/drivers/sqlite.py +170 -0
  92. snakeorm/drivers/threaded.py +143 -0
  93. snakeorm/drivers/timeout.py +87 -0
  94. snakeorm/dto/__init__.py +55 -0
  95. snakeorm/dto/read.py +231 -0
  96. snakeorm/dto/region.py +326 -0
  97. snakeorm/dto/resolve.py +354 -0
  98. snakeorm/dto/spec.py +201 -0
  99. snakeorm/expressions/__init__.py +88 -0
  100. snakeorm/expressions/conditional.py +178 -0
  101. snakeorm/expressions/expression.py +810 -0
  102. snakeorm/expressions/functions.py +71 -0
  103. snakeorm/expressions/keys.py +299 -0
  104. snakeorm/expressions/paths.py +43 -0
  105. snakeorm/expressions/scalar.py +293 -0
  106. snakeorm/expressions/window.py +248 -0
  107. snakeorm/fields/__init__.py +68 -0
  108. snakeorm/fields/check.py +79 -0
  109. snakeorm/fields/column.py +316 -0
  110. snakeorm/fields/enum.py +64 -0
  111. snakeorm/fields/index.py +46 -0
  112. snakeorm/fields/relationship.py +738 -0
  113. snakeorm/fields/typed.py +736 -0
  114. snakeorm/helpers/__init__.py +1 -0
  115. snakeorm/helpers/annotations.py +21 -0
  116. snakeorm/helpers/inheritance.py +41 -0
  117. snakeorm/helpers/pyliteral.py +52 -0
  118. snakeorm/helpers/pytype.py +50 -0
  119. snakeorm/introspection/__init__.py +13 -0
  120. snakeorm/introspection/base.py +30 -0
  121. snakeorm/introspection/drift.py +78 -0
  122. snakeorm/introspection/models.py +633 -0
  123. snakeorm/introspection/mysql.py +367 -0
  124. snakeorm/introspection/postgres.py +397 -0
  125. snakeorm/introspection/sqlite.py +217 -0
  126. snakeorm/introspection/unsupported.py +94 -0
  127. snakeorm/linker/__init__.py +3 -0
  128. snakeorm/linker/linker.py +521 -0
  129. snakeorm/metadata/__init__.py +41 -0
  130. snakeorm/metadata/check.py +37 -0
  131. snakeorm/metadata/column.py +201 -0
  132. snakeorm/metadata/enum_storage.py +33 -0
  133. snakeorm/metadata/fk_action.py +19 -0
  134. snakeorm/metadata/foreign_key.py +26 -0
  135. snakeorm/metadata/index.py +43 -0
  136. snakeorm/metadata/index_method.py +19 -0
  137. snakeorm/metadata/int_size.py +20 -0
  138. snakeorm/metadata/json_storage.py +21 -0
  139. snakeorm/metadata/polymorphic.py +30 -0
  140. snakeorm/metadata/primary_key.py +23 -0
  141. snakeorm/metadata/relationship.py +60 -0
  142. snakeorm/metadata/relationship_kind.py +43 -0
  143. snakeorm/metadata/routine.py +26 -0
  144. snakeorm/metadata/server_default.py +20 -0
  145. snakeorm/metadata/table.py +97 -0
  146. snakeorm/metadata/table_kind.py +23 -0
  147. snakeorm/metadata/trigger.py +62 -0
  148. snakeorm/metadata/type_params.py +348 -0
  149. snakeorm/migration/__init__.py +88 -0
  150. snakeorm/migration/asyncrunner.py +168 -0
  151. snakeorm/migration/autodetect.py +96 -0
  152. snakeorm/migration/ddl.py +1172 -0
  153. snakeorm/migration/diff.py +701 -0
  154. snakeorm/migration/loader.py +77 -0
  155. snakeorm/migration/operations.py +952 -0
  156. snakeorm/migration/planner.py +78 -0
  157. snakeorm/migration/realize.py +420 -0
  158. snakeorm/migration/renames.py +153 -0
  159. snakeorm/migration/render.py +1095 -0
  160. snakeorm/migration/runner.py +314 -0
  161. snakeorm/migration/squash.py +60 -0
  162. snakeorm/migration/state.py +107 -0
  163. snakeorm/model.py +162 -0
  164. snakeorm/py.typed +0 -0
  165. snakeorm/query/__init__.py +9 -0
  166. snakeorm/query/compound.py +288 -0
  167. snakeorm/query/join_kind.py +19 -0
  168. snakeorm/query/joined.py +143 -0
  169. snakeorm/query/query.py +1001 -0
  170. snakeorm/query/recursive.py +244 -0
  171. snakeorm/registry/__init__.py +5 -0
  172. snakeorm/registry/by_module.py +163 -0
  173. snakeorm/registry/registry.py +166 -0
  174. snakeorm/session/__init__.py +8 -0
  175. snakeorm/session/asyncsession.py +638 -0
  176. snakeorm/session/coercion.py +306 -0
  177. snakeorm/session/factory.py +29 -0
  178. snakeorm/session/guards.py +269 -0
  179. snakeorm/session/isolation.py +23 -0
  180. snakeorm/session/mapper.py +213 -0
  181. snakeorm/session/planning.py +815 -0
  182. snakeorm/session/retry.py +96 -0
  183. snakeorm/session/session.py +871 -0
  184. snakeorm/session/shared.py +355 -0
  185. snakeorm/sql/__init__.py +18 -0
  186. snakeorm/sql/adapt.py +104 -0
  187. snakeorm/sql/aggregate.py +232 -0
  188. snakeorm/sql/condition.py +359 -0
  189. snakeorm/sql/delete.py +46 -0
  190. snakeorm/sql/insert.py +149 -0
  191. snakeorm/sql/joins.py +158 -0
  192. snakeorm/sql/pk_subquery.py +54 -0
  193. snakeorm/sql/refs.py +19 -0
  194. snakeorm/sql/resolver.py +28 -0
  195. snakeorm/sql/select.py +175 -0
  196. snakeorm/sql/update.py +82 -0
  197. snakeorm/sql/value.py +552 -0
  198. snakeorm/times.py +276 -0
@@ -0,0 +1,366 @@
1
+ Metadata-Version: 2.5
2
+ Name: snake-orm
3
+ Version: 0.1.0b1
4
+ Summary: Fully typed deep relationship navigation in Python. No codegen, no type-checker plugin.
5
+ Project-URL: Homepage, https://github.com/velezanthony/snake-orm
6
+ Project-URL: Documentation, https://velezanthony.github.io/snake-orm/
7
+ Project-URL: Source, https://github.com/velezanthony/snake-orm
8
+ Project-URL: Issues, https://github.com/velezanthony/snake-orm/issues
9
+ Project-URL: Changelog, https://github.com/velezanthony/snake-orm/blob/main/CHANGELOG.md
10
+ Author-email: Anthony Velez Tapia <velezanthony2000@gmail.com>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: dataclasses,mysql,orm,postgresql,sql,sqlite,typing
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Python :: Implementation :: CPython
24
+ Classifier: Topic :: Database
25
+ Classifier: Topic :: Database :: Front-Ends
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.11
29
+ Requires-Dist: psycopg2-binary>=2.9.9
30
+ Requires-Dist: python-dotenv>=1.2.2
31
+ Provides-Extra: async
32
+ Requires-Dist: psycopg[binary]>=3.2; extra == 'async'
33
+ Provides-Extra: mysql
34
+ Requires-Dist: pymysql>=1.1; extra == 'mysql'
35
+ Provides-Extra: otel
36
+ Requires-Dist: opentelemetry-api>=1.27; extra == 'otel'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # 🐍 SnakeORM
40
+
41
+ [![CI](https://github.com/velezanthony/snake-orm/actions/workflows/ci.yml/badge.svg)](https://github.com/velezanthony/snake-orm/actions/workflows/ci.yml)
42
+ ![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)
43
+ ![Engines](https://img.shields.io/badge/engines-PostgreSQL%20%7C%20MySQL%20%7C%20SQLite-336791)
44
+ ![Typing](https://img.shields.io/badge/typing-mypy%20%2B%20pyright-2ea44f)
45
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
46
+
47
+ **Fully typed deep relationship navigation in Python. No codegen. No type-checker plugin.**
48
+
49
+ Docs (English and Spanish): <https://velezanthony.github.io/snake-orm/>
50
+
51
+ ```python
52
+ Truck.maker.nation.name == "España" # SnakeExpr[str] -> SnakeCondition
53
+ ```
54
+
55
+ ```sql
56
+ SELECT t0."id", t0."model", t0."maker_id" FROM "public"."trucks" AS t0
57
+ JOIN "public"."makers" AS t1 ON t0."maker_id" = t1."id"
58
+ JOIN "public"."nations" AS t2 ON t1."nation_id" = t2."id"
59
+ WHERE t2."name" = %s
60
+ ```
61
+
62
+ Mypy checks it. Pyright checks it. Pylance autocompletes it. The Django equivalent,
63
+ `filter(maker__nation__name="España")`, is a string: no autocomplete, no check, and renaming
64
+ `nation` fails in production.
65
+
66
+ ---
67
+
68
+ ## Install
69
+
70
+ Requires Python 3.11+. SQLite ships with the standard library, so nothing needs to be running to
71
+ start.
72
+
73
+ ```bash
74
+ pip install snake-orm==0.1.0b1 # or: pip install --pre snake-orm
75
+ ```
76
+
77
+ The distribution is `snake-orm` and the package is `snakeorm`: `import snakeorm`.
78
+
79
+ The version is a **beta**, and the pin is the point: a preliminary is not picked up by a plain
80
+ `pip install snake-orm`, so nobody upgrades into it by accident while the API is still moving.
81
+
82
+ From a checkout, to work on the ORM itself:
83
+
84
+ ```bash
85
+ uv sync --all-extras --all-groups
86
+ uv run pytest # suite
87
+ uv run mypy . # must pass
88
+ uv run ruff check . # must pass
89
+ ```
90
+
91
+ [Installation](docs/users/getting-started/installation.md) →
92
+ [first model](docs/users/getting-started/first-model.md) →
93
+ [migrations](docs/users/getting-started/migrations.md).
94
+
95
+ ```python
96
+ @snake_model(table="makers")
97
+ class Maker(SnakeModel):
98
+ id: SnakeColumn[int] = snake_auto()
99
+ name: SnakeColumn[str] = snake_str(unique=True)
100
+ nation_id: SnakeColumn[int] = snake_int()
101
+ nation: SnakeToOne[Nation] = snake_to_one(nation_id)
102
+ trucks: SnakeToMany[Truck] = snake_to_many("maker")
103
+
104
+ snake_link() # once, after importing every model
105
+ ```
106
+
107
+ The type always comes from the annotation. `snake_column()` only adds SQL information.
108
+
109
+ ```bash
110
+ uv run snakeorm makemigrations --models myapp.models --name initial
111
+ uv run snakeorm migrate --models myapp.models --dsn "host=... dbname=..."
112
+ uv run snakeorm rollback --models myapp.models --dsn "..."
113
+ ```
114
+
115
+ ---
116
+
117
+ ## The mechanism
118
+
119
+ **Recursive descriptors** whose `__get__` returns a different type depending on the access:
120
+
121
+ ```python
122
+ user.car.name # instance -> the value -> str
123
+ User.car.brand.name == "x" # class -> a SQL expression -> SnakeCondition
124
+ ```
125
+
126
+ `User.car` returns `type[Car]`, so `.brand.name` re-triggers the class overload of `Car`'s
127
+ descriptors. `@dataclass_transform` on the decorator types `__init__`. It is the manual equivalent
128
+ of TypeScript's mapped types.
129
+
130
+ The type system is the single source of truth: the class is compiled **once** into an immutable
131
+ metadata graph, and the runtime never reflects on it again.
132
+
133
+ ---
134
+
135
+ ## Why Django returns `Any`
136
+
137
+ ```python
138
+ User.objects.annotate(num_posts=Count("posts"))
139
+ user.num_posts # -> Any
140
+ ```
141
+
142
+ `annotate` returns "a `User` **plus** a `num_posts: int`". That is an intersection type. TypeScript
143
+ has it; Python does not. So there are three paths, and only three:
144
+
145
+ | Path | Dynamic names | Real typing | IntelliSense |
146
+ |---|---|---|---|
147
+ | `__getattr__ -> Any` | ✅ | ❌ | ❌ |
148
+ | Declared names | ❌ | ✅ | ✅ |
149
+ | Type-checker plugin | ✅ | ✅ | partial |
150
+
151
+ Django took the first and patched it with the third (`django-stubs`). SnakeORM forbids the third by
152
+ thesis and takes the second, with a typed escape hatch.
153
+
154
+ `Any` is not typing, it is switching the checker off:
155
+
156
+ ```python
157
+ def __getattr__(self, name: str) -> Any: ...
158
+ u.agg.count_children * 2 # mypy: 0 errors
159
+ other: str = u.agg.count_children # mypy: 0 errors <- same value, as a str
160
+ ```
161
+
162
+ `object` keeps the dynamic name and wakes the checker up:
163
+
164
+ ```python
165
+ def __getattr__(self, name: str) -> object: ...
166
+ u.agg.count_children * 2 # error: unsupported operand types for *
167
+ count: int = cast("int", u.agg.count_children) # explicit, and signed with your name
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Illegal states you cannot write
173
+
174
+ ```python
175
+ SnakeQuery(Nation).filter(Nation.makers.name == "SEAT")
176
+ # error: "SnakeCollection[Maker]" has no attribute "name" [attr-defined]
177
+ ```
178
+
179
+ In Django that compiles, runs and silently duplicates rows — which is what `.distinct()` is for. A
180
+ to-many exposes **collection operations**, not the child's columns:
181
+
182
+ ```python
183
+ q.filter(Nation.makers.any(Maker.name == "SEAT")) # any? -> correlated EXISTS
184
+ q.filter(~Nation.makers.any()) # none? -> NOT EXISTS
185
+ q.filter(Nation.makers.count() > 3) # how many? -> scalar subquery
186
+ q.include(Nation.makers) # load them -> select-in, 2 queries
187
+
188
+ q.include(SnakePrefetch(Nation.makers).then(Maker.trucks))
189
+ # one query per LEVEL (root + makers + trucks = 3), never one per parent
190
+ ```
191
+
192
+ One row per parent. `DISTINCT` is never needed.
193
+
194
+ ```sql
195
+ -- Nation.makers.any(Maker.trucks.any(Truck.model == "Ibiza"))
196
+ SELECT "id", "name" FROM "public"."nations" WHERE EXISTS (
197
+ SELECT 1 FROM "public"."makers" AS e0 WHERE e0."nation_id" = "nations"."id" AND EXISTS (
198
+ SELECT 1 FROM "public"."trucks" AS e1 WHERE e1."maker_id" = e0."id" AND e1."model" = %s))
199
+ ```
200
+
201
+ > **Implicit when the answer is unique. Explicit when there is more than one correct answer.**
202
+
203
+ A to-one never changes the row count, so the `JOIN` is inferred. A to-many does, so the ORM does not
204
+ guess.
205
+
206
+ ---
207
+
208
+ ## What the thesis gives for free
209
+
210
+ **No silent N+1.** An unloaded relation raises instead of querying:
211
+
212
+ ```python
213
+ truck.maker
214
+ # SnakeRelationshipNotLoaded: Relation 'maker' was not loaded.
215
+ # Use .include(Truck.maker) in the query.
216
+ ```
217
+
218
+ **No `F()`.** Class access already is an expression:
219
+
220
+ ```python
221
+ session.update_where(query, [(Counter.views, Counter.views + 1)])
222
+ # UPDATE "counters" SET "views" = ("views" + %s) WHERE ...
223
+ ```
224
+
225
+ Pairs, not a dict: `SnakeExpr` is unhashable because its `__eq__` returns a `SnakeCondition`.
226
+
227
+ **Many-to-many crosses a real model**, never an implicit table:
228
+
229
+ ```python
230
+ tags: SnakeToMany["Tag"] = snake_to_many_through(through="PostTag", via="post", to="tag")
231
+ ```
232
+
233
+ The bridge is an ordinary model, so extra columns on it are ordinary fields from day one.
234
+
235
+ **A bulk `UPDATE`/`DELETE` uses the filter and nothing else.** With no `WHERE` it is refused, and
236
+ so is any other knob you set on the same query — `limit()`, `order_by()`, `only()`. Dropping what
237
+ you asked for would answer a different question without saying so: select the rows first, then
238
+ write by primary key.
239
+
240
+ **`annotate()` counts with a correlated subquery, not a `LEFT JOIN`** — a parent with no children
241
+ comes out `0` instead of disappearing.
242
+
243
+ **A view is a read-only model, and navigable.** `@snake_view` maps a `VIEW` with typed columns,
244
+ navigable both ways; `session.add/update/delete` reject it in the **type**, because writing requires
245
+ a `SnakeModel`. `CreateView`/`AlterView`/`DropView` live in the migrations.
246
+
247
+ **Migration history is `.py`**, because `python_type` is a Python `type`. JSON would need a
248
+ name↔type registry: a second type system, parallel to Python's and worse.
249
+
250
+ ---
251
+
252
+ ## Typed annotations
253
+
254
+ Declare the result class — you choose the name, Python chooses the type:
255
+
256
+ ```python
257
+ @snake_result
258
+ class RealmStats(SnakeResult[Realm]):
259
+ realm: Realm
260
+ forge_count: int
261
+
262
+ rows = session.annotate(query, RealmStats, forge_count=Realm.forges.count())
263
+ rows[0].forge_count # int, with IntelliSense
264
+ rows[0].realm.name # str, navigation intact
265
+ ```
266
+
267
+ For genuinely dynamic names, the escape hatch is explicit:
268
+
269
+ ```python
270
+ count = cast("int", realm.aggregate.forge_count) # object -> the cast is mandatory
271
+ ```
272
+
273
+ Without the cast it does not compile. Without annotating, it raises `SnakeAggregateNotLoaded` naming
274
+ the aggregates it does have.
275
+
276
+ ---
277
+
278
+ ## What is inside
279
+
280
+ | | |
281
+ |---|---|
282
+ | **Queries** | filter · order/limit/offset · group by/having · aggregates · `annotate` · explicit joins · `include` (to-one and to-many) · deep navigation · `.any()` · correlated subqueries · composite `IN` · `only`/`defer` · `iterate` (server cursor) · `for_update` · `raw` |
283
+ | **SQL** | window functions with frame · `UNION`/`INTERSECT`/`EXCEPT` · `WITH RECURSIVE` · `CASE`/`COALESCE`/`NULLIF` · text, date and math functions · `json_get` · `ILIKE` |
284
+ | **Writes** | insert/update/delete · upsert · bulk · `RETURNING` · savepoints · isolation levels · retry on transient conflict · `refresh` |
285
+ | **Schema** | composite PK and FK · polymorphic inheritance · views · triggers · indexes (partial, functional, `GIN`/`GIST`/`BRIN`) · checks · comments · enums · custom converters |
286
+ | **Engines** | PostgreSQL · MySQL/MariaDB · SQLite, all first class · `Cap` catalogue (`Full`/`Degraded`/`Nope`) · sync and async drivers · pool with `pre_ping`/`recycle`/timeout · statement timeout · `EXPLAIN` |
287
+ | **Migrations** | autodetected diff · atomic runner · `RebuildTable` for SQLite · `RunPython` with reverse · squash · cross-app dependencies · drift detection against the live database |
288
+ | **Tooling** | introspection and scaffold for the three engines · debug panel (`ssr`, `envelope`, `timing`, `sidecar`, `otel`) · index advisor · WSGI/ASGI/Django contrib · CLI |
289
+
290
+ Row by row, with links to the code, the test and the page: [feature index](docs/features.md).
291
+
292
+ ---
293
+
294
+ ## Architecture
295
+
296
+ ```
297
+ Python class → Model Compiler → immutable metadata graph
298
+
299
+ SQL · migrations · query · session · CLI
300
+ ```
301
+
302
+ ```
303
+ decorators/ metadata/ compiler/ registry/ linker/
304
+ query/ expressions/ sql/ dialects/ drivers/ session/ migration/ cli/
305
+ ```
306
+
307
+ Two axes that never mix: the **dialect** decides how SQL is *written* (placeholders, quoting,
308
+ `RETURNING`, `ON CONFLICT`); the **driver** decides how it is *executed*. Models and graph are
309
+ engine-agnostic — anything Postgres-specific reaching the model is a bug.
310
+
311
+ SQL is always parameterised: emission returns `(sql, params)` and values never enter the string.
312
+ That kills injection, and it is what makes multi-engine possible.
313
+
314
+ Async reuses the whole core: SQL generation does not execute, so it has no colour.
315
+
316
+ Details in [architecture](docs/contributors/architecture.md); how to work here in
317
+ [CONTRIBUTING](CONTRIBUTING.md).
318
+
319
+ ---
320
+
321
+ ## The type contract is a test
322
+
323
+ In `test/typing/`:
324
+
325
+ - `cases_positive.py` — what **must** type, with `assert_type`.
326
+ - `cases_negative.py` — what **must not compile**, each line carrying its `# EXPECT: <code>`.
327
+ - The runner requires mypy to report exactly those errors on exactly those lines, and pyright to
328
+ reject the same ones.
329
+
330
+ Break `Truck.maker.nation.name` and the suite fails.
331
+
332
+ ---
333
+
334
+ ## Deliberately not built
335
+
336
+ - **Identity map and unit of work.** Two queries to the same row return two objects. Writes are
337
+ explicit; nothing is flushed behind your back.
338
+ - **Lazy loading.** Touching an unloaded relation raises. This is what makes N+1 impossible by
339
+ default.
340
+ - **Joined-table inheritance.** Single table with a discriminator covers the polymorphism, and its
341
+ price is one rule: a child's own columns must allow `NULL`.
342
+ - **Model default ordering.** A hidden `ORDER BY` you did not write.
343
+
344
+ ## Known limits
345
+
346
+ - `storage=NATIVE` in `snake_enum` (Postgres `CREATE TYPE`) is not built: `ALTER TYPE ... ADD VALUE`
347
+ has no inverse, so its `down_sql` would be a lie. The default `CHECK` is reversible.
348
+ - CHECKs are declared outside the class body (`snake_checks(User, ...)`). Inside it, `__set_name__`
349
+ has not run and the column does not know its own name.
350
+ - An expression does not carry its owning model in the type: `Maker.id` and `Truck.id` are twins to
351
+ the checker. Encoding the owner would break deep navigation and condition composition; where it
352
+ matters (aggregates, `.any()`) it is validated at runtime.
353
+ - No lazy loading, full-text search, JSON containment operators or array operators with a typed API.
354
+
355
+ The complete, current list is [known limits](docs/users/reference/limits.md) — part of the contract,
356
+ not a list of apologies.
357
+
358
+ ---
359
+
360
+ ## Status
361
+
362
+ Not published on PyPI. The distribution is named `snake-orm`, the import name is
363
+ `snakeorm`; both are explained in [release](docs/contributors/release.md).
364
+
365
+ Everything above is implemented and tested against real PostgreSQL, MySQL/MariaDB and SQLite. It has
366
+ not run in production yet, and that is the one thing a repository cannot give itself.
@@ -0,0 +1,198 @@
1
+ snakeorm/__init__.py,sha256=VCNI0yKRf0deHAoUKQqYsNG3h0_c9dGuDsiq5ivMoGo,12263
2
+ snakeorm/advisor.py,sha256=vOJH4tkqt7tkTPKnkDDy_JJmX1SqiupOYqbMbJbaO2k,4879
3
+ snakeorm/connection.py,sha256=jQeT7oUPGVagwZewX9AKeWtkSI1yKlHRbeCzSVV_5Y4,11396
4
+ snakeorm/model.py,sha256=cas8xzBxs1TukqlKbU3d-_hEd80oLkp2nhmOHdRBk1U,6166
5
+ snakeorm/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ snakeorm/times.py,sha256=JhQmAhX4QwUIwWUS6juxnIeeZM32okSMgPOR4nng_2Y,12421
7
+ snakeorm/cli/__init__.py,sha256=qHOfkNqMYdYhLamwFgr-gvvr-HvR6gdcIfI1LZiE87E,200
8
+ snakeorm/cli/app.py,sha256=WYy-8u4LThLakUpaFXtpZYrCpWTa8yDaOodESu53HiU,42046
9
+ snakeorm/cli/discovery.py,sha256=67lRKaBRe8MNF11_o2aRXELlu8TnOYxA84g48a95jvE,7796
10
+ snakeorm/cli/hooks.py,sha256=WaYZfxHAKYoafRtUBm3cZTrBnssf-4SWon-dr8gGP-Q,3847
11
+ snakeorm/compiler/__init__.py,sha256=nw5qKO4xdSFNwNse6lgnCKoOIKUGnonq-3-97Ytfuxc,140
12
+ snakeorm/compiler/compiler.py,sha256=T-MqoF6uOjS9JSzbMuhYJu54VW-302xkNRS-yfRAua4,15238
13
+ snakeorm/contrib/__init__.py,sha256=aCuxfOM9wkRVZC8VKGYvw8sasm_z-XLjeD1tl_5mgf4,986
14
+ snakeorm/contrib/asgi.py,sha256=S5Awsdd5C7wjkk3gJOgfNUGk5dnIZxsi1q0CBbopJwQ,7321
15
+ snakeorm/contrib/config.py,sha256=5lG_7Ld8uheH8nBwad2WDbkMtTz9IuEPytco9qMXS9U,11962
16
+ snakeorm/contrib/deliver.py,sha256=0wivvwUkqBeJcXtCCTm8mn_PhLgLmbWEmuJYzMu9DR0,10998
17
+ snakeorm/contrib/django.py,sha256=YX52bLi_Pf2GK637e8p018S0Nv_6TWOrz4IWjHIttOg,10262
18
+ snakeorm/contrib/sidecar.py,sha256=W4TgjDrNBrhwreJOhVV8Ag-miQL269iKmCs1old7wWU,1589
19
+ snakeorm/contrib/wsgi.py,sha256=Rq6qz5dR9bZ2tucza6dT-3i3MgFF0db43StBx2lwb5s,6828
20
+ snakeorm/core/__init__.py,sha256=lbXm3QM16-tfI8sifIrpf5LqiCDV6p8NBX5jnznUQSY,92
21
+ snakeorm/core/config.py,sha256=70E-avkF_jbjDBslpGV9WXuTl-9hW1ajxNWNw3rY03Q,7416
22
+ snakeorm/core/converters.py,sha256=7CxxfB-QwwiOHYC5lZJtB37cWQIOYgMWSLC6YNkH_aM,8409
23
+ snakeorm/core/exceptions.py,sha256=RAanQWdrIn5-x_usmUcR6x8MGRvAGnfhggFITucqtGI,7308
24
+ snakeorm/core/placement.py,sha256=iMddxsgiRk8yQXRZ4PsdO-N0GjUFByOKlZf2URQnUnI,637
25
+ snakeorm/core/sentinels.py,sha256=dNdylIDWEKH5UnPxWRaCy34N2CET3vondYR7Wt1b2VE,1427
26
+ snakeorm/core/signals.py,sha256=imfSa8jXds6afmL7NR6nxjdOK6Zu2RCEgIv3KxAIh80,4164
27
+ snakeorm/debug/__init__.py,sha256=Psh3Al0cjY--saRBsl4xhHmAUtjILJpugrVkvNWed8o,2716
28
+ snakeorm/debug/capture.py,sha256=PAc3ZCjIichv3pjLycMfKkDa8K41ihOansX3IM_fxew,8720
29
+ snakeorm/debug/channel.py,sha256=q2P0IFj_ziLOzi0zW4WcAXO3oZU7Eq23Fj8RzmUX4cY,6151
30
+ snakeorm/debug/collector.py,sha256=SCyUGd3n9M-NU7l0OYRlB_HtghpAi3w-ogKaHeK9RCo,5608
31
+ snakeorm/debug/config.py,sha256=LBkUfOBTaa6S2KI99PxSWdFLTt2WVcpLz42yeLQOOpc,6853
32
+ snakeorm/debug/html.py,sha256=S-YyE9sJChUvnDN8ff3vozjAyBxv6yrx1vp_oKJVc3c,23941
33
+ snakeorm/debug/matrix.py,sha256=gmhBT5LlAVos_Zb3qMZZ7N3KQG1chz2mCMo_o1HCCcY,2294
34
+ snakeorm/debug/origin.py,sha256=3JpPNl0InMHKEnMUBEM3edl_Sk1rJuUyL9D-YuVE3Wg,1870
35
+ snakeorm/debug/record.py,sha256=FDmxwN8MdStwshCWYpOXXzt57LhyG51hc80E2AnZ-ts,2263
36
+ snakeorm/debug/report.py,sha256=_ZbIxIxlRvQnY4-yu1SpYXRRCId1Ogta7URW8NMZqL0,12990
37
+ snakeorm/debug/testing.py,sha256=mnM2DqPfcK7srriPDk12Agj5DgjHSPvo735CiTv6HIQ,809
38
+ snakeorm/debug/assets/css/panel.css,sha256=9qR1U9kBrUxqtyP-vYpCPRtF9js2XSIXdbS8B23FnXc,11719
39
+ snakeorm/debug/assets/css/snake_orm_app.css,sha256=SHNdOhb0Xgn4f2O3Pw-AZInj5qhDYH9L1VZyfbu1M3I,5594
40
+ snakeorm/debug/assets/img/icon.svg,sha256=Z91qpA0khEkUKDCkgNzPjMU9Fl507oAkE9AU2WF947w,368
41
+ snakeorm/debug/assets/js/history.js,sha256=7stu6cXXKeLDA_u3r_Jn1XYy5m0_DCLiEbbLJiMpYwY,17549
42
+ snakeorm/debug/assets/js/language.js,sha256=FkzSb1jkUWra0e0e8ccH0gELQGyL4jLiMuGO5dfGHrU,17773
43
+ snakeorm/debug/assets/js/panel.js,sha256=Dwh3raH5TpJHXsMrXHLy2yMyXfWrfWbVu4HRvdAorCk,9436
44
+ snakeorm/debug/assets/js/snake_orm_app.js,sha256=0X_o2rmrK0mKrgncPSpl0o9wYM4IX9L9P3LYQFgTRmc,3430
45
+ snakeorm/debug/assets/pages/cli.html,sha256=PI2S_J5zb0u-ZxyScc49J7UvoHk8OKtgoyYAg3CKXDM,1809
46
+ snakeorm/debug/assets/pages/config.html,sha256=mPQgpnz9UbHiuj8WxU4bygE1qDbzpFougy1jYqjXfbs,2905
47
+ snakeorm/debug/assets/pages/database_first.html,sha256=5kBwsqxEL1bhlYbDHRjTDoAviqKRecc2UPr6oGFlFZ8,2334
48
+ snakeorm/debug/assets/pages/help.html,sha256=0GJgfTk0Zdcl1W2YkaZa6ollYwkiiZeWm51Fh2UVp_o,1193
49
+ snakeorm/debug/assets/pages/history.html,sha256=azGLQd4kf3h3kAmaw7KG7e5akVLKkFKjYpD-gISn_Bk,3088
50
+ snakeorm/debug/otel/__init__.py,sha256=r5VCFowlqMWn1uPP7Lhg7GOhhSa8Kne6iZADRRBzXhM,4263
51
+ snakeorm/debug/otel/context.py,sha256=3yWFEUNvaP8txliQaVxgpNLWLYIwdahQ4Vkb354A620,1576
52
+ snakeorm/debug/otel/exporter.py,sha256=2r0oAMy7vFCf1cz12BXhgRlBRr-_p5NopcW7GWL1Qk8,10599
53
+ snakeorm/debug/otel/payload.py,sha256=El0Z-S9Nn2MT34ZL8kvB2u-5M5mfZtn3Rq9177s6CKg,3558
54
+ snakeorm/debug/otel/report.py,sha256=ZnH4vggM4lZm5diYdQiEjFFTlXiskoJMRXVoktan1Bk,1301
55
+ snakeorm/debug/otel/spans.py,sha256=WRl40__Rt3cO7ScRAtHZyK6VRmQGeUOAN7FvmxAGDv0,15230
56
+ snakeorm/debug/otel/summary.py,sha256=MQ_I4o6KXju1yq9GqFYW4MkB1XbBD6UoUze4EhO48m4,2473
57
+ snakeorm/decorators/__init__.py,sha256=5btwYRaemuxOllc23Y32en69Tbq5XCpojzlBB7SfbKU,770
58
+ snakeorm/decorators/abstract.py,sha256=q0w7r_mSZ_NfuuQtq9jFSidxXQNx4uUr6aKdDBnEWlA,968
59
+ snakeorm/decorators/db_first.py,sha256=NjIpYI35wgV2NyqH2UrLJ7YSSN_5QDNvAOkSMTamNA8,3736
60
+ snakeorm/decorators/function.py,sha256=LgjnMpxIeFvhp_RKwSQMsahcCaVJc9dNDfKgll-O9Uk,1224
61
+ snakeorm/decorators/model.py,sha256=hJk4YdfSHHQPK23L7_fX3VFRbEGk4iZwXlxuorNAdgo,10806
62
+ snakeorm/decorators/polymorphic.py,sha256=l6Nmsf0dxW2dFZTnL5cNj_udFsXDtWDcZ6JuOnrznds,5874
63
+ snakeorm/decorators/result.py,sha256=3UKU0mn5RpMJdmCjVnUjzCHRB_x3XnKOb15pENIRgGU,6926
64
+ snakeorm/decorators/row.py,sha256=iSgwDYGNVh9o0RW2flOa2Wi6OM_hYn9JgtbaGhXYFnE,3158
65
+ snakeorm/decorators/trigger.py,sha256=RMfDJcljVZdZWZ8xLc2OnsoO2-UsN_e0G95oeqoIGFA,1658
66
+ snakeorm/decorators/view.py,sha256=xX7M1DwNuZNL3ClNjV5_Xdbvqcf__5E1QyZ71MXw7MM,5579
67
+ snakeorm/dialects/__init__.py,sha256=AzkKHZjLFB04rrOhoi5rhz24_BnIM7WlWMDB2_7Mixg,343
68
+ snakeorm/dialects/base.py,sha256=YLpulEJo31mRXFLBgjkSFL069aUx8ZvRASchesBjH_Y,17603
69
+ snakeorm/dialects/capabilities.py,sha256=Vh426O-8cjJOJ95OewGwjTXprZ7n_Z0kWStLM9ve9Ks,27715
70
+ snakeorm/dialects/literals.py,sha256=WzYNiWXajmzxBAdYLRA-fNFHn3BJEhx0J4FW3oYhfBA,2349
71
+ snakeorm/dialects/matrix.py,sha256=OSZMpnF9bP5JETNp34scaPMtlLA1gu0wJUKyykhAVBI,18429
72
+ snakeorm/dialects/mysql.py,sha256=gRbn7t8uye3QCv1O6FTZNdhPkXiG3lod-1f4zeETjRM,25782
73
+ snakeorm/dialects/postgres.py,sha256=EZ3IXPS8aHEhNovNH3XXjK8f3lQBy9DAFEXayAnYxAk,23384
74
+ snakeorm/dialects/sqlite.py,sha256=dRkZx6RNbf49wU6nTiYbmpqgQ9t7J8Xv2bfgSg_Y1D4,19777
75
+ snakeorm/drivers/__init__.py,sha256=5Z3MuxwrgJWbcd4hNnYHclmT5vA44glTPcCcup6nmeM,1221
76
+ snakeorm/drivers/asyncbase.py,sha256=yHLM2QZpeJrLeiYBBmi0f57OXR7-E7gyNgpt3KJ1gBw,2786
77
+ snakeorm/drivers/asyncdecorators.py,sha256=cuQ5OdEUlpLEw5r717v7cDKufH3epEBoF3ywrlPLkCo,10094
78
+ snakeorm/drivers/asyncpool.py,sha256=Z-ASwurX1PCRaf0B-D0OOT9FS1ziSqrw1MAcpVwU0U4,9976
79
+ snakeorm/drivers/asyncpsycopg.py,sha256=K0FrxStyPO9k7Z3Pa2HdlaYjYsThlC62YlLsrNKqT9o,5042
80
+ snakeorm/drivers/asyncpymysql.py,sha256=dc879K0ZIqQzuLf6A-XY4ZkkOmIYN65LUBK54csXSJ0,2408
81
+ snakeorm/drivers/asyncsqlite.py,sha256=oVWqUaJgQyy2Xu_URBiOVDpDmTm-9jlsuj8DjQ4p7zU,1426
82
+ snakeorm/drivers/base.py,sha256=zRO9Q9yNp-PkbYbIyZCEYpdqCIWUq2DesOZ5uolWQU4,3033
83
+ snakeorm/drivers/failures.py,sha256=JWLBdsEfY82l40DD6vAaiwCEU38y-TQSSyGW96ftAl4,8156
84
+ snakeorm/drivers/logging.py,sha256=MUpL_7LVjhAudswAwNop6cM_DwCMyMik9bRrLv6AyUc,7217
85
+ snakeorm/drivers/pool.py,sha256=6gtfssbDJX13_dTf5lvq3Mgk2EH-x2chMxWtK3wGNAI,16643
86
+ snakeorm/drivers/psycopg.py,sha256=BgCfBIg10KdS5aqKJ3tvd7hpLsbF2uM80JMsX-LHG4s,7705
87
+ snakeorm/drivers/pymysql.py,sha256=zdulMiJE-OjGzC_FLxDEN-ZhGuAtqt3vpG0W0daw0Jw,7292
88
+ snakeorm/drivers/savepoints.py,sha256=Q0flSUCALbm0TwfEt5rc7yDsfIKbvy41nGJkNYAHrc0,2353
89
+ snakeorm/drivers/sqlite.py,sha256=NEVH-GYQlCrwqhdyY2vC7xaCax-HG8FHivYvrgvT0bk,7730
90
+ snakeorm/drivers/threaded.py,sha256=bieXQWZWlOPC7MIsA61Reczn0fz283zZOSxmU-lhH9c,6877
91
+ snakeorm/drivers/timeout.py,sha256=_6f5Bd2ktrWcEdhcjf98_G5alZw7UZlof-kmsIVvzYg,3808
92
+ snakeorm/dto/__init__.py,sha256=kazOsbp0VR8rxNaP1ezQSXj22yh3rK67SPIQpwXM5l8,2126
93
+ snakeorm/dto/read.py,sha256=ZdPo92JpvYY24-GgY32YqmJrLSH-x8ZFYmS-awbpmlE,10174
94
+ snakeorm/dto/region.py,sha256=Sdo9A6iew3uyodOqQ5CXcMV5BviQozqTWV7o4Y1P3yw,13903
95
+ snakeorm/dto/resolve.py,sha256=SUwBKGRS8oiIIGtUCPrnCHdslT3d-fWPn8XUS1s8ehs,15615
96
+ snakeorm/dto/spec.py,sha256=EcSVNlowe5hJfDahfYyL3bGA5kjgv0z3hFx-dtHH6gw,8982
97
+ snakeorm/expressions/__init__.py,sha256=iDwRIs8ZnfMVvz08k8WnbQNfJdkQBNXZrvUdNW7IRGo,5854
98
+ snakeorm/expressions/conditional.py,sha256=rhVeFJ17yKygFDB_nirOgB8_tPEs2tZvNNjZjo2kICA,7267
99
+ snakeorm/expressions/expression.py,sha256=_QsOcEB7-1Vuv8Y0mmEb8HzIZBielPbiHDkup8ridY8,33781
100
+ snakeorm/expressions/functions.py,sha256=9WzTksjfCntr5F0bgxiHm5loVPvhi7KZw9BfntpEm84,2843
101
+ snakeorm/expressions/keys.py,sha256=DXfvhW46soeuFF3eo0vNNrRu3snhkQtaARceJT-XTGU,14503
102
+ snakeorm/expressions/paths.py,sha256=vvfKMlzslBiHHMIuRORwkbzc24iF5e_MJDfKkpSRV8o,1550
103
+ snakeorm/expressions/scalar.py,sha256=eJJ54zMbcVwkvoyhuDpAQphjijoZ5Pp9df1EcZkk0RY,12362
104
+ snakeorm/expressions/window.py,sha256=gWcIBwB--1HFTncMbWtERzeiprEuTe6e8mvZ6GM9Dns,10253
105
+ snakeorm/fields/__init__.py,sha256=EoSx4O8z0QnpHKm2qw0UWRzeOMo0Rd7qCjaJGtp-jD0,3531
106
+ snakeorm/fields/check.py,sha256=eL_i2nkw-hkci6msivTgoypRJu7xh6ypodZiif0-d9s,3238
107
+ snakeorm/fields/column.py,sha256=uOvUrSMJ9kgcZ63l-NyEO8VUMsfPpU8jrRxJ7dOmbJo,13098
108
+ snakeorm/fields/enum.py,sha256=OP7zSQk8AMaDGUwugs0v0H5cBJ_shCYjnRnsbpMn78A,2599
109
+ snakeorm/fields/index.py,sha256=GzsbwNXUGW9GjIPoChY7JQhjwHsj9_NytV5aSpH87Xg,1837
110
+ snakeorm/fields/relationship.py,sha256=VjzsrxtSAmEwj29W3NSFRbhl_at90b7jG1KSDedlAc0,33503
111
+ snakeorm/fields/typed.py,sha256=MdjAdWPskaaI4jQ0ZqrOtfoJbI5YF65NuY8GjP6nDGw,24083
112
+ snakeorm/helpers/__init__.py,sha256=oTAk4BaEG7b_6vFomwBiYuW0ZyjQco-2hcMUsAadOHI,90
113
+ snakeorm/helpers/annotations.py,sha256=u3VlsJeT5VoXelQYRYkg27kTlmvrlmCoX4kl5fabipI,822
114
+ snakeorm/helpers/inheritance.py,sha256=sAOway-_q_lbA6lO2LJECTjc4vxwfU2fMj51-Krd3nM,1603
115
+ snakeorm/helpers/pyliteral.py,sha256=dnjCUibXSR4fQmIVqdc3N2s65xdJMNc-2QRVvlsPawU,2034
116
+ snakeorm/helpers/pytype.py,sha256=oSRxns3aqFzh3o3AJ845ca2-bd5MfEatN_GXzqkftzM,2354
117
+ snakeorm/introspection/__init__.py,sha256=3UsiXVTAWjZ1d9uby9ZegI8RFs8UDoJoNsat_uHiWa0,844
118
+ snakeorm/introspection/base.py,sha256=7MvpJtf-au_23wPrz2yI_h3Qvkg87crRZJqDAzl-7zI,1368
119
+ snakeorm/introspection/drift.py,sha256=HqyFIYwigSXbUH8VhI5Xy4Z5KZPh3Idj7gCrFfyTes4,3898
120
+ snakeorm/introspection/models.py,sha256=iCYOqaXasOxDDSAFg7WNAOHoMDju4dJHGoccS__fyMI,30538
121
+ snakeorm/introspection/mysql.py,sha256=R3mOWCQDWXInFQ5JyMO3eLdSBOhR7dgsbttBlkg7AME,15869
122
+ snakeorm/introspection/postgres.py,sha256=lYIi-vlUBjwHqckDnnTjwBwFg7s7XzA1o8sYRLSzokg,18469
123
+ snakeorm/introspection/sqlite.py,sha256=NmCByKel-lXAz224P8DF0CO2_akgzbV6qNG0VbDNgos,8512
124
+ snakeorm/introspection/unsupported.py,sha256=ZqnsHsIuUxMqzcF1Lp6C3XLySFrfbBn8MIfj_QBo06Q,4358
125
+ snakeorm/linker/__init__.py,sha256=h50PEU2-uwHVWIFHkS-qJhjOeNSy7S5H32adgw5qB-I,135
126
+ snakeorm/linker/linker.py,sha256=tEgtKKnamfA01nobCqY22fO9fjKz_03iHg7kkuka7Ek,24715
127
+ snakeorm/metadata/__init__.py,sha256=d93E2t3BCQEZp4BS1W6kAv7x4jczba2PxiTy0BgJWmE,2616
128
+ snakeorm/metadata/check.py,sha256=30cEN9AzQfbqp-KkDW74zjVJfceDCYgOXhtXeyPuAXo,1401
129
+ snakeorm/metadata/column.py,sha256=oMxH-aHQ4TSvoscMw25V6-1DFbIjj2XjaNpjUKSzYSY,9142
130
+ snakeorm/metadata/enum_storage.py,sha256=KftQksmdoPc9cySVG0w39zIQ-mDzDtuDzLLJzDkbjcs,1600
131
+ snakeorm/metadata/fk_action.py,sha256=V9oI2hc0WbXvUtH8wzNUUJb2fjkNiDJCysXIEfbi0fI,500
132
+ snakeorm/metadata/foreign_key.py,sha256=gF-klSkb4YXICvckvq1aqHrvh8X5K9bQz9f3qXgm3Gk,850
133
+ snakeorm/metadata/index.py,sha256=64lAVN8MlMwFOfb5HXqXtGWLsngNR4FIfUFukip8VFM,1772
134
+ snakeorm/metadata/index_method.py,sha256=neFbGeeFFesy-4r0KoVzLTYCz31L9N4gHwFeF2ZyPv0,636
135
+ snakeorm/metadata/int_size.py,sha256=Rot4E1kbaACdfq43KRfXpeY8ka0Ehm1qAQQ5cdSVkFc,864
136
+ snakeorm/metadata/json_storage.py,sha256=PvounEiA_4fJ8wvPcm4vj4E8pww3Adcy0QLFb9ueC4I,739
137
+ snakeorm/metadata/polymorphic.py,sha256=8yAGVHSoy7bxcyxcTKAVAAuLklCBSf2MvV5tj_Vz3aM,1223
138
+ snakeorm/metadata/primary_key.py,sha256=Tm7n74kIB4o-bZN1BK1_l09lV13cmKvtRgXGfTTV08Y,676
139
+ snakeorm/metadata/relationship.py,sha256=4uEb5rKWA0cCrnBZhZ5EE-0b6hP-1OWveZI5ikQLeDY,2578
140
+ snakeorm/metadata/relationship_kind.py,sha256=xXI3ME4kbh1ZOXDI_uudD_H2l2VHahqrnHUX3Uauq-M,1734
141
+ snakeorm/metadata/routine.py,sha256=GTILeiDBJWWiA8PQyoOv0-_BXg71mKnjb3mtioOGjXw,844
142
+ snakeorm/metadata/server_default.py,sha256=p744uQW0ee3-QOui9TE2X1FYaDKE4uqMolQR4FzH_6o,757
143
+ snakeorm/metadata/table.py,sha256=ZT5Ul3cs4kvsBcTadbEe-mlQt4yo-YBoCMU_zzPwX0c,4490
144
+ snakeorm/metadata/table_kind.py,sha256=55-mRkDsbja23F73MkI23sal6thNo4FZxVNEMtoQCW8,743
145
+ snakeorm/metadata/trigger.py,sha256=4Gh4k9ZV0KUT5tVP_igvm8Xq1Bc-AVHeWOMxHS1ZkvU,2057
146
+ snakeorm/metadata/type_params.py,sha256=X2OmrNq-HFZb9db5MHs9nfTfBfLRjCIbUbIVG-PBNNU,15918
147
+ snakeorm/migration/__init__.py,sha256=XHXCZLz6WAlFKE9cCBSdqOcjgNaOLvysRuKjWVxIf9o,6099
148
+ snakeorm/migration/asyncrunner.py,sha256=GkRsvbfr4e0mbHjyYlqXsMRQZiPibDBiB7UpltxMDkg,7505
149
+ snakeorm/migration/autodetect.py,sha256=nRaTjRVnqdZM0Uu5UzbqGYCPGwcUTUKDRGuX4315aeY,4389
150
+ snakeorm/migration/ddl.py,sha256=RbkshLX-gq69IqHhNwHwKaSnQpphJ1oC3u__5_a-1bw,55389
151
+ snakeorm/migration/diff.py,sha256=-TcVdJkQGRfcYKeMbOWZNsD3lBKwN3duZW6kbqqxyyM,33387
152
+ snakeorm/migration/loader.py,sha256=1d_8lR-D4pPArgSR81tLV7c2ufthEbZg66gXW39JcVQ,2836
153
+ snakeorm/migration/operations.py,sha256=a7i8XeC7GdiEpEnJxw5TE52yBxt6bbfbwfuI_Kr9CJM,38440
154
+ snakeorm/migration/planner.py,sha256=lkf5KPN2TrUheXKV6PGWlDJ6asSp9sWTVoL3qiGcXno,3302
155
+ snakeorm/migration/realize.py,sha256=HiA9T7I5QVQDkEqAckKHyBxd14tc4c-by6kdEAVH01s,23427
156
+ snakeorm/migration/renames.py,sha256=74t40eWSiS_C-dj7f6f8nl9SfLDl55I1AlAeM-3COCg,7381
157
+ snakeorm/migration/render.py,sha256=YL5mDBDrETU1uzfnBgtdaMMhFAFR-ITvupHPiikEIYo,45045
158
+ snakeorm/migration/runner.py,sha256=0UnT2G85-B_w0zgozx8uWsYEs12ZQCu0xrmhz1-a12A,15383
159
+ snakeorm/migration/squash.py,sha256=MLmf_DCyLfWwnTyULIYwl3-jx6ghM6fhlLbBqzkyrTI,3159
160
+ snakeorm/migration/state.py,sha256=aVfl0iCnec_wzKctnKTdTUTtJVTiJN4IQ_mFmQ2KCSk,4716
161
+ snakeorm/query/__init__.py,sha256=kEKbmgVRQ4YOiPQYLcgAAz87iMHAIgJTqdCQTL3156w,560
162
+ snakeorm/query/compound.py,sha256=AUmwZ1Vu4CrE6V7Tv-AjaV9Di0totl3hiXXBDVQ0PXQ,13672
163
+ snakeorm/query/join_kind.py,sha256=nXHFOo7gFtCPohf-REgPP-Q3eKK35CLaBf2Lpb0OgRA,685
164
+ snakeorm/query/joined.py,sha256=fjSctbgUSrebeo_yoR459vBq_UxvTFAQOU2SpoEQ6OE,6204
165
+ snakeorm/query/query.py,sha256=h-32vd4SehNdG_Ro_4NP3v3nGQXNebCEl-u5rGmVGSQ,45193
166
+ snakeorm/query/recursive.py,sha256=0L3u9h33Ysm6U7mhj5IHZYG1XgEPqtnFt44GybgLB2c,10826
167
+ snakeorm/registry/__init__.py,sha256=o1e7NmEwRNPPiJRU84tcjncp8u0PeUYw3jkvsiUVB9A,243
168
+ snakeorm/registry/by_module.py,sha256=KMEcSLErvMW5RwzHIc_twM1mjU2GJNCrV8oTJnbJi2E,7096
169
+ snakeorm/registry/registry.py,sha256=jhXNGixjCMc1LilHLSc2_9dhJBc86sm3ka3TCbB-fbg,7773
170
+ snakeorm/session/__init__.py,sha256=Ut_zFwbn3fxRdoMDrjD82RkeCGUuD1kOVXpxUe5a7d4,468
171
+ snakeorm/session/asyncsession.py,sha256=zhOmEL1yu7irD6i8i0-pBw4KWi8Pzz1UP3yVxgmmPcU,28100
172
+ snakeorm/session/coercion.py,sha256=_VbS6dje0tEILVtNfuSIYAykq5uqZiiBe-IXUyc6A3w,13782
173
+ snakeorm/session/factory.py,sha256=Cr2V1hriAOjUfbTSiHzBR_0CZ7175Sj_Uzj6GmgIH_8,1519
174
+ snakeorm/session/guards.py,sha256=yk8TVZ1dhxw9DXRpNIy5n0Lm9UPdcdC-qLsSrx3PLXA,13495
175
+ snakeorm/session/isolation.py,sha256=bRl2WZeY41LZcsycpS3pSJorE9qHtuky5w_lf7O8Nbc,946
176
+ snakeorm/session/mapper.py,sha256=Hs58azawWilfiit3lDYy1U3SzHyY4m1r2VqMLij-ltw,9219
177
+ snakeorm/session/planning.py,sha256=TAyVpWkxFWK0fW96PnblNTBw3HDloXFGMmbQ8-63dBE,34959
178
+ snakeorm/session/retry.py,sha256=PwIxs6MZW-SkRsKOYBS0oHf9_KCZEa0iXGCjV1jGTH0,4336
179
+ snakeorm/session/session.py,sha256=ddXsLOKVh0wG1PmF80SVX44MMJQSgnOXYESWJ_XQ7cI,40015
180
+ snakeorm/session/shared.py,sha256=DS0cvmW8ww5mYikwoe4Ls6NoJfSa2Kbp08YiRCvUZVg,16165
181
+ snakeorm/sql/__init__.py,sha256=qa4MkP3h55e-2fC-ZZciK3ChGLUG-doigIel9QOfP8E,1124
182
+ snakeorm/sql/adapt.py,sha256=0-bFPAkniub5u6ZDE-WtLVeFBwkr42WlDDsJipJ5gPQ,5632
183
+ snakeorm/sql/aggregate.py,sha256=3m1CUtLQwWNbOmDfi56dgsPjhrqhEgZuCdZDd5bojEM,9440
184
+ snakeorm/sql/condition.py,sha256=dGKJG8efnBiO8W-t_NQTAX-Qpv0TGXrOIcn3RbxJk9c,15267
185
+ snakeorm/sql/delete.py,sha256=u41Nz-dQAV3i1QQ73nPfJ89kZ6Orq_6ZWXjE5qe9Vp4,1770
186
+ snakeorm/sql/insert.py,sha256=XMukIKlxYjcvf_GjBVtjhXHFaxYSpVu3NcdQ18GqGDM,6045
187
+ snakeorm/sql/joins.py,sha256=rh7yHexrj3J8vZu6rh0iblaGeyugBLbLKY3fY7zxPk8,7526
188
+ snakeorm/sql/pk_subquery.py,sha256=58vd0ZZ8qDd-ae278zppzNDVRuZXTM5aAM2AgTQU_lk,2310
189
+ snakeorm/sql/refs.py,sha256=HY68tKVegwULk7-t13ljIzs5Hl5omOYkBqhGGTEimIs,728
190
+ snakeorm/sql/resolver.py,sha256=yMTDBSouuxHSzSC_7J-OlWqrb2nCLrw0gG2PVWVggZE,1216
191
+ snakeorm/sql/select.py,sha256=utTv5qauHDKHeROxWb89TzgK2CF-9jHozbHC9sDu8RI,7028
192
+ snakeorm/sql/update.py,sha256=vjVLhDhFVpeIPfbEgoeRO1lOGWAD5gQQepDEcTnc3eY,3169
193
+ snakeorm/sql/value.py,sha256=uRkJqrxWXimNEqlr-aH3ZRXppdlww_MW82zH9wW7bes,22920
194
+ snake_orm-0.1.0b1.dist-info/METADATA,sha256=08rQZl7tpnhQjzmD3UbRt3cln-fGGf4bELmBR-1t0Es,14688
195
+ snake_orm-0.1.0b1.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
196
+ snake_orm-0.1.0b1.dist-info/entry_points.txt,sha256=5U4-q754fJzSepwYrVP1noJO887yafx8sCTKnhM5cvk,47
197
+ snake_orm-0.1.0b1.dist-info/licenses/LICENSE,sha256=bUCxdUV3QMfnRb-mnSglz35O7RvBFiHyOpDHOB-DvPY,1076
198
+ snake_orm-0.1.0b1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ snakeorm = snakeorm.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anthony Velez Tapia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.