PyCRDT
Pycrdt is a Python library providing bindings for Yrs, the Rust port of the Yjs framework. It enables the creation of Conflict-free Replicated Data Types (CRDTs), allowing shared documents to automatically merge concurrent changes from different replicas, ensuring eventual consistency. The library is actively maintained, with frequent releases, and is currently at version 0.12.50.
Warnings
- breaking Python 3.9 support was dropped in version 0.12.37.
- breaking PyPy 3.10 support was dropped in version 0.12.41.
- gotcha Prior to version 0.12.50, there could be issues with concurrent async transactions that might lead to unexpected behavior.
- gotcha Before version 0.12.48, tuples assigned to `Map` entries were incorrectly handled and converted to 'undefined' in the underlying Yrs structure.
- gotcha Prior to version 0.12.45, exceptions raised within observer callbacks (e.g., `observe`, `observe_deep`) were not grouped. A single exception could halt further callback execution.
Install
-
pip install pycrdt
Imports
- Doc
from pycrdt import Doc
- Text
from pycrdt import Text
- Array
from pycrdt import Array
- Map
from pycrdt import Map
Quickstart
from pycrdt import Doc, Text, Array, Map
# Create a shared document
doc0 = Doc()
# Get or create root types for collaborative data
text0 = doc0.get("text", type=Text)
array0 = doc0.get("array", type=Array)
map0 = doc0.get("map", type=Map)
# Make changes to the document within a transaction
with doc0.transaction():
text0.insert(0, "hello")
array0.append()
map0["key1"] = "value1"
# Get the updates from doc0
updates = doc0.get_update()
# Simulate another client or replica
doc1 = Doc()
# Apply updates from doc0 to doc1
doc1.apply_update(updates)
# Verify the state of doc1
text1 = doc1.get("text", type=Text)
array1 = doc1.get("array", type=Array)
map1 = doc1.get("map", type=Map)
print(f"Doc1 Text: {text1}")
print(f"Doc1 Array: {array1}")
print(f"Doc1 Map: {map1}")
# Make further changes on doc1 concurrently
with doc1.transaction():
text1.insert(5, " world")
array1.append()
map1["key2"] = "value2"
# Get updates from doc1, relative to doc0's initial state for merging
updates_from_doc1 = doc1.get_update(doc0.get_state())
# Apply updates from doc1 back to doc0
doc0.apply_update(updates_from_doc1)
# Verify doc0 has the merged state
print(f"Doc0 Text (merged): {doc0.get('text', type=Text)}")
print(f"Doc0 Array (merged): {doc0.get('array', type=Array)}")
print(f"Doc0 Map (merged): {doc0.get('map', type=Map)}")