Skip to content

Commit bd99898

Browse files
Use pycrdt instead of y-py (#194)
* Use pycrdt instead of y-py * Review * Update pycrdt and pycrdt-websocket * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 6d131d0 commit bd99898

File tree

8 files changed

+112
-126
lines changed

8 files changed

+112
-126
lines changed

jupyter_ydoc/ybasedoc.py

+17-19
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
# Distributed under the terms of the Modified BSD License.
33

44
from abc import ABC, abstractmethod
5-
from typing import Any, Callable, Optional
5+
from typing import Any, Callable, Dict, Optional
66

7-
import y_py as Y
7+
from pycrdt import Doc, Map
88

99

1010
class YBaseDoc(ABC):
@@ -15,19 +15,19 @@ class YBaseDoc(ABC):
1515
subscribe to changes in the document.
1616
"""
1717

18-
def __init__(self, ydoc: Optional[Y.YDoc] = None):
18+
def __init__(self, ydoc: Optional[Doc] = None):
1919
"""
2020
Constructs a YBaseDoc.
2121
22-
:param ydoc: The :class:`y_py.YDoc` that will hold the data of the document, if provided.
23-
:type ydoc: :class:`y_py.YDoc`, optional.
22+
:param ydoc: The :class:`pycrdt.Doc` that will hold the data of the document, if provided.
23+
:type ydoc: :class:`pycrdt.Doc`, optional.
2424
"""
2525
if ydoc is None:
26-
self._ydoc = Y.YDoc()
26+
self._ydoc = Doc()
2727
else:
2828
self._ydoc = ydoc
29-
self._ystate = self._ydoc.get_map("state")
30-
self._subscriptions = {}
29+
self._ydoc["state"] = self._ystate = Map()
30+
self._subscriptions: Dict[Any, str] = {}
3131

3232
@property
3333
@abstractmethod
@@ -40,22 +40,22 @@ def version(self) -> str:
4040
"""
4141

4242
@property
43-
def ystate(self) -> Y.YMap:
43+
def ystate(self) -> Map:
4444
"""
45-
A :class:`y_py.YMap` containing the state of the document.
45+
A :class:`pycrdt.Map` containing the state of the document.
4646
4747
:return: The document's state.
48-
:rtype: :class:`y_py.YMap`
48+
:rtype: :class:`pycrdt.Map`
4949
"""
5050
return self._ystate
5151

5252
@property
53-
def ydoc(self) -> Y.YDoc:
53+
def ydoc(self) -> Doc:
5454
"""
55-
The underlying :class:`y_py.YDoc` that contains the data.
55+
The underlying :class:`pycrdt.Doc` that contains the data.
5656
5757
:return: The document's ydoc.
58-
:rtype: :class:`y_py.YDoc`
58+
:rtype: :class:`pycrdt.Doc`
5959
"""
6060
return self._ydoc
6161

@@ -87,7 +87,7 @@ def dirty(self) -> Optional[bool]:
8787
:return: Whether the document is dirty.
8888
:rtype: Optional[bool]
8989
"""
90-
return self._ystate["dirty"]
90+
return self._ystate.get("dirty")
9191

9292
@dirty.setter
9393
def dirty(self, value: bool) -> None:
@@ -97,8 +97,7 @@ def dirty(self, value: bool) -> None:
9797
:param value: Whether the document is clean or dirty.
9898
:type value: bool
9999
"""
100-
with self._ydoc.begin_transaction() as t:
101-
self._ystate.set(t, "dirty", value)
100+
self._ystate["dirty"] = value
102101

103102
@property
104103
def path(self) -> Optional[str]:
@@ -118,8 +117,7 @@ def path(self, value: str) -> None:
118117
:param value: Document's path.
119118
:type value: str
120119
"""
121-
with self._ydoc.begin_transaction() as t:
122-
self._ystate.set(t, "path", value)
120+
self._ystate["path"] = value
123121

124122
@abstractmethod
125123
def get(self) -> Any:

jupyter_ydoc/yblob.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from functools import partial
66
from typing import Any, Callable, Optional, Union
77

8-
import y_py as Y
8+
from pycrdt import Doc, Map
99

1010
from .ybasedoc import YBaseDoc
1111

@@ -28,15 +28,15 @@ class YBlob(YBaseDoc):
2828
}
2929
"""
3030

31-
def __init__(self, ydoc: Optional[Y.YDoc] = None):
31+
def __init__(self, ydoc: Optional[Doc] = None):
3232
"""
3333
Constructs a YBlob.
3434
35-
:param ydoc: The :class:`y_py.YDoc` that will hold the data of the document, if provided.
36-
:type ydoc: :class:`y_py.YDoc`, optional.
35+
:param ydoc: The :class:`pycrdt.Doc` that will hold the data of the document, if provided.
36+
:type ydoc: :class:`pycrdt.Doc`, optional.
3737
"""
3838
super().__init__(ydoc)
39-
self._ysource = self._ydoc.get_map("source")
39+
self._ydoc["source"] = self._ysource = Map()
4040

4141
@property
4242
def version(self) -> str:
@@ -66,8 +66,7 @@ def set(self, value: Union[bytes, str]) -> None:
6666
"""
6767
if isinstance(value, bytes):
6868
value = base64.b64encode(value).decode()
69-
with self._ydoc.begin_transaction() as t:
70-
self._ysource.set(t, "base64", value)
69+
self._ysource["base64"] = value
7170

7271
def observe(self, callback: Callable[[str, Any], None]) -> None:
7372
"""

jupyter_ydoc/ynotebook.py

+33-60
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from typing import Any, Callable, Dict, Optional
88
from uuid import uuid4
99

10-
import y_py as Y
10+
from pycrdt import Array, Doc, Map, Text
1111

1212
from .utils import cast_all
1313
from .ybasedoc import YBaseDoc
@@ -47,16 +47,16 @@ class YNotebook(YBaseDoc):
4747
}
4848
"""
4949

50-
def __init__(self, ydoc: Optional[Y.YDoc] = None):
50+
def __init__(self, ydoc: Optional[Doc] = None):
5151
"""
5252
Constructs a YNotebook.
5353
54-
:param ydoc: The :class:`y_py.YDoc` that will hold the data of the document, if provided.
55-
:type ydoc: :class:`y_py.YDoc`, optional.
54+
:param ydoc: The :class:`pycrdt.Doc` that will hold the data of the document, if provided.
55+
:type ydoc: :class:`pycrdt.Doc`, optional.
5656
"""
5757
super().__init__(ydoc)
58-
self._ymeta = self._ydoc.get_map("meta")
59-
self._ycells = self._ydoc.get_array("cells")
58+
self._ydoc["meta"] = self._ymeta = Map()
59+
self._ydoc["cells"] = self._ycells = Array()
6060

6161
@property
6262
def version(self) -> str:
@@ -74,7 +74,7 @@ def ycells(self):
7474
Returns the Y-cells.
7575
7676
:return: The Y-cells.
77-
:rtype: :class:`y_py.YArray`
77+
:rtype: :class:`pycrdt.Array`
7878
"""
7979
return self._ycells
8080

@@ -98,8 +98,8 @@ def get_cell(self, index: int) -> Dict[str, Any]:
9898
:return: A cell.
9999
:rtype: Dict[str, Any]
100100
"""
101-
meta = json.loads(self._ymeta.to_json())
102-
cell = json.loads(self._ycells[index].to_json())
101+
meta = json.loads(str(self._ymeta))
102+
cell = json.loads(str(self._ycells[index]))
103103
cast_all(cell, float, int) # cells coming from Yjs have e.g. execution_count as float
104104
if "id" in cell and meta["nbformat"] == 4 and meta["nbformat_minor"] <= 4:
105105
# strip cell IDs if we have notebook format 4.0-4.4
@@ -112,26 +112,17 @@ def get_cell(self, index: int) -> Dict[str, Any]:
112112
del cell["attachments"]
113113
return cell
114114

115-
def append_cell(self, value: Dict[str, Any], txn: Optional[Y.YTransaction] = None) -> None:
115+
def append_cell(self, value: Dict[str, Any]) -> None:
116116
"""
117117
Appends a cell.
118118
119119
:param value: A cell.
120120
:type value: Dict[str, Any]
121-
122-
:param txn: A YTransaction, defaults to None
123-
:type txn: :class:`y_py.YTransaction`, optional.
124121
"""
125122
ycell = self.create_ycell(value)
126-
if txn is None:
127-
with self._ydoc.begin_transaction() as txn:
128-
self._ycells.append(txn, ycell)
129-
else:
130-
self._ycells.append(txn, ycell)
131-
132-
def set_cell(
133-
self, index: int, value: Dict[str, Any], txn: Optional[Y.YTransaction] = None
134-
) -> None:
123+
self._ycells.append(ycell)
124+
125+
def set_cell(self, index: int, value: Dict[str, Any]) -> None:
135126
"""
136127
Sets a cell into indicated position.
137128
@@ -140,60 +131,48 @@ def set_cell(
140131
141132
:param value: A cell.
142133
:type value: Dict[str, Any]
143-
144-
:param txn: A YTransaction, defaults to None
145-
:type txn: :class:`y_py.YTransaction`, optional.
146134
"""
147135
ycell = self.create_ycell(value)
148-
self.set_ycell(index, ycell, txn)
136+
self.set_ycell(index, ycell)
149137

150-
def create_ycell(self, value: Dict[str, Any]) -> Y.YMap:
138+
def create_ycell(self, value: Dict[str, Any]) -> Map:
151139
"""
152140
Creates YMap with the content of the cell.
153141
154142
:param value: A cell.
155143
:type value: Dict[str, Any]
156144
157145
:return: A new cell.
158-
:rtype: :class:`y_py.YMap`
146+
:rtype: :class:`pycrdt.Map`
159147
"""
160148
cell = copy.deepcopy(value)
161149
if "id" not in cell:
162150
cell["id"] = str(uuid4())
163151
cell_type = cell["cell_type"]
164152
cell_source = cell["source"]
165153
cell_source = "".join(cell_source) if isinstance(cell_source, list) else cell_source
166-
cell["source"] = Y.YText(cell_source)
167-
cell["metadata"] = Y.YMap(cell.get("metadata", {}))
154+
cell["source"] = Text(cell_source)
155+
cell["metadata"] = Map(cell.get("metadata", {}))
168156

169157
if cell_type in ("raw", "markdown"):
170158
if "attachments" in cell and not cell["attachments"]:
171159
del cell["attachments"]
172160
elif cell_type == "code":
173-
cell["outputs"] = Y.YArray(cell.get("outputs", []))
161+
cell["outputs"] = Array(cell.get("outputs", []))
174162

175-
return Y.YMap(cell)
163+
return Map(cell)
176164

177-
def set_ycell(self, index: int, ycell: Y.YMap, txn: Optional[Y.YTransaction] = None) -> None:
165+
def set_ycell(self, index: int, ycell: Map) -> None:
178166
"""
179167
Sets a Y cell into the indicated position.
180168
181169
:param index: The index of the cell.
182170
:type index: int
183171
184172
:param ycell: A YMap with the content of a cell.
185-
:type ycell: :class:`y_py.YMap`
186-
187-
:param txn: A YTransaction, defaults to None
188-
:type txn: :class:`y_py.YTransaction`, optional.
173+
:type ycell: :class:`pycrdt.Map`
189174
"""
190-
if txn is None:
191-
with self._ydoc.begin_transaction() as txn:
192-
self._ycells.delete(txn, index)
193-
self._ycells.insert(txn, index, ycell)
194-
else:
195-
self._ycells.delete(txn, index)
196-
self._ycells.insert(txn, index, ycell)
175+
self._ycells[index] = ycell
197176

198177
def get(self) -> Dict:
199178
"""
@@ -202,7 +181,7 @@ def get(self) -> Dict:
202181
:return: Document's content.
203182
:rtype: Dict
204183
"""
205-
meta = json.loads(self._ymeta.to_json())
184+
meta = json.loads(str(self._ymeta))
206185
cast_all(meta, float, int) # notebook coming from Yjs has e.g. nbformat as float
207186
cells = []
208187
for i in range(len(self._ycells)):
@@ -247,29 +226,23 @@ def set(self, value: Dict) -> None:
247226
}
248227
]
249228

250-
with self._ydoc.begin_transaction() as t:
229+
with self._ydoc.transaction():
251230
# clear document
252-
cells_len = len(self._ycells)
253-
for key in self._ymeta:
254-
self._ymeta.pop(t, key)
255-
if cells_len:
256-
self._ycells.delete_range(t, 0, cells_len)
257-
for key in [k for k in self._ystate if k not in ("dirty", "path")]:
258-
self._ystate.pop(t, key)
231+
self._ymeta.clear()
232+
self._ycells.clear()
233+
for key in [k for k in self._ystate.keys() if k not in ("dirty", "path")]:
234+
del self._ystate[key]
259235

260236
# initialize document
261-
# workaround for https://github.com/y-crdt/ypy/issues/126:
262-
# self._ycells.extend(t, [self.create_ycell(cell) for cell in cells])
263-
for cell in cells:
264-
self._ycells.append(t, self.create_ycell(cell))
265-
self._ymeta.set(t, "nbformat", nb.get("nbformat", NBFORMAT_MAJOR_VERSION))
266-
self._ymeta.set(t, "nbformat_minor", nb.get("nbformat_minor", NBFORMAT_MINOR_VERSION))
237+
self._ycells.extend([self.create_ycell(cell) for cell in cells])
238+
self._ymeta["nbformat"] = nb.get("nbformat", NBFORMAT_MAJOR_VERSION)
239+
self._ymeta["nbformat_minor"] = nb.get("nbformat_minor", NBFORMAT_MINOR_VERSION)
267240

268241
metadata = nb.get("metadata", {})
269242
metadata.setdefault("language_info", {"name": ""})
270243
metadata.setdefault("kernelspec", {"name": "", "display_name": ""})
271244

272-
self._ymeta.set(t, "metadata", Y.YMap(metadata))
245+
self._ymeta["metadata"] = Map(metadata)
273246

274247
def observe(self, callback: Callable[[str, Any], None]) -> None:
275248
"""

jupyter_ydoc/yunicode.py

+8-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from functools import partial
55
from typing import Any, Callable, Optional
66

7-
import y_py as Y
7+
from pycrdt import Doc, Text
88

99
from .ybasedoc import YBaseDoc
1010

@@ -23,15 +23,15 @@ class YUnicode(YBaseDoc):
2323
}
2424
"""
2525

26-
def __init__(self, ydoc: Optional[Y.YDoc] = None):
26+
def __init__(self, ydoc: Optional[Doc] = None):
2727
"""
2828
Constructs a YUnicode.
2929
30-
:param ydoc: The :class:`y_py.YDoc` that will hold the data of the document, if provided.
31-
:type ydoc: :class:`y_py.YDoc`, optional.
30+
:param ydoc: The :class:`pycrdt.Doc` that will hold the data of the document, if provided.
31+
:type ydoc: :class:`pycrdt.Doc`, optional.
3232
"""
3333
super().__init__(ydoc)
34-
self._ysource = self._ydoc.get_text("source")
34+
self._ydoc["source"] = self._ysource = Text()
3535

3636
@property
3737
def version(self) -> str:
@@ -59,14 +59,12 @@ def set(self, value: str) -> None:
5959
:param value: The content of the document.
6060
:type value: str
6161
"""
62-
with self._ydoc.begin_transaction() as t:
62+
with self._ydoc.transaction():
6363
# clear document
64-
source_len = len(self._ysource)
65-
if source_len:
66-
self._ysource.delete_range(t, 0, source_len)
64+
self._ysource.clear()
6765
# initialize document
6866
if value:
69-
self._ysource.extend(t, value)
67+
self._ysource += value
7068

7169
def observe(self, callback: Callable[[str, Any], None]) -> None:
7270
"""

0 commit comments

Comments
 (0)