Skip to content

Commit 9db91b8

Browse files
Merge pull request #3 from davidbrochart/tests
Add test
2 parents 3ec7528 + 30e4c0b commit 9db91b8

File tree

12 files changed

+246
-27
lines changed

12 files changed

+246
-27
lines changed

.github/workflows/test.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,9 @@ jobs:
4848
python-version: "3.7"
4949
- name: Install dependencies
5050
run: |
51-
mamba install python=${{ matrix.python-version }} pip
51+
mamba install python=${{ matrix.python-version }} pip nodejs
5252
pip install .[test]
53+
cd tests; npm install
54+
- name: Run tests
55+
run: |
56+
pytest -v

README.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@ documents used in the Jupyter ecosystem. Built-in documents include:
88
- `YFile`: a generic text document.
99
- `YNotebook`: a Jupyter notebook document.
1010

11-
These documents are registered in an entry point under the `jupyter_ydoc` group as `"file"` and
11+
These documents are registered via an entry point under the `"jupyter_ydoc"` group as `"file"` and
1212
`"notebook"`, respectively. You can access them as follows:
1313

14+
```py
15+
from jupyter_ydoc import ydocs
16+
17+
print(ydocs)
18+
# {'file': <class 'jupyter_ydoc.ydoc.YFile'>, 'notebook': <class 'jupyter_ydoc.ydoc.YNotebook'>}
19+
```
20+
21+
Which is just a shortcut to:
22+
1423
```py
1524
import sys
1625
import jupyter_ydoc
@@ -22,8 +31,11 @@ else:
2231
from importlib.metadata import entry_points
2332

2433
ydocs = {ep.name: ep.load() for ep in entry_points(group="jupyter_ydoc")}
25-
print(ydocs)
26-
# {'file': <class 'jupyter_ydoc.ydoc.YFile'>, 'notebook': <class 'jupyter_ydoc.ydoc.YNotebook'>}
34+
```
35+
36+
Or directly import them:
37+
```py
38+
from jupyter_ydoc import YFile, YNotebook
2739
```
2840

2941
The `jupyter_ydoc` entry point group can be populated with your own document, e.g. by adding the

jupyter_ydoc/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,14 @@
1+
import sys
2+
3+
from .ydoc import YFile, YNotebook # noqa
4+
5+
# See compatibility note on `group` keyword in
6+
# https://docs.python.org/3/library/importlib.metadata.html#entry-points
7+
if sys.version_info < (3, 10):
8+
from importlib_metadata import entry_points
9+
else:
10+
from importlib.metadata import entry_points
11+
12+
ydocs = {ep.name: ep.load() for ep in entry_points(group="jupyter_ydoc")}
13+
114
__version__ = "0.1.0"

jupyter_ydoc/utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from typing import Dict, List, Union
2+
3+
4+
def cast_all(o: Union[List, Dict], from_type, to_type) -> Union[List, Dict]:
5+
if isinstance(o, list):
6+
for i, v in enumerate(o):
7+
if isinstance(v, from_type):
8+
o[i] = to_type(v)
9+
elif isinstance(v, (list, dict)):
10+
cast_all(v, from_type, to_type)
11+
elif isinstance(o, dict):
12+
for k, v in o.items():
13+
if isinstance(v, from_type):
14+
o[k] = to_type(v)
15+
elif isinstance(v, (list, dict)):
16+
cast_all(v, from_type, to_type)
17+
return o

jupyter_ydoc/ydoc.py

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
from typing import Dict, List, Union
1+
import copy
22
from uuid import uuid4
33

44
import y_py as Y
55
from ypy_websocket.websocket_server import YDoc
66

7+
from .utils import cast_all
8+
79

810
class YBaseDoc:
911
def __init__(self, ydoc: YDoc):
@@ -96,9 +98,10 @@ def source(self):
9698

9799
@source.setter
98100
def source(self, value):
99-
cast_all(value, int, float)
100-
if not value["cells"]:
101-
value["cells"] = [
101+
nb = copy.deepcopy(value)
102+
cast_all(nb, int, float)
103+
if not nb["cells"]:
104+
nb["cells"] = [
102105
{
103106
"cell_type": "code",
104107
"execution_count": None,
@@ -122,7 +125,7 @@ def source(self, value):
122125

123126
# initialize document
124127
ycells = []
125-
for cell in value["cells"]:
128+
for cell in nb["cells"]:
126129
cell_source = cell["source"]
127130
if cell_source:
128131
ytext = Y.YText(cell_source)
@@ -137,9 +140,9 @@ def source(self, value):
137140

138141
if ycells:
139142
self._ycells.push(t, ycells)
140-
self._ymeta.set(t, "metadata", value["metadata"])
141-
self._ystate.set(t, "nbformat", value["nbformat"])
142-
self._ystate.set(t, "nbformatMinor", value["nbformat_minor"])
143+
self._ymeta.set(t, "metadata", nb["metadata"])
144+
self._ystate.set(t, "nbformat", nb["nbformat"])
145+
self._ystate.set(t, "nbformatMinor", nb["nbformat_minor"])
143146
with self._ydoc.begin_transaction() as t:
144147
for ytext in ytexts_to_clear:
145148
ytext.delete(t, 0, 1)
@@ -153,18 +156,3 @@ def observe(self, callback):
153156
self._subscriptions[cell] = cell.observe(callback)
154157
self._subscriptions[self._ycells] = self._ycells.observe(callback)
155158
self._subscriptions[self._ymeta] = self._ymeta.observe(callback)
156-
157-
158-
def cast_all(o: Union[List, Dict], from_type, to_type) -> None:
159-
if isinstance(o, list):
160-
for i, v in enumerate(o):
161-
if isinstance(v, from_type):
162-
o[i] = to_type(v)
163-
elif isinstance(v, (list, dict)):
164-
cast_all(v, from_type, to_type)
165-
elif isinstance(o, dict):
166-
for k, v in o.items():
167-
if isinstance(v, from_type):
168-
o[k] = to_type(v)
169-
elif isinstance(v, (list, dict)):
170-
cast_all(v, from_type, to_type)

pytest.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[pytest]
2+
asyncio_mode = auto

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ install_requires =
2626
[options.extras_require]
2727
test =
2828
pre-commit
29+
pytest
30+
pytest-asyncio
31+
websockets >=10.0
2932

3033
[options.entry_points]
3134
jupyter_ydoc =

tests/conftest.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import json
2+
import subprocess
3+
from pathlib import Path
4+
5+
import pytest
6+
from websockets import serve # type: ignore
7+
from ypy_websocket import WebsocketServer
8+
9+
# workaround until these PRs are merged:
10+
# - https://github.com/jupyterlab/jupyterlab/pull/12519
11+
# - https://github.com/yjs/y-websocket/pull/104
12+
13+
14+
def update_json_file(path: Path, d: dict):
15+
with open(path, "rb") as f:
16+
package_json = json.load(f)
17+
package_json.update(d)
18+
with open(path, "wt") as f:
19+
json.dump(package_json, f, indent=2)
20+
21+
22+
here = Path(__file__).parent
23+
d = {"type": "module"}
24+
update_json_file(here / "node_modules/@jupyterlab/shared-models/package.json", d)
25+
update_json_file(here / "node_modules/y-websocket/package.json", d)
26+
27+
28+
@pytest.fixture
29+
async def yws_server(request):
30+
try:
31+
kwargs = request.param
32+
except Exception:
33+
kwargs = {}
34+
websocket_server = WebsocketServer(**kwargs)
35+
async with serve(websocket_server.serve, "localhost", 1234):
36+
yield websocket_server
37+
38+
39+
@pytest.fixture
40+
def yjs_client(request):
41+
client_id = request.param
42+
p = subprocess.Popen(
43+
[
44+
"node",
45+
"--experimental-specifier-resolution=node",
46+
f"{here / 'yjs_client_'}{client_id}.js",
47+
]
48+
)
49+
yield p
50+
p.kill()

tests/files/nb0.ipynb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"source": "print('Hello, World!')",
6+
"metadata": {},
7+
"outputs": [],
8+
"execution_count": null,
9+
"id": null
10+
}
11+
],
12+
"metadata": {
13+
"kernelspec": {
14+
"display_name": "Python 3 (ipykernel)",
15+
"language": "python",
16+
"name": "python3"
17+
},
18+
"language_info": {
19+
"codemirror_mode": {
20+
"name": "ipython",
21+
"version": 3
22+
},
23+
"file_extension": ".py",
24+
"mimetype": "text/x-python",
25+
"name": "python",
26+
"nbconvert_exporter": "python",
27+
"pygments_lexer": "ipython3",
28+
"version": "3.10.2"
29+
}
30+
},
31+
"nbformat": 4,
32+
"nbformat_minor": 5
33+
}

tests/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type": "module",
3+
"dependencies": {
4+
"@jupyterlab/shared-models": "^3.3.4",
5+
"ws": "^8.5.0",
6+
"y-websocket": "^1.4.1"
7+
}
8+
}

0 commit comments

Comments
 (0)