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.
- snekql-0.1.0/.gitignore +2 -0
- snekql-0.1.0/.python-version +1 -0
- snekql-0.1.0/AGENTS.md +24 -0
- snekql-0.1.0/CHANGELOG.md +13 -0
- snekql-0.1.0/CONTEXT.md +41 -0
- snekql-0.1.0/PKG-INFO +210 -0
- snekql-0.1.0/PRD.md +473 -0
- snekql-0.1.0/README.md +200 -0
- snekql-0.1.0/main.py +6 -0
- snekql-0.1.0/pyproject.toml +178 -0
- snekql-0.1.0/snekql/__init__.py +114 -0
- snekql-0.1.0/snekql/__init__.pyi +102 -0
- snekql-0.1.0/snekql/_pool.py +219 -0
- snekql-0.1.0/snekql/errors.py +98 -0
- snekql-0.1.0/snekql/expressions.py +62 -0
- snekql-0.1.0/snekql/model.py +468 -0
- snekql-0.1.0/snekql/py.typed +0 -0
- snekql-0.1.0/snekql/query.py +788 -0
- snekql-0.1.0/snekql/runtime.py +297 -0
- snekql-0.1.0/snekql/schema.py +166 -0
- snekql-0.1.0/snekql/storage.py +596 -0
- snekql-0.1.0/snekql/validation.py +40 -0
- snekql-0.1.0/tests/__init__.py +1 -0
- snekql-0.1.0/tests/test_architecture_modules.py +26 -0
- snekql-0.1.0/tests/test_boundary_validation.py +59 -0
- snekql-0.1.0/tests/test_database_initialization.py +266 -0
- snekql-0.1.0/tests/test_database_runtime.py +166 -0
- snekql-0.1.0/tests/test_delete_execution.py +141 -0
- snekql-0.1.0/tests/test_insert_execution.py +121 -0
- snekql-0.1.0/tests/test_model_declaration.py +208 -0
- snekql-0.1.0/tests/test_predicate_and_mutation_execution.py +240 -0
- snekql-0.1.0/tests/test_public_api.py +287 -0
- snekql-0.1.0/tests/test_public_typing.py +109 -0
- snekql-0.1.0/tests/test_select_execution.py +274 -0
- snekql-0.1.0/tests/test_storage_codecs.py +212 -0
- snekql-0.1.0/tests/test_update_execution.py +117 -0
- snekql-0.1.0/tests/test_v1_integration.py +281 -0
- snekql-0.1.0/uv.lock +254 -0
snekql-0.1.0/.gitignore
ADDED
|
@@ -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.
|
snekql-0.1.0/CONTEXT.md
ADDED
|
@@ -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.
|