Skip to content

Commit 962f123

Browse files
authored
Jupyter Collaboration v2 (#239)
1 parent eab781e commit 962f123

File tree

12 files changed

+82
-97
lines changed

12 files changed

+82
-97
lines changed

packages/base/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
},
3737
"dependencies": {
3838
"@deathbeds/jupyterlab-rjsf": "^1.1.0",
39-
"@jupyter/docprovider": "^1.0.0",
40-
"@jupyter/ydoc": "^0.3.4 || ^1.0.2",
39+
"@jupyter/docprovider": "^2.0.0",
40+
"@jupyter/ydoc": "^1.0.0",
4141
"@jupytercad/occ-worker": "^1.0.1",
4242
"@jupytercad/schema": "^1.0.1",
4343
"@jupyterlab/application": "^4.0.0",

packages/schema/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
},
3939
"dependencies": {
4040
"@apidevtools/json-schema-ref-parser": "^9.0.9",
41-
"@jupyter/ydoc": "^0.3.4 || ^1.0.2",
41+
"@jupyter/ydoc": "^1.0.0",
4242
"@jupyterlab/apputils": "^4.0.0",
4343
"@jupyterlab/coreutils": "^6.0.0",
4444
"@jupyterlab/docregistry": "^4.0.0",

python/jupytercad_app/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
"dependencies": {
5252
"@codemirror/state": "^6.2.0",
5353
"@codemirror/view": "^6.9.3",
54-
"@jupyter/collaboration": "^1.0.0",
55-
"@jupyter/docprovider": "^1.0.0",
54+
"@jupyter/collaboration": "^2.0.0",
55+
"@jupyter/docprovider": "^2.0.0",
5656
"@jupyter/ydoc": "^0.3.4 || ^1.0.2",
5757
"@jupytercad/base": "^1.0.1",
5858
"@jupytercad/schema": "^1.0.1",

python/jupytercad_core/jupytercad_core/jcad_ydoc.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22
from typing import Any, Callable
33
from functools import partial
44

5-
import y_py as Y
5+
from pycrdt import Array, Map, Text
66
from jupyter_ydoc.ybasedoc import YBaseDoc
77

88

99
class YJCad(YBaseDoc):
1010
def __init__(self, *args, **kwargs):
1111
super().__init__(*args, **kwargs)
12-
self._ysource = self._ydoc.get_text("source")
13-
self._yobjects = self._ydoc.get_array("objects")
14-
self._yoptions = self._ydoc.get_map("options")
15-
self._ymeta = self._ydoc.get_map("metadata")
16-
self._youtputs = self._ydoc.get_map("outputs")
12+
self._ydoc["source"] = self._ysource = Text()
13+
self._ydoc["objects"] = self._yobjects = Array()
14+
self._ydoc["options"] = self._yoptions = Map()
15+
self._ydoc["metadata"] = self._ymetadata = Map()
16+
self._ydoc["outputs"] = self._youtputs = Map()
1717

1818
def version(self) -> str:
1919
return "0.1.0"
@@ -24,10 +24,10 @@ def get(self) -> str:
2424
:return: Document's content.
2525
:rtype: Any
2626
"""
27-
objects = json.loads(self._yobjects.to_json())
28-
options = json.loads(self._yoptions.to_json())
29-
meta = json.loads(self._ymeta.to_json())
30-
outputs = json.loads(self._youtputs.to_json())
27+
objects = self._yobjects.to_py()
28+
options = self._yoptions.to_py()
29+
meta = self._ymetadata.to_py()
30+
outputs = self._youtputs.to_py()
3131
return json.dumps(
3232
dict(objects=objects, options=options, metadata=meta, outputs=outputs),
3333
indent=2,
@@ -42,18 +42,14 @@ def set(self, value: str) -> None:
4242
valueDict = json.loads(value)
4343
newObj = []
4444
for obj in valueDict["objects"]:
45-
newObj.append(Y.YMap(obj))
46-
with self._ydoc.begin_transaction() as t:
47-
length = len(self._yobjects)
48-
self._yobjects.delete_range(t, 0, length)
49-
# workaround for https://github.com/y-crdt/ypy/issues/126:
50-
# self._yobjects.extend(t, newObj)
51-
for o in newObj:
52-
self._yobjects.append(t, o)
45+
newObj.append(Map(obj))
5346

54-
self._replace_y_map(t, self._yoptions, valueDict["options"])
55-
self._replace_y_map(t, self._ymeta, valueDict["metadata"])
56-
self._replace_y_map(t, self._youtputs, valueDict.get("outputs", {}))
47+
self._yobjects.clear()
48+
self._yobjects.extend(newObj)
49+
50+
self._yoptions.update(valueDict["options"])
51+
self._ymetadata.update(valueDict["metadata"])
52+
self._youtputs.update(valueDict["outputs"])
5753

5854
def observe(self, callback: Callable[[str, Any], None]):
5955
self.unobserve()
@@ -69,11 +65,6 @@ def observe(self, callback: Callable[[str, Any], None]):
6965
self._subscriptions[self._yoptions] = self._yoptions.observe_deep(
7066
partial(callback, "options")
7167
)
72-
self._subscriptions[self._ymeta] = self._ymeta.observe_deep(
68+
self._subscriptions[self._ymetadata] = self._ymetadata.observe_deep(
7369
partial(callback, "meta")
7470
)
75-
76-
def _replace_y_map(self, t: Y.YTransaction, y_map: Y.YMap, new_value: dict):
77-
for key in y_map:
78-
y_map.pop(t, key)
79-
y_map.update(t, new_value.items())

python/jupytercad_core/jupytercad_core/step_ydoc.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
from typing import Any, Callable
33
from functools import partial
44

5+
from pycrdt import Text
56
from jupyter_ydoc.ybasedoc import YBaseDoc
67

78

89
class YSTEP(YBaseDoc):
910
def __init__(self, *args, **kwargs):
1011
super().__init__(*args, **kwargs)
11-
self._ysource = self._ydoc.get_text("source")
12+
self._ydoc["source"] = self._ysource = Text()
1213

1314
def version(self) -> str:
1415
return "0.1.0"
@@ -19,16 +20,15 @@ def get(self) -> str:
1920
:return: Document's content.
2021
:rtype: Any
2122
"""
22-
return json.dumps(self._ysource.to_json())
23+
return json.dumps(self._ysource.to_py())
2324

2425
def set(self, value: str) -> None:
2526
"""
2627
Sets the content of the document.
2728
:param value: The content of the document.
2829
:type value: Any
2930
"""
30-
with self._ydoc.begin_transaction() as t:
31-
self._ysource.extend(t, value)
31+
self._ysource[:] = value
3232

3333
def observe(self, callback: Callable[[str, Any], None]):
3434
self.unobserve()

python/jupytercad_core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"build:worker:prod": "webpack --config worker.webpack.config.js --mode=production"
5454
},
5555
"dependencies": {
56-
"@jupyter/docprovider": "^1.0.0",
56+
"@jupyter/docprovider": "^2.0.0",
5757
"@jupytercad/base": "^1.0.1",
5858
"@jupytercad/occ-worker": "^1.0.1",
5959
"@jupytercad/schema": "^1.0.1",

python/jupytercad_core/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ classifiers = [
2020
]
2121
dependencies = [
2222
"jupyter_server>=2.0.6,<3",
23-
"jupyter_ydoc>=1.1.0,<2",
23+
"jupyter_ydoc>=2,<3",
2424
"y-py>=0.6.0,<0.7",
25-
"jupyter-collaboration>=1.2.0,<2",
25+
"jupyter-collaboration>=2,<3",
2626
]
2727
dynamic = ["version", "description", "authors", "urls", "keywords"]
2828
license = {file = "LICENSE"}

python/jupytercad_lab/jupytercad_lab/notebook/cad_document.py

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
from pathlib import Path
88
from typing import Any, Dict, List, Optional, Union
99

10-
import y_py as Y
10+
from pycrdt import Array, Doc, Map
1111
from pydantic import BaseModel
12-
from ypywidgets.ypywidgets import Widget
12+
from ypywidgets.comm import CommWidget
1313

1414
from .objects._schema.any import IAny
1515
from uuid import uuid4
@@ -32,7 +32,7 @@
3232
logger = logging.getLogger(__file__)
3333

3434

35-
class CadDocument(Widget):
35+
class CadDocument(CommWidget):
3636
"""
3737
Create a new CadDocument object.
3838
@@ -43,19 +43,17 @@ class CadDocument(Widget):
4343
def __init__(self, path: Optional[str] = None):
4444
comm_metadata = CadDocument._path_to_comm(path)
4545

46-
ydoc = Y.YDoc()
46+
ydoc = Doc()
4747

4848
super().__init__(
4949
comm_metadata=dict(ymodel_name="@jupytercad:widget", **comm_metadata),
5050
ydoc=ydoc,
5151
)
5252

53-
self.ydoc = ydoc
54-
55-
self._objects_array = self.ydoc.get_array("objects")
56-
self._metadata = self.ydoc.get_map("metadata")
57-
self._outputs = self.ydoc.get_map("outputs")
58-
self._options = self.ydoc.get_map("options")
53+
self.ydoc["objects"] = self._objects_array = Array()
54+
self.ydoc["metadata"] = self._metadata = Map()
55+
self.ydoc["outputs"] = self._outputs = Map()
56+
self.ydoc["options"] = self._options = Map()
5957

6058
@property
6159
def objects(self) -> List[str]:
@@ -88,28 +86,27 @@ def _path_to_comm(cls, filePath: Optional[str]) -> Dict:
8886
else:
8987
raise ValueError("File extension is not supported!")
9088
return dict(
91-
path=path, format=format, contentType=contentType, create_ydoc=path is None
89+
path=path, format=format, contentType=contentType, createydoc=path is None
9290
)
9391

9492
def get_object(self, name: str) -> Optional["PythonJcadObject"]:
9593
if self.check_exist(name):
96-
data = json.loads(self._get_yobject_by_name(name).to_json())
94+
data = json.loads(self._get_yobject_by_name(name).to_py())
9795
return OBJECT_FACTORY.create_object(data, self)
9896

9997
def remove(self, name: str) -> CadDocument:
10098
index = self._get_yobject_index_by_name(name)
10199
if self._objects_array and index != -1:
102-
with self.ydoc.begin_transaction() as t:
100+
with self.ydoc.transaction() as t:
103101
self._objects_array.delete(t, index)
104102
return self
105103

106104
def add_object(self, new_object: "PythonJcadObject") -> CadDocument:
107105
if self._objects_array is not None and not self.check_exist(new_object.name):
108106
obj_dict = json.loads(new_object.json())
109107
obj_dict["visible"] = True
110-
new_map = Y.YMap(obj_dict)
111-
with self.ydoc.begin_transaction() as t:
112-
self._objects_array.append(t, new_map)
108+
new_map = Map(obj_dict)
109+
self._objects_array.append(new_map)
113110
else:
114111
logger.error(f"Object {new_object.name} already exists")
115112
return self
@@ -144,7 +141,7 @@ def add_annotation(
144141
)
145142
contents = [{"user": user, "value": message}]
146143
if self._metadata is not None:
147-
with self.ydoc.begin_transaction() as t:
144+
with self.ydoc.transaction() as t:
148145
self._metadata.set(
149146
t,
150147
new_id,
@@ -165,7 +162,7 @@ def remove_annotation(self, annotation_id: str) -> None:
165162
:param annotation_id: The id of the annotation
166163
"""
167164
if self._metadata is not None:
168-
with self.ydoc.begin_transaction() as t:
165+
with self.ydoc.transaction() as t:
169166
self._metadata.pop(t, annotation_id, None)
170167

171168
def set_color(self, object_name: str, color: Optional[List]) -> None:
@@ -191,7 +188,7 @@ def set_color(self, object_name: str, color: Optional[List]) -> None:
191188
if color is not None:
192189
new_gui = {object_name: {"color": color}}
193190
if new_gui is not None:
194-
with self.ydoc.begin_transaction() as t:
191+
with self.ydoc.transaction() as t:
195192
self._options.set(t, "guidata", new_gui)
196193

197194
def add_step_file(
@@ -225,8 +222,7 @@ def add_step_file(
225222
"visible": True,
226223
}
227224

228-
with self.ydoc.begin_transaction() as t:
229-
self._objects_array.append(t, Y.YMap(data))
225+
self._objects_array.append(Map(data))
230226

231227
return self
232228

@@ -278,8 +274,7 @@ def add_occ_shape(
278274
"visible": True,
279275
}
280276

281-
with self.ydoc.begin_transaction() as t:
282-
self._objects_array.append(t, Y.YMap(data))
277+
self._objects_array.append(Map(data))
283278

284279
return self
285280

@@ -625,20 +620,19 @@ def _get_boolean_operands(self, shape1: str | int | None, shape2: str | int | No
625620
return shape1, shape2
626621

627622
def set_visible(self, name: str, value):
628-
obj: Optional[Y.YMap] = self._get_yobject_by_name(name)
623+
obj: Optional[Map] = self._get_yobject_by_name(name)
629624

630625
if obj is None:
631626
raise RuntimeError(f"No object named {name}")
632627

633-
with self.ydoc.begin_transaction() as t:
634-
obj.set(t, "visible", False)
628+
obj["visible"] = False
635629

636630
def check_exist(self, name: str) -> bool:
637631
if self.objects:
638632
return name in self.objects
639633
return False
640634

641-
def _get_yobject_by_name(self, name: str) -> Optional[Y.YMap]:
635+
def _get_yobject_by_name(self, name: str) -> Optional[Map]:
642636
if self._objects_array:
643637
for index, item in enumerate(self._objects_array):
644638
if item["name"] == name:

python/jupytercad_lab/jupytercad_lab/notebook/y_connector.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
from typing import Optional
33

4-
from ypywidgets.ypywidgets import Widget
4+
from ypywidgets import Widget
55

66
logger = logging.getLogger(__file__)
77

python/jupytercad_lab/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"watch:labextension": "jupyter labextension watch ."
5252
},
5353
"dependencies": {
54-
"@jupyter/docprovider": "^1.0.0",
54+
"@jupyter/docprovider": "^2.0.0",
5555
"@jupytercad/base": "^1.0.1",
5656
"@jupytercad/jupytercad-core": "^1.0.1",
5757
"@jupytercad/schema": "^1.0.1",

0 commit comments

Comments
 (0)