diff --git a/javascript/src/ycell.ts b/javascript/src/ycell.ts index ccafbf8..1dab677 100644 --- a/javascript/src/ycell.ts +++ b/javascript/src/ycell.ts @@ -758,7 +758,12 @@ export class YCodeCell * Execution, display, or stream outputs. */ getOutputs(): Array { - return JSONExt.deepCopy(this._youtputs.toArray()); + return JSONExt.deepCopy( + this._youtputs.toArray().filter( + // Filter out stdin output. + el => !(el instanceof Y.Map && el.get('output_type') === 'stdin') + ) + ); } /** diff --git a/jupyter_ydoc/__init__.py b/jupyter_ydoc/__init__.py index 9450058..9161b2b 100644 --- a/jupyter_ydoc/__init__.py +++ b/jupyter_ydoc/__init__.py @@ -7,6 +7,7 @@ from .yblob import YBlob as YBlob from .yfile import YFile as YFile from .ynotebook import YNotebook as YNotebook +from .ystdin import add_stdin_output as add_stdin_output from .yunicode import YUnicode as YUnicode # See compatibility note on `group` keyword in diff --git a/jupyter_ydoc/ynotebook.py b/jupyter_ydoc/ynotebook.py index 5166af1..f20bf7d 100644 --- a/jupyter_ydoc/ynotebook.py +++ b/jupyter_ydoc/ynotebook.py @@ -109,6 +109,16 @@ def get_cell(self, index: int) -> Dict[str, Any]: and not cell["attachments"] ): del cell["attachments"] + # filter out stdin output + outputs = cell.get("outputs", []) + del_outputs = [] + for idx, output in enumerate(outputs): + if output["output_type"] == "stdin": + del_outputs.append(idx) + deleted = 0 + for idx in del_outputs: + del outputs[idx - deleted] + deleted += 1 return cell def append_cell(self, value: Dict[str, Any]) -> None: diff --git a/jupyter_ydoc/ystdin.py b/jupyter_ydoc/ystdin.py new file mode 100644 index 0000000..dac96a4 --- /dev/null +++ b/jupyter_ydoc/ystdin.py @@ -0,0 +1,34 @@ +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. + +from pycrdt import Array, Map, Text + + +def add_stdin_output(outputs: Array, prompt: str = "", password: bool = False) -> int: + """ + Adds an stdin output Map in the cell outputs, and returns its index. + + Schema: + + .. code-block:: json + + { + "output_type": "stdin", + "submitted": bool, + "password": bool + "prompt": str, + "value": Text + } + """ + stdin_output = Map( + { + "output_type": "stdin", + "submitted": False, + "password": password, + "prompt": prompt, + "value": Text(), + } + ) + stdin_idx = len(outputs) + outputs.append(stdin_output) + return stdin_idx diff --git a/tests/test_ydocs.py b/tests/test_ydocs.py index c9fdd3e..7be48fe 100644 --- a/tests/test_ydocs.py +++ b/tests/test_ydocs.py @@ -1,7 +1,7 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -from jupyter_ydoc import YBlob +from jupyter_ydoc import YBlob, YNotebook, add_stdin_output def test_yblob(): @@ -12,7 +12,6 @@ def test_yblob(): changes = [] def callback(topic, event): - print(topic, event) changes.append((topic, event)) yblob.observe(callback) @@ -22,3 +21,38 @@ def callback(topic, event): assert topic == "source" assert event.keys["bytes"]["oldValue"] == b"012" assert event.keys["bytes"]["newValue"] == b"345" + + +def test_stdin_output(): + ynotebook = YNotebook() + ynotebook.append_cell( + { + "cell_type": "code", + "source": "", + } + ) + ycell = ynotebook.ycells[0] + youtputs = ycell["outputs"] + stdin_idx = add_stdin_output(youtputs, prompt="pwd:", password=True) + stdin_output = youtputs[stdin_idx] + stdin = stdin_output["value"] + stdin += "mypassword" + stdin_output["submitted"] = True + + cell = ycell.to_py() + # cell ID is random, ignore that + del cell["id"] + assert cell == { + "outputs": [ + { + "output_type": "stdin", + "value": "mypassword", + "prompt": "pwd:", + "password": True, + "submitted": True, + } + ], + "source": "", + "metadata": {}, + "cell_type": "code", + }