Bc-AI commited on
Commit
80653e8
·
verified ·
1 Parent(s): b00f048

Create notebook_runner.py

Browse files
Files changed (1) hide show
  1. server/notebook_runner.py +68 -0
server/notebook_runner.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ notebook_runner.py
3
+
4
+ Executes a Jupyter notebook (.ipynb) in-process and returns
5
+ cell outputs as a list of dicts. Used by POST /notebook/run.
6
+ """
7
+
8
+ import asyncio
9
+ import json
10
+ import time
11
+ from pathlib import Path
12
+ from typing import AsyncIterator
13
+
14
+ import nbformat
15
+ from nbconvert.preprocessors import ExecutePreprocessor
16
+ from nbformat.v4 import new_notebook, new_code_cell
17
+
18
+
19
+ async def run_notebook_source(
20
+ cells_source: list[str],
21
+ timeout: int = 60,
22
+ ) -> AsyncIterator[str]:
23
+ """
24
+ Given a list of code-cell source strings, execute them and
25
+ yield Server-Sent Event lines with cell outputs.
26
+ """
27
+ nb = new_notebook()
28
+ nb.cells = [new_code_cell(src) for src in cells_source]
29
+
30
+ def _execute():
31
+ ep = ExecutePreprocessor(
32
+ timeout=timeout,
33
+ kernel_name="python3",
34
+ allow_errors=True, # send errors as output, don't abort
35
+ )
36
+ ep.preprocess(nb)
37
+ return nb
38
+
39
+ loop = asyncio.get_event_loop()
40
+ executed_nb = await loop.run_in_executor(None, _execute)
41
+
42
+ for i, cell in enumerate(executed_nb.cells):
43
+ for output in cell.get("outputs", []):
44
+ otype = output.get("output_type", "")
45
+ if otype in ("stream",):
46
+ text = "".join(output.get("text", []))
47
+ yield _sse({"cell": i, "type": "stream", "text": text})
48
+ elif otype in ("execute_result", "display_data"):
49
+ data = output.get("data", {})
50
+ yield _sse({
51
+ "cell": i,
52
+ "type": otype,
53
+ "text": data.get("text/plain", ""),
54
+ "html": data.get("text/html", ""),
55
+ })
56
+ elif otype == "error":
57
+ yield _sse({
58
+ "cell": i,
59
+ "type": "error",
60
+ "ename": output.get("ename", ""),
61
+ "evalue": output.get("evalue", ""),
62
+ "traceback": output.get("traceback", []),
63
+ })
64
+ yield _sse({"type": "done"})
65
+
66
+
67
+ def _sse(obj: dict) -> str:
68
+ return f"data: {json.dumps(obj)}\n\n"