sqlalchemy-libsql-native
SQLAlchemy compatibility DBAPI facade + dialect for remote libSQL (Turso managed libSQL / Hrana).
Note: This repository is a read-only release mirror. Development happens in the private Keelson monorepo; issues are welcome here, but pull requests are not accepted — changes land through the next release.
Wraps the frozen upstream driver
(libsql==0.1.11, exact pin) without forking it, and adds the minimal contract
SQLAlchemy needs for exception classification and disconnect recovery.
This is a bridge, not a permanent artifact: it exists to make the current frozen driver usable with SQLAlchemy today, and is expected to shrink or retire as upstream grows a real DBAPI surface. Full PEP 249 conformance is not claimed; the goal is limited to making SQLAlchemy's exception-classification and disconnect-recovery machinery work correctly.
Design goals:
- Raw driver errors (
builtins.ValueError) are classified into a PEP 249-shaped exception hierarchy, structuredSQLITE_CONSTRAINT*codes taking priority over message text, with an unknown error failing closed toDatabaseError. - Hrana stream loss/expiry maps to
OperationalErrorso SQLAlchemy'sis_disconnect/pool_pre_pinginvalidation and recovery work; nothing else is ever treated as a disconnect. - The dialect owns the URL/argument contract and the pool policy (NullPool for remote URLs by default); the adapter never retries on the caller's behalf.
- Deterministic, self-contained fault injection (no external services beyond Docker) proves the above, including commit ambiguity on a lost commit acknowledgement.
Invariants
- The package is standalone: no imports from any host application; shipped
helpers (
sqlalchemy_libsql_native.testing) let downstream test harnesses import them without a dependency on this repo's test-only files. - No cloud credentials in this package.
- Driver is exact-pinned (
libsql==0.1.11); supported SQLAlchemy versions are>=2.0.51,<2.1. src/layout, hatchling build backend, Python>=3.12,<3.13.
Layout
pyproject.toml
src/sqlalchemy_libsql_native/
__init__.py
errors.py # PEP 249-shaped exception hierarchy
classify.py # raw builtins.ValueError -> facade exception (structured code first)
dbapi.py # ConnectionProxy / CursorProxy facade (connect args, raising attribute boundaries)
dialect.py # import_dbapi / create_connect_args / on_connect / is_disconnect / pool policy
testing.py # generic Tier 2/3 helpers: local sqld + fault injection + asserts
tests/
test_errors.py test_classify.py test_dbapi.py test_dialect.py # Tier 0 unit
test_file_integration.py # Tier 1 (real driver, file SQLite)
test_local_sqld_integration.py # Tier 2 (real driver, local Hrana)
Running the tests
Test tiers: Tier 0 — pure-Python unit tests (no driver). Tier 1 — real
driver against an embedded file SQLite DB (no network). Tier 2 — real driver
against a local libsql-server (Hrana) in Docker. Tier 3 — downstream
integration against a real managed deployment; those suites live outside this
package but reuse the shipped sqlalchemy_libsql_native.testing helpers.
Tier 0 + Tier 1 (self-contained, no server — always green)
uv sync --group dev
uv run pytest tests/ -q
Tier 0 (classify/errors) is pure Python. Tier 1 exercises the real
libsql==0.1.11 driver against an embedded file SQLite DB (no network). Tier 2
below skips with a reason when Docker is unavailable, so this command stays
green everywhere.
Tier 2 (local libsql-server / Hrana — needs Docker)
Reproduces the remote transport that file mode cannot: remote constraint
classification (a), transparent disconnect recovery (b), and the three
fault-injection points. Self-contained (a local server in Docker — no Turso Cloud,
no credentials).
# Requires: `docker` on PATH + a running Docker daemon.
uv run pytest tests/test_local_sqld_integration.py -v
The server image is pinned by tag + digest (a moved tag cannot silently change the image):
ghcr.io/tursodatabase/libsql-server:v0.24.32
@sha256:dedf5273da945d90f3b85fe367ed8f821349117ac2c488bf43c4a6bf9f2123e0
Auxiliary observations:
- Natural idle stream-expiry (~330–360 s: 10 s expiration + 300 s cleanup) is the
slow, non-deterministic counterpart to the deterministic
(b)injection. It is opt-in (default-skipped): run withLIBSQL_NATIVE_SLOW_EXPIRY=1. (c)concurrent writes is observation only — success is not asserted; the invariant is only that any surfaced error is facade-classified (never a rawValueError). On v0.24.32 the collapse surfaces asTRANSACTION_TIMEOUT(→DatabaseError) / occasionallySTREAM_EXPIRED(→OperationalError).
Commit-response disconnect injection
The "cut at commit response" fault (point="commit_response" in
sqlalchemy_libsql_native.testing) runs the real commit first — so the write is
durably applied server-side — and only then injects the disconnect, modelling a
lost acknowledgement. The Tier 2 test proves the resulting ambiguity concretely:
the caller receives a classified OperationalError, yet the row is present when
read back through a fresh connection, so "failure" does not mean "rolled back"
and a naive retry would double-apply. This is deterministic and self-contained (no
extra service). A network-level proxy (Toxiproxy) is documented in testing.py as
the higher-fidelity Tier 3 alternative; point="commit" is the complementary
request-side loss (fails before the commit reaches the server → not applied). In
all cases the connection is invalidated and the adapter does not retry — retry
policy is deliberately left to the caller.
Generic test helpers (sqlalchemy_libsql_native.testing)
The sqld launcher, fault injector, and classification asserts live in the
shipped package (not tests/) so downstream test harnesses can import them
from the installed distribution without a dependency on this repo's test-only
files. Key surface:
LibsqlServer, fault_injecting_engine, FaultController, docker_available,
assert_integrity_surfaced, assert_disconnect_surfaced.
Metadata
Release files for sqlalchemy-libsql-native 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sqlalchemy_libsql_native-0.1.0.tar.gz | 34.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sqlalchemy_libsql_native-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 63.3 kB
Release files / sqlalchemy_libsql_native-0.1.0.tar.gz
| Download URL | sqlalchemy_libsql_native-0.1.0.tar.gz |
|---|---|
| Size | 34.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f03eba4bf75b9c8c69d6c7e65af833c5b8165f9dc17a7dbd363eff4ede044ca1
|
|
BLAKE2b-256 checksum How to use checksums |
f18c7a9768d143149534b92372a935ebf513c7ccf2ecbb6046d204f02731137e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 16, 2026.
Transparency logRelease files / sqlalchemy_libsql_native-0.1.0-py3-none-any.whl
| Download URL | sqlalchemy_libsql_native-0.1.0-py3-none-any.whl |
|---|---|
| Size | 29.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b720113b64a22c8aba99c82d00210cb5643aa7a1994fdf5b4547f234f27b12ab
|
|
BLAKE2b-256 checksum How to use checksums |
fdfabb5ea6d42c3a9e50546f73044c6170ebf3df3144fdfa94bf5139fc134ba4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 16, 2026.
Transparency log