snekql 0.1.0__tar.gz

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 (38) hide show
  1. snekql-0.1.0/.gitignore +2 -0
  2. snekql-0.1.0/.python-version +1 -0
  3. snekql-0.1.0/AGENTS.md +24 -0
  4. snekql-0.1.0/CHANGELOG.md +13 -0
  5. snekql-0.1.0/CONTEXT.md +41 -0
  6. snekql-0.1.0/PKG-INFO +210 -0
  7. snekql-0.1.0/PRD.md +473 -0
  8. snekql-0.1.0/README.md +200 -0
  9. snekql-0.1.0/main.py +6 -0
  10. snekql-0.1.0/pyproject.toml +178 -0
  11. snekql-0.1.0/snekql/__init__.py +114 -0
  12. snekql-0.1.0/snekql/__init__.pyi +102 -0
  13. snekql-0.1.0/snekql/_pool.py +219 -0
  14. snekql-0.1.0/snekql/errors.py +98 -0
  15. snekql-0.1.0/snekql/expressions.py +62 -0
  16. snekql-0.1.0/snekql/model.py +468 -0
  17. snekql-0.1.0/snekql/py.typed +0 -0
  18. snekql-0.1.0/snekql/query.py +788 -0
  19. snekql-0.1.0/snekql/runtime.py +297 -0
  20. snekql-0.1.0/snekql/schema.py +166 -0
  21. snekql-0.1.0/snekql/storage.py +596 -0
  22. snekql-0.1.0/snekql/validation.py +40 -0
  23. snekql-0.1.0/tests/__init__.py +1 -0
  24. snekql-0.1.0/tests/test_architecture_modules.py +26 -0
  25. snekql-0.1.0/tests/test_boundary_validation.py +59 -0
  26. snekql-0.1.0/tests/test_database_initialization.py +266 -0
  27. snekql-0.1.0/tests/test_database_runtime.py +166 -0
  28. snekql-0.1.0/tests/test_delete_execution.py +141 -0
  29. snekql-0.1.0/tests/test_insert_execution.py +121 -0
  30. snekql-0.1.0/tests/test_model_declaration.py +208 -0
  31. snekql-0.1.0/tests/test_predicate_and_mutation_execution.py +240 -0
  32. snekql-0.1.0/tests/test_public_api.py +287 -0
  33. snekql-0.1.0/tests/test_public_typing.py +109 -0
  34. snekql-0.1.0/tests/test_select_execution.py +274 -0
  35. snekql-0.1.0/tests/test_storage_codecs.py +212 -0
  36. snekql-0.1.0/tests/test_update_execution.py +117 -0
  37. snekql-0.1.0/tests/test_v1_integration.py +281 -0
  38. snekql-0.1.0/uv.lock +254 -0
@@ -0,0 +1,2 @@
1
+ **/__pycache__
2
+ *.db
@@ -0,0 +1 @@
1
+ 3.14
snekql-0.1.0/AGENTS.md ADDED
@@ -0,0 +1,24 @@
1
+ # AGENTS.md
2
+
3
+ ## GitHub workflow
4
+
5
+ - Use the `gh` CLI for GitHub interactions: issues, PRs, comments, labels, and repo metadata.
6
+ - Do not hand-edit GitHub URLs or assume issue state; query with `gh issue view/list` when needed.
7
+ - Implementation work should reference the relevant GitHub issue.
8
+ - When starting a new unit of work, first stash any uncommited changes, do a `git fetch`, then create a new branch, based on the latest `origin/main` branch.
9
+ - All work should be done in a branch, and when a unit of work is complete, open a PR against `main`. Only merge the PR if explicitly told to do so.
10
+ - When doing feature/bug-fixing/refactoring or any code-related work, use TDD.
11
+
12
+ ## Testing and validation
13
+
14
+ - Use `snektest` for tests.
15
+ - Look up the installed distribution metadata for `snektest` using `importlib.metadata.distribution("snektest").read_text("METADATA").` The README.
16
+ - Use `pyright` for static typing validation.
17
+ - Preferred validation commands:
18
+
19
+ ```bash
20
+ uv run snektest
21
+ uv run pyright .
22
+ uv run ruff check .
23
+ uv run ruff format --check .
24
+ ```
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - Unreleased
4
+
5
+ Initial v1 release candidate.
6
+
7
+ - Typed table model declarations with pending/fetched lifecycle states.
8
+ - SQLite-first storage declarations and logical codecs.
9
+ - Immutable query builders for single-table select/insert/update/delete.
10
+ - Async SQLite runtime with bounded connection pool and transactions.
11
+ - Deterministic SQLite `STRICT` table creation and schema verification.
12
+ - Public `SnekqlError` exception hierarchy.
13
+ - PEP 561 typing support with `py.typed` and a public facade stub.
@@ -0,0 +1,41 @@
1
+ # snekql
2
+
3
+ snekql is a library for declaring relational data contracts and executing typed SQL-shaped operations against a database. It exists to give Python applications an explicit query layer and runtime without becoming an ORM.
4
+
5
+ ## Language
6
+
7
+ **Query Builder**:
8
+ The layer that declares relational data contracts and builds typed SQL-shaped operations.
9
+ _Avoid_: ORM, repository
10
+
11
+ **Query Runtime**:
12
+ The layer that executes built queries against a database and manages database-backed execution concerns.
13
+ _Avoid_: ORM session, persistence layer
14
+
15
+ **Database**:
16
+ An initialized snekql runtime service that owns database connectivity, schema startup work, and transaction entry.
17
+ _Avoid_: Pool, uninitialized database config
18
+
19
+ **Transaction**:
20
+ A database transaction exposed directly through the library as the unit within which reads and writes are executed.
21
+ _Avoid_: Unit of Work, session
22
+
23
+ **Table Model**:
24
+ A Python class that declares a table's row contract and serves as an ergonomic front end over the query builder's schema model.
25
+ _Avoid_: Entity, ORM model
26
+
27
+ **Dialect**:
28
+ The database-specific SQL and schema behavior targeted by compilation and verification.
29
+ _Avoid_: Driver, runtime
30
+
31
+ **Server Default**:
32
+ A database-supplied column value that is filled in by the database when an insert omits that column.
33
+ _Avoid_: Python default, constructor default
34
+
35
+ **Pending Model**:
36
+ A model instance constructed by application code before it has been materialized from the database.
37
+ _Avoid_: Draft, unsaved entity
38
+
39
+ **Fetched Model**:
40
+ A model instance materialized from database query results by the Query Runtime.
41
+ _Avoid_: Loaded, read model, entity
snekql-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,210 @@
1
+ Metadata-Version: 2.4
2
+ Name: snekql
3
+ Version: 0.1.0
4
+ Summary: Async typed query builder and runtime for SQLite STRICT tables
5
+ Requires-Python: >=3.14
6
+ Requires-Dist: aiosqlite>=0.22.1
7
+ Requires-Dist: annotated-types>=0.7.0
8
+ Requires-Dist: pydantic>=2.13.4
9
+ Description-Content-Type: text/markdown
10
+
11
+ # snekql
12
+
13
+ snekql is a Python async-first query builder and query runtime for SQL.
14
+ It gives applications explicit SQL-shaped operations, typed
15
+ model declarations, runtime validation, startup schema checks, and transaction-
16
+ based execution without becoming an ORM.
17
+
18
+ ## Quick start
19
+
20
+ ```python
21
+ from __future__ import annotations
22
+
23
+ from datetime import datetime
24
+ from pathlib import Path
25
+
26
+ from snekql import (
27
+ MISSING,
28
+ CurrentTimestamp,
29
+ Database,
30
+ DateTime,
31
+ Fetched,
32
+ Integer,
33
+ Model,
34
+ Pending,
35
+ Text,
36
+ insert,
37
+ select,
38
+ )
39
+
40
+
41
+ class User[S = Pending](Model[S, "User[Fetched]"]):
42
+ id: User.GenCol[int] = Integer(
43
+ primary_key=True,
44
+ auto_increment=True,
45
+ default=MISSING,
46
+ )
47
+ email: User.Col[str] = Text(nullable=False)
48
+ status: User.Col[str] = Text(nullable=False, default="active")
49
+ created_at: User.GenCol[datetime] = DateTime(
50
+ server_default=CurrentTimestamp(),
51
+ default=MISSING,
52
+ )
53
+
54
+
55
+ async def main() -> None:
56
+ db = await Database.initialize(
57
+ database=Path("app.db"),
58
+ models=[User],
59
+ schema_policy="strict",
60
+ pool_size=5,
61
+ acquire_timeout=30.0,
62
+ )
63
+ try:
64
+ async with db.transaction(timeout=5.0) as tx:
65
+ await tx.execute(insert(User(email="alice@example.com")))
66
+ user = await tx.fetch_one(
67
+ select(User).where(User.email.eq("alice@example.com")),
68
+ )
69
+ if user is not None:
70
+ print(user.email)
71
+ finally:
72
+ await db.close()
73
+ ```
74
+
75
+ ## Model declaration
76
+
77
+ Models directly subclass `Model[S, "ModelName[Fetched]"]`. Application-created
78
+ instances are `Pending`; database reads return `Fetched` instances.
79
+
80
+ ```python
81
+ class AuditLog[S = Pending](Model[S, "AuditLog[Fetched]"]):
82
+ __tablename__ = "audit_log"
83
+
84
+ id: AuditLog.GenCol[int] = Integer(
85
+ primary_key=True,
86
+ auto_increment=True,
87
+ default=MISSING,
88
+ )
89
+ message: AuditLog.Col[str] = Text(nullable=False)
90
+ created_at: AuditLog.GenCol[datetime] = DateTime(
91
+ server_default=CurrentTimestamp(),
92
+ default=MISSING,
93
+ )
94
+ ```
95
+
96
+ Rules to remember:
97
+
98
+ - `Col[T]` is a normal persisted column.
99
+ - `GenCol[T]` is server/generated; pending values may be `MISSING`, fetched
100
+ values are `T`.
101
+ - If `__tablename__` is omitted, class names become snake_case table names.
102
+ - Models are immutable after construction/materialization.
103
+ - Fetched models are produced by database reads only.
104
+
105
+ ## Storage classes
106
+
107
+ - `Integer`
108
+ - `Real`
109
+ - `Text`
110
+ - `Blob`
111
+ - `Json` stores `JSON` text.
112
+ - `Boolean` stores `0` / `1` in an `INTEGER` column.
113
+ - `DateTime` stores UTC text as `YYYY-MM-DDTHH:MM:SS.SSSZ`.
114
+
115
+ `CurrentTimestamp()` is the only v1 server default and is valid only on
116
+ `DateTime` `GenCol` fields.
117
+
118
+ ## Queries
119
+
120
+ Queries are immutable. Chaining returns new query objects.
121
+
122
+ ```python
123
+ from snekql import delete, insert, select, update
124
+
125
+ select(User).all()
126
+ select(User.email).where(User.status.eq("active"))
127
+ select(User.email, User.status).where(User.email.like("%@example.com"))
128
+
129
+ insert(User(email="alice@example.com"))
130
+
131
+ update(User).set(User.status.to("disabled")).where(
132
+ User.email.eq("alice@example.com"),
133
+ )
134
+
135
+ delete(User).where(User.email.eq("retired@example.com"))
136
+ delete(User).all() # explicit full-table delete
137
+ ```
138
+
139
+ Filtering is explicit: `select`, `update`, and `delete` must choose exactly one
140
+ of `.where(...)` or `.all()` before execution. Predicates use methods such as
141
+ `.eq(...)`, `.ne(...)`, `.is_null()`, `.in_(...)`, `.like(...)`; Python
142
+ comparison operators are not part of the v1 API.
143
+
144
+ ## Runtime
145
+
146
+ `Database.initialize(...)` is the only public construction path.
147
+
148
+ ```python
149
+ db = await Database.initialize(database=Path("app.db"), models=[User])
150
+ memory_db = await Database.initialize(database=":memory:")
151
+ ```
152
+
153
+ Use transactions for all work:
154
+
155
+ ```python
156
+ async with db.transaction() as tx:
157
+ rows = await tx.fetch_all(select(User).all())
158
+ first_email = await tx.fetch_one(select(User.email).all())
159
+ await tx.execute(update(User).set(User.status.to("inactive")).all())
160
+ ```
161
+
162
+ Runtime methods:
163
+
164
+ - `fetch_all(select(...))` returns all result rows.
165
+ - `fetch_one(select(...))` returns the first row or `None`.
166
+ - `execute(insert/update/delete)` returns `None`.
167
+ - `close()` is async and idempotent after a successful close.
168
+
169
+ ## Schema startup
170
+
171
+ When initialized with `models=[...]`, snekql:
172
+
173
+ 1. Preserves model order.
174
+ 2. Rejects duplicate resolved table names.
175
+ 3. Creates missing `STRICT` tables.
176
+ 4. Verifies existing tables by comparing deterministic generated DDL with
177
+ SQLite metadata.
178
+ 5. Treats drift according to `schema_policy`: `"strict"` raises,
179
+ `"warn"` logs and continues.
180
+
181
+ ## Error model
182
+
183
+ Every intentional package-originated exception is a `SnekqlError` subclass.
184
+ Use `SnekqlError` to catch all snekql failures, or catch narrower subclasses:
185
+
186
+ - `ModelDeclarationError`, `ModelValidationError`, `FrozenModelError`
187
+ - `QueryConstructionError`, `QueryCompilationError`
188
+ - `DatabaseClosedError`, `PoolTimeoutError`, `TransactionClosedError`,
189
+ `ExecutionError`
190
+ - `SchemaVerificationError`
191
+
192
+ `ExecutionError` preserves `sql` and `params` for debugging.
193
+
194
+ ## Public API
195
+
196
+ Agent navigation map:
197
+
198
+ - `snekql/model.py`: model metaclass, table metadata, pending/fetched
199
+ materialization.
200
+ - `snekql/storage.py`: column descriptors, SQLite storage metadata, value
201
+ codecs.
202
+ - `snekql/expressions.py`: predicates, ordering, update assignments.
203
+ - `snekql/query.py`: query builders and SQL compilation.
204
+ - `snekql/runtime.py`: `Database`, `Transaction`, execution methods.
205
+ - `snekql/_pool.py`: internal async SQLite connection pool.
206
+ - `snekql/schema.py`: `STRICT` DDL generation and schema verification.
207
+ - `snekql/errors.py`: public exception hierarchy.
208
+ - `tests/test_public_typing.py`: type-checker prototypes for the public API.
209
+ - `PRD.md`: full v1 product contract.
210
+ - `CONTEXT.md`: project language and terminology.