0
# pycrdt
1
2
## Overview
3
4
pycrdt is a Python library that provides bindings for Yrs, a high-performance Conflict-free Replicated Data Type (CRDT) library written in Rust. It enables developers to build collaborative applications with automatic conflict resolution, supporting various data structures like arrays, maps, text, and XML documents. The library is designed for real-time collaborative editing, distributed systems, and offline-first applications that require eventual consistency across multiple clients.
5
6
Key features:
7
- **Collaborative data structures**: Text, Array, Map, and XML with automatic conflict resolution
8
- **Real-time synchronization**: Built-in support for document synchronization across clients
9
- **Type safety**: TypedDoc, TypedArray, and TypedMap for runtime type checking
10
- **Event system**: Comprehensive observation and event handling for all data types
11
- **Position tracking**: StickyIndex for maintaining positions during concurrent edits
12
- **Undo/redo**: Full undo manager with origin filtering and scope control
13
- **Async support**: Full async/await support through anyio integration
14
15
## Package Information
16
17
- **Package Name**: pycrdt
18
- **Language**: Python
19
- **Package Manager**: pip
20
- **Installation**: `pip install pycrdt`
21
- **Minimum Python Version**: 3.9+
22
- **Build System**: maturin (Python/Rust hybrid)
23
24
## Core Imports
25
26
```python
27
import pycrdt
28
29
# Import main document and transaction types
30
from pycrdt import Doc, TypedDoc
31
from pycrdt import Transaction, NewTransaction, ReadTransaction
32
33
# Import collaborative data types
34
from pycrdt import Text, Array, Map
35
from pycrdt import TypedArray, TypedMap
36
from pycrdt import XmlFragment, XmlElement, XmlText
37
38
# Import event types
39
from pycrdt import TextEvent, ArrayEvent, MapEvent, XmlEvent
40
from pycrdt import TransactionEvent, SubdocsEvent
41
42
# Import synchronization components
43
from pycrdt import Provider, Channel, Awareness
44
from pycrdt import Decoder, Encoder
45
from pycrdt import YMessageType, YSyncMessageType
46
from pycrdt import create_sync_message, handle_sync_message
47
from pycrdt import create_awareness_message, create_update_message
48
from pycrdt import read_message, write_message, write_var_uint
49
50
# Import utility types
51
from pycrdt import StickyIndex, Assoc, UndoManager
52
from pycrdt import Subscription, StackItem
53
54
# Import update functions
55
from pycrdt import get_state, get_update, merge_updates
56
57
# Import awareness utility
58
from pycrdt import is_awareness_disconnect_message
59
60
# Get package version
61
import pycrdt
62
print(pycrdt.__version__) # Package version string
63
```
64
65
## Basic Usage
66
67
```python
68
import pycrdt
69
from pycrdt import Doc, Text, Array, Map
70
71
# Create a new collaborative document
72
doc = Doc()
73
74
# Create shared data structures
75
text = doc.get("shared_text", type=Text)
76
array = doc.get("shared_array", type=Array)
77
map_data = doc.get("shared_map", type=Map)
78
79
# Work with text
80
text.insert(0, "Hello, ")
81
text.insert(6, "world!")
82
print(str(text)) # "Hello, world!"
83
84
# Work with arrays
85
array.append("item1")
86
array.extend(["item2", "item3"])
87
print(len(array)) # 3
88
89
# Work with maps
90
map_data["key1"] = "value1"
91
map_data["key2"] = 42
92
print(map_data["key1"]) # "value1"
93
94
# Observe changes
95
def on_text_change(event):
96
print(f"Text changed: {event.delta}")
97
98
subscription = text.observe(on_text_change)
99
100
# Make changes in a transaction
101
with doc.transaction() as txn:
102
text += " How are you?"
103
array.append("item4")
104
map_data["timestamp"] = "2024-01-01"
105
```
106
107
## Architecture
108
109
pycrdt follows a layered architecture:
110
111
1. **Document Layer**: `Doc` serves as the container for all shared data types
112
2. **Transaction Layer**: All mutations happen within `Transaction` contexts for atomicity
113
3. **Data Type Layer**: `Text`, `Array`, `Map`, and XML types provide collaborative editing
114
4. **Event Layer**: Comprehensive event system for observing changes
115
5. **Synchronization Layer**: `Provider` and `Channel` handle network communication
116
6. **Awareness Layer**: Client presence and metadata management
117
118
## Capabilities
119
120
### Document Management
121
Comprehensive document lifecycle management with transactions and type safety.
122
123
```python { .api }
124
class Doc:
125
def __init__(self, init: dict = {}, *, client_id: int | None = None) -> None
126
def transaction(self, origin: Any = None) -> Transaction
127
def get(self, key: str, *, type: type[T]) -> T
128
def observe(self, callback: Callable[[TransactionEvent], None]) -> Subscription
129
```
130
131
**See [Document Management](./document-management.md) for complete API details.**
132
133
### Text Operations
134
Collaborative text editing with rich formatting and position tracking.
135
136
```python { .api }
137
class Text:
138
def insert(self, index: int, value: str, attrs: dict[str, Any] | None = None) -> None
139
def format(self, start: int, stop: int, attrs: dict[str, Any]) -> None
140
def diff(self) -> list[tuple[Any, dict[str, Any] | None]]
141
def observe(self, callback: Callable[[TextEvent], None]) -> Subscription
142
```
143
144
**See [Text Operations](./text-operations.md) for complete API details.**
145
146
### Array Operations
147
Collaborative arrays with list-like interface and change tracking.
148
149
```python { .api }
150
class Array[T]:
151
def append(self, value: T) -> None
152
def extend(self, value: list[T]) -> None
153
def insert(self, index: int, object: T) -> None
154
def move(self, source_index: int, destination_index: int) -> None
155
def observe(self, callback: Callable[[ArrayEvent], None]) -> Subscription
156
```
157
158
**See [Array Operations](./array-operations.md) for complete API details.**
159
160
### Map Operations
161
Collaborative dictionaries with key-value storage and event tracking.
162
163
```python { .api }
164
class Map[T]:
165
def get(self, key: str, default_value: T_DefaultValue = None) -> T | T_DefaultValue | None
166
def pop(self, key: str, default_value: T_DefaultValue = None) -> T | T_DefaultValue
167
def update(self, value: dict[str, T]) -> None
168
def observe(self, callback: Callable[[MapEvent], None]) -> Subscription
169
```
170
171
**See [Map Operations](./map-operations.md) for complete API details.**
172
173
### XML Support
174
Structured XML document editing with elements, attributes, and text nodes.
175
176
```python { .api }
177
class XmlElement:
178
def __init__(self, tag: str | None = None, attributes: dict[str, str] | None = None) -> None
179
@property
180
def tag(self) -> str | None
181
@property
182
def attributes(self) -> XmlAttributesView
183
@property
184
def children(self) -> XmlChildrenView
185
```
186
187
**See [XML Support](./xml-support.md) for complete API details.**
188
189
### Synchronization
190
Network synchronization with providers, channels, and message handling.
191
192
```python { .api }
193
class Provider:
194
def __init__(self, doc: Doc, channel: Channel, log: Logger | None = None) -> None
195
async def start(self) -> None
196
async def stop(self) -> None
197
198
def create_sync_message(ydoc: Doc) -> bytes
199
def handle_sync_message(message: bytes, ydoc: Doc) -> bytes | None
200
```
201
202
**See [Synchronization](./synchronization.md) for complete API details.**
203
204
### Awareness Protocol
205
Client presence management and metadata sharing in collaborative sessions.
206
207
```python { .api }
208
class Awareness:
209
def __init__(self, ydoc: Doc, *, outdated_timeout: int = 30000) -> None
210
def get_local_state(self) -> dict[str, Any] | None
211
def set_local_state(self, state: dict[str, Any] | None) -> None
212
def encode_awareness_update(self, client_ids: list[int]) -> bytes
213
```
214
215
**See [Awareness](./awareness.md) for complete API details.**
216
217
### Position Management & Undo
218
Persistent position tracking and comprehensive undo/redo operations.
219
220
```python { .api }
221
class StickyIndex:
222
def get_index(self, transaction: Transaction | None = None) -> int
223
@classmethod
224
def new(cls, sequence: Sequence, index: int, assoc: Assoc) -> Self
225
226
class UndoManager:
227
def can_undo(self) -> bool
228
def undo(self) -> bool
229
def can_redo(self) -> bool
230
def redo(self) -> bool
231
```
232
233
**See [Position Management & Undo](./position-undo.md) for complete API details.**
234
235
## Common Patterns
236
237
### Context Manager Pattern
238
All transactions use context managers for automatic cleanup:
239
240
```python
241
with doc.transaction() as txn:
242
# All mutations here are batched
243
text.insert(0, "New content")
244
array.append("new item")
245
```
246
247
### Observer Pattern
248
All shared types support event observation:
249
250
```python
251
def on_change(event):
252
print(f"Change detected: {event}")
253
254
subscription = shared_type.observe(on_change)
255
# Remember to clean up
256
shared_type.unobserve(subscription)
257
```
258
259
### Type Safety Pattern
260
Use typed variants for runtime type checking:
261
262
```python
263
class MyDoc(TypedDoc):
264
text_field: Text
265
array_field: Array[str]
266
map_field: Map[int]
267
268
doc = MyDoc()
269
doc.text_field.insert(0, "Hello") # Type-safe access
270
```
271
272
## Error Handling
273
274
pycrdt operations can raise the following exceptions:
275
276
- **ValueError**: Invalid parameters or operations
277
- **TypeError**: Type mismatches in typed variants
278
- **RuntimeError**: Document or transaction state errors
279
280
```python
281
try:
282
with doc.transaction() as txn:
283
text.insert(-1, "Invalid index") # May raise ValueError
284
except ValueError as e:
285
print(f"Operation failed: {e}")
286
```