Skip to content

Commit 2c9eef1

Browse files
Split documents into separate files (#135)
1 parent 32ff384 commit 2c9eef1

File tree

8 files changed

+312
-280
lines changed

8 files changed

+312
-280
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ from jupyter_ydoc import ydocs
1919

2020
print(ydocs)
2121
# {
22-
# 'blob': <class 'jupyter_ydoc.ydoc.YBlob'>,
23-
# 'file': <class 'jupyter_ydoc.ydoc.YFile'>,
24-
# 'notebook': <class 'jupyter_ydoc.ydoc.YNotebook'>,
25-
# 'unicode': <class 'jupyter_ydoc.ydoc.YUnicode'>
22+
# 'blob': <class 'jupyter_ydoc.yblob.YBlob'>,
23+
# 'file': <class 'jupyter_ydoc.yfile.YFile'>,
24+
# 'notebook': <class 'jupyter_ydoc.ynotebook.YNotebook'>,
25+
# 'unicode': <class 'jupyter_ydoc.yunicode.YUnicode'>
2626
# }
2727
```
2828

jupyter_ydoc/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
import sys
55

66
from ._version import __version__ # noqa
7-
from .ydoc import YBlob, YFile, YNotebook, YUnicode # noqa
7+
from .yblob import YBlob # noqa
8+
from .yfile import YFile # noqa
9+
from .ynotebook import YNotebook # noqa
10+
from .yunicode import YUnicode # noqa
811

912
# See compatibility note on `group` keyword in
1013
# https://docs.python.org/3/library/importlib.metadata.html#entry-points

jupyter_ydoc/ybasedoc.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Copyright (c) Jupyter Development Team.
2+
# Distributed under the terms of the Modified BSD License.
3+
4+
from abc import ABC, abstractmethod
5+
from typing import Any, Callable, Optional
6+
7+
import y_py as Y
8+
9+
10+
class YBaseDoc(ABC):
11+
"""
12+
Base YDoc class.
13+
This class, defines the minimum API that any document must provide
14+
to be able to get and set the content of the document as well as
15+
subscribe to changes in the document.
16+
"""
17+
18+
def __init__(self, ydoc: Optional[Y.YDoc] = None):
19+
"""
20+
Constructs a YBaseDoc.
21+
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.
24+
"""
25+
if ydoc is None:
26+
self._ydoc = Y.YDoc()
27+
else:
28+
self._ydoc = ydoc
29+
self._ystate = self._ydoc.get_map("state")
30+
self._subscriptions = {}
31+
32+
@property
33+
def ystate(self) -> Y.YMap:
34+
"""
35+
A :class:`y_py.YMap` containing the state of the document.
36+
37+
:return: The document's state.
38+
:rtype: :class:`y_py.YMap`
39+
"""
40+
return self._ystate
41+
42+
@property
43+
def ydoc(self) -> Y.YDoc:
44+
"""
45+
The underlying :class:`y_py.YDoc` that contains the data.
46+
47+
:return: The document's ydoc.
48+
:rtype: :class:`y_py.YDoc`
49+
"""
50+
return self._ydoc
51+
52+
@property
53+
def source(self) -> Any:
54+
"""
55+
Returns the content of the document.
56+
57+
:return: The content of the document.
58+
:rtype: Any
59+
"""
60+
return self.get()
61+
62+
@source.setter
63+
def source(self, value: Any):
64+
"""
65+
Sets the content of the document.
66+
67+
:param value: The content of the document.
68+
:type value: Any
69+
"""
70+
return self.set(value)
71+
72+
@property
73+
def dirty(self) -> Optional[bool]:
74+
"""
75+
Returns whether the document is dirty.
76+
77+
:return: Whether the document is dirty.
78+
:rtype: Optional[bool]
79+
"""
80+
return self._ystate["dirty"]
81+
82+
@dirty.setter
83+
def dirty(self, value: bool) -> None:
84+
"""
85+
Sets the document as clean (all changes committed) or dirty (uncommitted changes).
86+
87+
:param value: Whether the document is clean or dirty.
88+
:type value: bool
89+
"""
90+
with self._ydoc.begin_transaction() as t:
91+
self._ystate.set(t, "dirty", value)
92+
93+
@property
94+
def path(self) -> Optional[str]:
95+
"""
96+
Returns document's path.
97+
98+
:return: Document's path.
99+
:rtype: Optional[str]
100+
"""
101+
return self._ystate.get("path")
102+
103+
@path.setter
104+
def path(self, value: str) -> None:
105+
"""
106+
Sets document's path.
107+
108+
:param value: Document's path.
109+
:type value: str
110+
"""
111+
with self._ydoc.begin_transaction() as t:
112+
self._ystate.set(t, "path", value)
113+
114+
@abstractmethod
115+
def get(self) -> Any:
116+
"""
117+
Returns the content of the document.
118+
119+
:return: Document's content.
120+
:rtype: Any
121+
"""
122+
123+
@abstractmethod
124+
def set(self, value: Any) -> None:
125+
"""
126+
Sets the content of the document.
127+
128+
:param value: The content of the document.
129+
:type value: Any
130+
"""
131+
132+
@abstractmethod
133+
def observe(self, callback: Callable[[str, Any], None]) -> None:
134+
"""
135+
Subscribes to document changes.
136+
137+
:param callback: Callback that will be called when the document changes.
138+
:type callback: Callable[[str, Any], None]
139+
"""
140+
141+
def unobserve(self) -> None:
142+
"""
143+
Unsubscribes to document changes.
144+
145+
This method removes all the callbacks.
146+
"""
147+
for k, v in self._subscriptions.items():
148+
k.unobserve(v)
149+
self._subscriptions = {}

jupyter_ydoc/yblob.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Copyright (c) Jupyter Development Team.
2+
# Distributed under the terms of the Modified BSD License.
3+
4+
import base64
5+
from functools import partial
6+
from typing import Any, Callable, Optional, Union
7+
8+
import y_py as Y
9+
10+
from .ybasedoc import YBaseDoc
11+
12+
13+
class YBlob(YBaseDoc):
14+
"""
15+
Extends :class:`YBaseDoc`, and represents a blob document.
16+
It is currently encoded as base64 because of:
17+
https://github.com/y-crdt/ypy/issues/108#issuecomment-1377055465
18+
The Y document can be set from bytes or from str, in which case it is assumed to be encoded as
19+
base64.
20+
21+
Schema:
22+
23+
.. code-block:: json
24+
25+
{
26+
"state": YMap,
27+
"source": YMap
28+
}
29+
"""
30+
31+
def __init__(self, ydoc: Optional[Y.YDoc] = None):
32+
"""
33+
Constructs a YBlob.
34+
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.
37+
"""
38+
super().__init__(ydoc)
39+
self._ysource = self._ydoc.get_map("source")
40+
41+
def get(self) -> bytes:
42+
"""
43+
Returns the content of the document.
44+
45+
:return: Document's content.
46+
:rtype: bytes
47+
"""
48+
return base64.b64decode(self._ysource.get("base64", "").encode())
49+
50+
def set(self, value: Union[bytes, str]) -> None:
51+
"""
52+
Sets the content of the document.
53+
54+
:param value: The content of the document.
55+
:type value: Union[bytes, str]
56+
"""
57+
if isinstance(value, bytes):
58+
value = base64.b64encode(value).decode()
59+
with self._ydoc.begin_transaction() as t:
60+
self._ysource.set(t, "base64", value)
61+
62+
def observe(self, callback: Callable[[str, Any], None]) -> None:
63+
"""
64+
Subscribes to document changes.
65+
66+
:param callback: Callback that will be called when the document changes.
67+
:type callback: Callable[[str, Any], None]
68+
"""
69+
self.unobserve()
70+
self._subscriptions[self._ystate] = self._ystate.observe(partial(callback, "state"))
71+
self._subscriptions[self._ysource] = self._ysource.observe(partial(callback, "source"))

jupyter_ydoc/yfile.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) Jupyter Development Team.
2+
# Distributed under the terms of the Modified BSD License.
3+
4+
from .yunicode import YUnicode
5+
6+
7+
class YFile(YUnicode): # for backwards-compatibility
8+
pass

0 commit comments

Comments
 (0)