This release is a pre-release and may not be stable for production use.
Ditto Python SDK
An asyncio-native Python binding for Ditto — a cross-platform, peer-to-peer database that syncs data with and without internet connectivity. Install it, read and write with DQL, and Ditto automatically syncs changes to other devices over Bluetooth LE, P2P Wi-Fi, LAN, and the cloud.
The distribution is named dittolive-ditto; you import it as ditto.
Public Preview. This is an early preview release. The API may change before a stable release, and it is published as a pre-release — see Installation.
Installation
python3 -m pip install --pre dittolive-ditto
--pre is required while the SDK is in preview (the published versions are
pre-releases such as 5.2.0.dev0). Requires Python 3.10+.
Published wheels are platform-specific and bundle the matching native library
(libdittoffi) — there is nothing else to install or build.
Preview wheels are currently published for macOS on Apple Silicon and Linux on
x86_64 and arm64 (manylinux_2_35 — glibc 2.35+); more platforms are on the way.
Getting Started
Ditto.open(...) is both awaitable and an async context manager. The context
manager form closes the peer for you:
import asyncio
from ditto import Ditto, DittoConfig, DittoConfigConnect
async def main() -> None:
config = DittoConfig(
database_id="your-database-id",
connect=DittoConfigConnect.small_peers_only(),
persistence_directory="./ditto-data",
)
async with Ditto.open(config) as peer:
await peer.store.execute(
"INSERT INTO cars DOCUMENTS (:car)",
{"car": {"_id": "car1", "make": "Tesla", "color": "red"}},
)
with await peer.store.execute("SELECT * FROM cars") as result:
for item in result:
print(item.value)
asyncio.run(main())
The equivalent explicit form (close() is async because shutdown may wait for
native work):
peer = await Ditto.open(config)
try:
...
finally:
await peer.close()
DittoConfigConnect.small_peers_only() runs fully offline / local + peer-to-peer.
To sync through a Ditto server (Big Peer), use DittoConfigConnect.server(...) —
see Sync and Connecting to a Ditto Server.
Store and Queries (DQL)
All reads and writes go through DQL, executed on
peer.store. execute() is async and returns a QueryResult you iterate for
document values. The snippets below run inside the async with Ditto.open(config) as peer: block from Getting Started; each result owns native
resources, so wrap it in with await …: (as the read below does) when you keep it:
# Create / upsert
await peer.store.execute(
"INSERT INTO cars DOCUMENTS (:car) ON ID CONFLICT DO UPDATE",
{"car": {"_id": "car1", "make": "Tesla", "color": "red"}},
)
# Update
await peer.store.execute(
"UPDATE cars SET color = :color WHERE _id = :id",
{"color": "blue", "id": "car1"},
)
# Read
with await peer.store.execute("SELECT * FROM cars WHERE color = :color",
{"color": "blue"}) as result:
cars = [item.value for item in result]
# Delete: writes a tombstone that propagates removal to peers
# (use EVICT instead to drop a document only from the local store)
await peer.store.execute("DELETE FROM cars WHERE _id = :id", {"id": "car1"})
Reactive Observers
Register an observer to be called with the current result set whenever documents matching a query change locally or arrive from a peer:
def on_change(result):
for item in result:
print("cars changed:", item.value)
observer = peer.store.register_observer("SELECT * FROM cars", on_change)
# ... later, to stop receiving updates (close() alone does not stop delivery):
observer.cancel()
The handler may be a sync or async function, and is delivered on the event loop that registered it.
Sync
Sync is off until you start it. peer.sync.start() brings up the transports;
register_subscription(...) tells Ditto which documents to sync from other peers
(the argument must be a SELECT query):
peer.sync.start()
subscription = peer.sync.register_subscription("SELECT * FROM cars")
# ... later:
subscription.cancel()
Syncing with other peers or a Ditto server requires a license. For offline / small-peers-only use you can set an offline token obtained from the Ditto Portal:
peer.set_offline_only_license_token("your-offline-license-token")
Connecting to a Ditto Server
Connecting through a server (DittoConfigConnect.server(...)) requires setting an
authentication expiration handler before starting sync — otherwise sync.start()
raises DittoExpirationHandlerMissingError:
from datetime import timedelta
from ditto import Ditto, DittoConfig, DittoConfigConnect
config = DittoConfig(
database_id="your-database-id",
connect=DittoConfigConnect.server("https://your-app.cloud.ditto.live"),
)
async with Ditto.open(config) as peer:
def on_expiring(ditto: Ditto, remaining: timedelta) -> None:
# Obtain a fresh token and call `ditto.auth.login(...)`.
...
peer.auth.expiration_handler = on_expiring
peer.sync.start()
Also Available
The SDK surfaces the rest of Ditto's v5 API through the same peer. All of these
are importable from the top-level ditto package:
- Presence — observe the live mesh with
peer.presence.register_observer(handler), wherehandler(graph: PresenceGraph)is called on each change. - Transactions — group reads/writes with
peer.store.transaction(...). - Attachments — store and fetch large binaries via
peer.store.new_attachment(...)andpeer.store.fetch_attachment(...). - Transports — configure Bluetooth LE, LAN, AWDL, Wi-Fi Aware, and WebSocket via
DittoConfig/ thetransport_configtypes (BluetoothLEConfig,DittoLanConfig,WifiAwareConfig, …). - Disk usage, logging, and typed errors —
DiskUsageObserver,DittoLogger/LogLevel, and theDitto*Errorhierarchy.
See the Ditto documentation for concepts (mesh networking, data handling, sync) and the DQL reference.
Native Library Discovery
Wheel installs need no configuration. If you are working from a source checkout
instead, point the loader at a compatible libdittoffi with DITTOFFI_LIB_PATH (the
file or a directory containing it) or DITTOFFI_SEARCH_PATH (platform-separated
directories). Importing ditto never loads the native library; it loads on first
use of a native API.
Concurrency and Resource Ownership
- Native callbacks are delivered on the asyncio event loop that registered them; handlers may be sync or async.
- Result and observer objects own native resources — close them promptly or use
their context-manager support.
Ditto.close()is async. - Ditto's native runtime and logger hold process-global state, so tests must run
sequentially (
pytest-xdistis intentionally unsupported). - A Ditto handle must not cross a process fork. Use
multiprocessing.get_context("spawn")and open a freshDittoin each worker.
Resources
- 📖 Documentation
- 🔎 DQL Reference
- 🧭 Ditto Portal
- 📦 PyPI
License
Ditto is commercial software. See ditto.com for licensing.
Release files for dittolive-ditto 5.2.0.dev0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dittolive_ditto-5.2.0.dev0-py3-none-manylinux_2_35_x86_64.whl | Python 3 | none | Linux glibc 2.35+ x86-64 | Details |
| dittolive_ditto-5.2.0.dev0-py3-none-manylinux_2_35_aarch64.whl | Python 3 | none | Linux glibc 2.35+ ARM64 | Details |
| dittolive_ditto-5.2.0.dev0-py3-none-macosx_12_0_arm64.whl | Python 3 | none | macOS 12.0+ ARM64 | Details |
Total release size:59.2 MB
Release files / dittolive_ditto-5.2.0.dev0-py3-none-manylinux_2_35_x86_64.whl
| Download URL | dittolive_ditto-5.2.0.dev0-py3-none-manylinux_2_35_x86_64.whl |
|---|---|
| Size | 21.0 MB |
| Tags | Linux glibc 2.35+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
c61cd25a2a885fed4e39f46886ff7d8ee9260faaadec32a6cc99afd53a208c89
|
|
BLAKE2b-256 checksum How to use checksums |
b39214f43d858947a5cdae2e74703e324d2b35123bd799c38fdbdc1130fb32df
|
| 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 14, 2026.
Transparency logRelease files / dittolive_ditto-5.2.0.dev0-py3-none-manylinux_2_35_aarch64.whl
| Download URL | dittolive_ditto-5.2.0.dev0-py3-none-manylinux_2_35_aarch64.whl |
|---|---|
| Size | 20.8 MB |
| Tags | Linux glibc 2.35+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
ad0c881ca44394d2d695e6d8dc67b9581a9a7998841da669164d8512b5bf22fb
|
|
BLAKE2b-256 checksum How to use checksums |
14337774aed5a981a654a1d13dcef2ee35118ad05dec4bb07451deec16c9d06d
|
| 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 14, 2026.
Transparency logRelease files / dittolive_ditto-5.2.0.dev0-py3-none-macosx_12_0_arm64.whl
| Download URL | dittolive_ditto-5.2.0.dev0-py3-none-macosx_12_0_arm64.whl |
|---|---|
| Size | 17.5 MB |
| Tags | Python 3 macOS 12.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
f783104fadf0034b7fe6bed999386491423ea0f33e1d2674e936b903ae604c56
|
|
BLAKE2b-256 checksum How to use checksums |
a0b3270a159554fdc99ecf2509a926386cd23d7e760375718709ca4637f57597
|
| 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 14, 2026.
Transparency log