mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-12-23 08:21:09 +00:00
111 lines
No EOL
4.8 KiB
Text
111 lines
No EOL
4.8 KiB
Text
---
|
|
name: 2025-11-26-py-bindings-async
|
|
---
|
|
|
|
<Output path="./turso/lib_aio.py">
|
|
|
|
<Code model="openai/gpt-5" language="python">
|
|
|
|
Turso - is the SQLite compatible database written in Rust.
|
|
One of the important features of the Turso - is async IO execution which can be used with modern storage backend like IO uring.
|
|
|
|
Your task is to generate ASYNC Python driver with the API similar aiosqlite (which is similar to DB-API 2 of sqlite module) by REUSING current sync driver imiplementation.
|
|
|
|
# Rules
|
|
|
|
General rules for driver implementation you **MUST** follow and never go against these rules:
|
|
- STRUCTURE of the implementation
|
|
* Declaration order of elements and semantic blocks MUST be exsactly the same
|
|
* (details and full enumerations omited in the example for brevity but you must generate full code)
|
|
```py
|
|
# all imports must be at the beginning - no imports in the middle of function
|
|
from typing import ...
|
|
from queue import SimpleQueue
|
|
|
|
from .worker import Worker
|
|
from .lib import (
|
|
Connection as BlockingConnection,
|
|
Cursor as BlockingCursor,
|
|
...
|
|
)
|
|
|
|
# Connection goes FIRST
|
|
class Connection:
|
|
def __init__(connector: Callable[[], BlockingConnection]): ...
|
|
# internal worker MUST be stopped when connection goes out of context scope
|
|
# close must wait for worker to be stopped
|
|
async def close(...): ...
|
|
# make Connection instance awaitable
|
|
def __await__(...): ...
|
|
async def __aenter__(...): ...
|
|
# just close the connection - do not add any extra logic
|
|
async def __aexit__(...): ...
|
|
await self.close()
|
|
...
|
|
|
|
# Cursor goes SECOND
|
|
class Cursor: ...
|
|
|
|
# connect is not async because it returns awaitable Connection
|
|
# same signature as in the lib.py
|
|
def connect(...): ...
|
|
|
|
```
|
|
- USE already implemented driver - DO NOT copy it
|
|
- USE primitives from the queue module in order to send work to the separate Thread
|
|
- USE per-connection thread to execute database work
|
|
- FOLLOW the pattern:
|
|
```py
|
|
# create future for completion
|
|
future = asyncio.get_event_loop().create_future()
|
|
# put the work to the unbounded queue
|
|
queue.put_nowait((future, function))
|
|
```
|
|
- NEVER block main event loop (e.g., do not use Queue with block=True - prefer unbounded SimpleQueue instead)
|
|
- REUSE data structures from lib.py if possible (e.g. exceptions)
|
|
- DO NOT accept extra `loop` parameter - ALWAYS use `asyncio.get_event_loop()`
|
|
- CREATE separate thread to execute Turso request and proxy all methods to these thread in order to not block event loop with database work
|
|
- DO NOT reimplement logic of the execution - reuse methods and classes from lib.py
|
|
- AVOID cryptic names - prefer short but concise names (wr is BAD, full_write is GOOD)
|
|
- NEVER put import in the middle of a function - always put all necessary immports at the beginning of the file
|
|
- FOCUS on code readability: if possible extract helper function but make sure that it will be used more than once and that it really contribute to the code readability
|
|
- WATCH OUT for variables scopes and do not use variables which are no longer accessible
|
|
- WATCH OUT for operations atomicity: DO NOT split atomic work in units which may be mixed with other work in async context - breaking atomicity guarantees
|
|
- USE forward reference string in when return method type depends on its class:
|
|
```py
|
|
class T:
|
|
def f(self) -> 'T':
|
|
```
|
|
|
|
# Implementation
|
|
|
|
- Put compact citations from the official DB-API doc if this is helpful
|
|
- Driver must implement context API for Python to be used like `with ... as conn:` ...
|
|
|
|
# Blocking driver
|
|
|
|
For turso db execution you MUST reuse blocking implementation:
|
|
|
|
<File path="./turso/lib.py" />
|
|
<File path="./turso/worker.py" />
|
|
<File path="./turso/__init__.py" />
|
|
|
|
# SQLite-like DB API
|
|
|
|
Make driver API similar to the SQLite DB-API2 for the python.
|
|
|
|
Pay additional attention to the following aspects:
|
|
* SQLite DB-API2 implementation implicitly opens transaction for DML queries in the execute(...) method:
|
|
> If autocommit is LEGACY_TRANSACTION_CONTROL, isolation_level is not None, sql is an INSERT, UPDATE, DELETE, or REPLACE statement, and there is no open transaction, a transaction is implicitly opened before executing sql.
|
|
- MAKE SURE this logic implemented properly
|
|
* Implement .rowcount property correctly, be careful with `executemany(...)` methods as it must return rowcount of all executed statements (not just last statement)
|
|
* Convert exceptions from rust layer to appropriate exceptions documented in the sqlite3 db-api2 docs
|
|
* BE CAREFUL with implementation of transaction control. Make sure that in LEGACY_TRANSACTION_CONTROL mode implicit transaction will be properly commited in case of cursor close
|
|
|
|
Also, make types and API compatible with aiosqlite (note, that aiosqlite implements some other methods, which can be omitted now, like interrupt)
|
|
|
|
<Shell cmd="python3 -m pydoc aiosqlite" />
|
|
|
|
</Code>
|
|
|
|
</Output> |