Skip to content

Commit

Permalink
Merge pull request #201 from davidbrochart/output
Browse files Browse the repository at this point in the history
Change notebook code cell stream output schema
  • Loading branch information
Zsailer authored Jun 7, 2024
2 parents e76549a + 06565d5 commit 280886e
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 6 deletions.
34 changes: 31 additions & 3 deletions javascript/src/ycell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,12 +753,38 @@ export class YCodeCell
set outputs(v: Array<nbformat.IOutput>) {
this.setOutputs(v);
}
get youtputs(): Y.Array<any> {
return this._youtputs;
}

/**
* Execution, display, or stream outputs.
*/
getOutputs(): Array<nbformat.IOutput> {
return JSONExt.deepCopy(this._youtputs.toArray());
return JSONExt.deepCopy(this._youtputs.toJSON());
}

createOutputs(outputs: Array<nbformat.IOutput>): Array<any> {
const newOutputs: Array<any> = [];
for (const output of outputs) {
let _newOutput: { [id: string]: any };
const newOutput = new Y.Map();
if (output.output_type === 'stream') {
// Set the text field as a Y.Array
const { text, ...outputWithoutText } = output;
_newOutput = outputWithoutText;
const newText = new Y.Array();
newText.push(text as string[]);
_newOutput['text'] = newText;
} else {
_newOutput = output;
}
for (const [key, value] of Object.entries(_newOutput)) {
newOutput.set(key, value);
}
newOutputs.push(newOutput);
}
return newOutputs;
}

/**
Expand All @@ -767,7 +793,8 @@ export class YCodeCell
setOutputs(outputs: Array<nbformat.IOutput>): void {
this.transact(() => {
this._youtputs.delete(0, this._youtputs.length);
this._youtputs.insert(0, outputs);
const newOutputs = this.createOutputs(outputs);
this._youtputs.insert(0, newOutputs);
}, false);
}

Expand All @@ -789,7 +816,8 @@ export class YCodeCell
end < this._youtputs.length ? end - start : this._youtputs.length - start;
this.transact(() => {
this._youtputs.delete(start, fin);
this._youtputs.insert(start, outputs);
const newOutputs = this.createOutputs(outputs);
this._youtputs.insert(start, newOutputs);
}, false);
}

Expand Down
7 changes: 6 additions & 1 deletion jupyter_ydoc/ynotebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@ def create_ycell(self, value: Dict[str, Any]) -> Map:
if "attachments" in cell and not cell["attachments"]:
del cell["attachments"]
elif cell_type == "code":
cell["outputs"] = Array(cell.get("outputs", []))
outputs = cell.get("outputs", [])
for idx, output in enumerate(outputs):
if output.get("output_type") == "stream":
output["text"] = Array(output.get("text", []))
outputs[idx] = Map(output)
cell["outputs"] = Array(outputs)

return Map(cell)

Expand Down
8 changes: 6 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ async def yws_server(request):
@pytest.fixture
def yjs_client(request):
client_id = request.param
p = subprocess.Popen(f"yarn node {here / 'yjs_client_'}{client_id}.js", shell=True)
p = subprocess.Popen(["node", f"{here / 'yjs_client_'}{client_id}.js"])
yield p
p.kill()
p.terminate()
try:
p.wait(timeout=10)
except Exception:
p.kill()
43 changes: 43 additions & 0 deletions tests/files/nb1.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "4166c837-41c7-4ada-b86e-fd9a7720a409",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello,"
]
}
],
"source": [
"print(\"Hello,\", end=\"\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
25 changes: 25 additions & 0 deletions tests/test_pycrdt_yjs.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,31 @@ async def test_ypy_yjs_0(yws_server, yjs_client):
assert ytest.source == nb


@pytest.mark.asyncio
@pytest.mark.parametrize("yjs_client", "1", indirect=True)
async def test_ypy_yjs_1(yws_server, yjs_client):
ydoc = Doc()
ynotebook = YNotebook(ydoc)
nb = stringify_source(json.loads((files_dir / "nb1.ipynb").read_text()))
ynotebook.source = nb
async with connect("ws://localhost:1234/my-roomname") as websocket, WebsocketProvider(
ydoc, websocket
):
output_text = ynotebook.ycells[0]["outputs"][0]["text"]
assert output_text.to_py() == ["Hello,"]
event = Event()

def callback(_event):
event.set()

output_text.observe(callback)

with move_on_after(10):
await event.wait()

assert output_text.to_py() == ["Hello,", " World!"]


def test_plotly_renderer():
"""This test checks in particular that the type cast is not breaking the data."""
ydoc = Doc()
Expand Down
23 changes: 23 additions & 0 deletions tests/yjs_client_1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) Jupyter Development Team.
* Distributed under the terms of the Modified BSD License.
*/

import { YNotebook } from '@jupyter/ydoc'
import { WebsocketProvider } from 'y-websocket'
import ws from 'ws'

const notebook = new YNotebook()

const wsProvider = new WebsocketProvider(
'ws://localhost:1234', 'my-roomname',
notebook.ydoc,
{ WebSocketPolyfill: ws }
)

wsProvider.on('sync', (isSynced) => {
const cell = notebook.getCell(0)
const youtput = cell.youtputs.get(0)
const text = youtput.get('text')
text.insert(1, [' World!'])
})

0 comments on commit 280886e

Please sign in to comment.