A Python loader for doltlite — Dolt's version control through SQLite's drop-in API.
import doltlite # one-time bootstrap
import sqlite3
conn = sqlite3.connect("repo.db")
conn.execute("CREATE TABLE t(x INT)")
conn.execute("INSERT INTO t VALUES (1)")
conn.execute("SELECT dolt_commit('-A', '-m', 'init')")import doltlite makes libdoltlite the active SQLite engine for the
current interpreter, so anything that uses the standard sqlite3 module
— including SQLAlchemy with sqlite:///... URLs — transparently
gains dolt_commit, dolt_branch, dolt_merge, dolt_log,
dolt_diff_<table>, and the other Dolt SQL functions and virtual tables.
pip install doltliteWheels bundle a precompiled libdoltlite for macOS (arm64) and Linux
(x86_64, aarch64). No system-level setup required.
Intel Mac wheels aren't shipped in v0.11.x — GitHub's free-tier
macos-13 runners queue for hours and block releases. Intel Mac users
should build libdoltlite locally and use the DOLTLITE_LIB path below.
For development against a local checkout of doltlite, point
DOLTLITE_LIB at your built library instead:
DOLTLITE_LIB=/path/to/libdoltlite.dylib python3 your_script.pyThe package piggybacks on Python's stdlib sqlite3, which must load
SQLite as a shared extension at runtime. The following Pythons work:
- Distro / system Python (Linux)
- Homebrew Python (macOS, Linux)
- pyenv-built Python
- Conda Python
These do not work because their _sqlite3 is statically linked into
the interpreter:
- python-build-standalone interpreters — the default for
uv python install,mise, and Rye
If you use uv, target one of the supported Pythons explicitly:
uv venv --python /opt/homebrew/bin/python3 # or /usr/bin/python3Doltlite is a SQLite drop-in: it implements the same sqlite3_* C API
and adds Dolt-specific functions and virtual tables on top. To use it
from Python, libdoltlite has to be loaded ahead of the system libsqlite3
so its symbols win during the dynamic-link symbol-resolution pass that
Python's _sqlite3 module triggers when it's first imported.
The mechanics differ by platform:
ELF flat-namespace symbol resolution: if libdoltlite is loaded into the
process with RTLD_GLOBAL before _sqlite3 is loaded, its
sqlite3_* symbols enter the global namespace and the later
libsqlite3.so lookup finds them first.
import doltlite does ctypes.CDLL(libdoltlite_path, mode=ctypes.RTLD_GLOBAL) in this case — no re-exec required.
If sqlite3 was already imported before doltlite, that boat has
sailed: we fall back to re-execing the interpreter with LD_PRELOAD
set, so libdoltlite is loaded at process start.
macOS uses a two-level namespace: _sqlite3.so has an LC_LOAD_DYLIB
command bound to a specific libsqlite3.dylib path (e.g.
/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib). Plain dlopen /
ctypes.CDLL does not redirect that resolution, and
DYLD_INSERT_LIBRARIES alone doesn't either — the inserted library has
to have an install_name (LC_ID_DYLIB) that matches the path
_sqlite3.so was linked against.
import doltlite does:
- Detect that path via
otool -L $(python3 -c 'import _sqlite3; print(_sqlite3.__file__)'). - Copy libdoltlite to
$TMPDIR/.../libsqlite3.dylib(cached per (lib, mtime, install_name)). - Rewrite the shim's install_name with
install_name_tool -id <path>. - Re-exec the interpreter with
DYLD_INSERT_LIBRARIES=<shim>.
The two-level lookup then accepts the shim because the install_name matches.
This requires otool and install_name_tool — install Xcode Command
Line Tools (xcode-select --install) if missing.
When a re-exec is required (macOS, or Linux-after-sqlite3-loaded), the
bootstrap calls os.execvpe with sys.argv. That means the
invocation must name a script file we can replay:
- ✅
python3 my_script.py - ✅
python3 -m my_package - ❌
python3 -c "import doltlite; ..."— code string isn't in argv - ❌ Interactive REPL / Jupyter — there's no script to re-exec
In the unsupported cases the bootstrap raises DoltliteLoadError with
a clear workaround: set DYLD_INSERT_LIBRARIES (macOS) or LD_PRELOAD
(Linux) yourself before starting Python.
import doltlite
# Side-effect import does the bootstrap automatically. Subsequent
# imports are no-ops thanks to a process-env marker.
# If you want to bootstrap explicitly (e.g. inside a function):
doltlite.bootstrap()
# Find where the loaded libdoltlite came from:
doltlite.libdoltlite_path() # absolute path- doltlite — the underlying SQLite + Dolt engine
- doltlite-sqlalchemy-getting-started — end-to-end SQLAlchemy demo (commits, branches, schema change, merge)
Apache License 2.0. See LICENSE.
