Skip to content

Commit 3c22892

Browse files
committed
Adding the outputs manager.
1 parent 49062c5 commit 3c22892

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import json
2+
import os
3+
from pathlib import Path, PurePath
4+
import shutil
5+
6+
7+
from traitlets.config.configurable import LoggingConfigurable
8+
from traitlets import (
9+
Any,
10+
Bool,
11+
Dict,
12+
Instance,
13+
List,
14+
TraitError,
15+
Type,
16+
Unicode,
17+
default,
18+
validate,
19+
)
20+
21+
from jupyter_core.paths import jupyter_runtime_dir
22+
23+
class OutputsManager(LoggingConfigurable):
24+
25+
outputs_path = Instance(PurePath, help="The local runtime dir")
26+
_last_output_index = Dict(default_value={})
27+
28+
@default("outputs_path")
29+
def _default_outputs_path(self):
30+
return Path(jupyter_runtime_dir()) / "outputs"
31+
32+
def _ensure_path(self, file_id, cell_id):
33+
nested_dir = self.outputs_path / file_id / cell_id
34+
self.log.info(f"Creating directory: {nested_dir}")
35+
nested_dir.mkdir(parents=True, exist_ok=True)
36+
37+
def _build_path(self, file_id, cell_id=None, output_index=None):
38+
path = self.outputs_path / file_id
39+
if cell_id is not None:
40+
path = path / cell_id
41+
if output_index is not None:
42+
path = path / f"{output_index}.output"
43+
return path
44+
45+
def get(self, file_id, cell_id, output_index):
46+
"""Get an outputs by file_id, cell_id, and output_index."""
47+
self.log.info(f"OutputsManager.get: {file_id} {cell_id} {output_index}")
48+
path = self._build_path(file_id, cell_id, output_index)
49+
if not os.path.isfile(path):
50+
raise FileNotFoundError(f"The output file doesn't exist: {path}")
51+
with open(path, "r", encoding="utf-8") as f:
52+
output = json.loads(f.read())
53+
return output
54+
55+
def get_stream(self, file_id, cell_id):
56+
"Get the stream output for a cell by file_id and cell_id."
57+
path = self._build_path(file_id, cell_id) / "stream"
58+
if not os.path.isfile(path):
59+
raise FileNotFoundError(f"The output file doesn't exist: {path}")
60+
with open(path, "r", encoding="utf-8") as f:
61+
output = f.read()
62+
return output
63+
64+
def write(self, file_id, cell_id, output):
65+
"""Write a new output for file_id and cell_id."""
66+
self.log.info(f"OutputsManager.write: {file_id} {cell_id} {output}")
67+
if output["output_type"] == "stream":
68+
url = self.write_stream(file_id, cell_id, output)
69+
else:
70+
url = self.write_output(file_id, cell_id, output)
71+
return url
72+
73+
def write_output(self, file_id, cell_id, output):
74+
self._ensure_path(file_id, cell_id)
75+
last_index = self._last_output_index.get(cell_id, -1)
76+
index = last_index + 1
77+
self._last_output_index[cell_id] = index
78+
path = self._build_path(file_id, cell_id, index)
79+
data = json.dumps(output, ensure_ascii=False)
80+
with open(path, "w", encoding="utf-8") as f:
81+
f.write(data)
82+
url = f"/api/outputs/{file_id}/{cell_id}/{index}.output"
83+
return url
84+
85+
def write_stream(self, file_id, cell_id, output):
86+
self._ensure_path(file_id, cell_id)
87+
path = self._build_path(file_id, cell_id) / "stream"
88+
text = output["text"]
89+
mode = 'a' if os.path.isfile(path) else 'w'
90+
with open(path, "a", encoding="utf-8") as f:
91+
f.write(text)
92+
url = f"/api/outputs/{file_id}/{cell_id}/stream"
93+
return url
94+
95+
def clear(self, file_id, cell_id=None):
96+
path = self._build_path(file_id, cell_id)
97+
shutil.rmtree(path)
98+
99+
100+

0 commit comments

Comments
 (0)