Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/jupyter/kernel/conat-service.ts
1447 views
1
/*
2
3
4
DEVELOPMENT:
5
6
Go to packages/project/c/open-files.ts and for a dev project, stop the built in open files service
7
and start your own in a terminal. If you then open a jupyter notebook in that project, you can use
8
your terminal running the open files service to interact with anything here from the server size.
9
In particular, set global.x = ..., etc.
10
*/
11
12
import { createConatJupyterService } from "@cocalc/conat/service/jupyter";
13
import { kernels } from "./kernels";
14
import { bufferToBase64 } from "@cocalc/util/base64";
15
import { once } from "@cocalc/util/async-utils";
16
import { type JupyterKernel } from "./kernel";
17
18
export async function initConatService({
19
path,
20
project_id,
21
timeout = 15000, // used for getKernel right now
22
}: {
23
path: string;
24
project_id: string;
25
timeout?: number;
26
}) {
27
const getExistingKernel = async (): Promise<JupyterKernel> => {
28
let kernel = kernels.get(path);
29
if (kernel != null) {
30
return kernel;
31
}
32
try {
33
[kernel] = await once(kernels, path, timeout);
34
return kernel!;
35
} catch {}
36
// timeout
37
// it doesn't exist right now, but it probably will in a few seconds, so wait
38
// in an event driven way for it to get opened.
39
40
throw Error(`no Jupyter kernel with path '${path}'`);
41
};
42
43
const impl = {
44
// Signal should be a string like "SIGINT", "SIGKILL".
45
signal: async (signal: string) => {
46
kernels.get(path)?.signal(signal);
47
},
48
49
save_ipynb_file: async (opts?) => {
50
await (await getExistingKernel()).save_ipynb_file(opts);
51
},
52
53
kernel_info: async () => {
54
return await (await getExistingKernel()).kernel_info();
55
},
56
57
more_output: async (id) => {
58
return (await getExistingKernel()).more_output(id);
59
},
60
61
complete: async (opts) => {
62
return await (
63
await getExistingKernel()
64
).complete(get_code_and_cursor_pos(opts));
65
},
66
67
introspect: async (opts) => {
68
const { code, cursor_pos } = get_code_and_cursor_pos(opts);
69
let detail_level = 0;
70
if (opts.level != null) {
71
try {
72
detail_level = parseInt(opts.level);
73
if (detail_level < 0) {
74
detail_level = 0;
75
} else if (detail_level > 1) {
76
detail_level = 1;
77
}
78
} catch (err) {}
79
}
80
return await (
81
await getExistingKernel()
82
).introspect({
83
code,
84
cursor_pos,
85
detail_level,
86
});
87
},
88
store: async ({
89
key,
90
value,
91
}: {
92
key: string;
93
value?: any;
94
}): Promise<any> => {
95
const kernel = await getExistingKernel();
96
if (value === undefined) {
97
// undefined when getting the value
98
return kernel.store.get(key);
99
} else if (value === null) {
100
// null is used for deleting the value
101
kernel.store.delete(key);
102
return {};
103
} else {
104
kernel.store.set(key, value);
105
return {};
106
}
107
},
108
comm: async (opts) => {
109
(await getExistingKernel()).send_comm_message_to_kernel(opts);
110
},
111
112
ipywidgetsGetBuffer: async ({ model_id, buffer_path }) => {
113
const buffer = (await getExistingKernel()).ipywidgetsGetBuffer(
114
model_id,
115
buffer_path,
116
);
117
if (buffer == null) {
118
throw Error(
119
`no buffer for model=${model_id}, buffer_path=${JSON.stringify(
120
buffer_path,
121
)}`,
122
);
123
}
124
return { buffer64: bufferToBase64(buffer) };
125
},
126
};
127
return await createConatJupyterService({
128
project_id,
129
path,
130
impl,
131
});
132
}
133
134
function get_code_and_cursor_pos(opts): {
135
code: string;
136
cursor_pos: number;
137
} {
138
const code: string = opts.code;
139
if (!code) {
140
throw Error("must specify code");
141
}
142
let cursor_pos: number;
143
if (opts.cursor_pos != null) {
144
try {
145
cursor_pos = parseInt(opts.cursor_pos);
146
} catch (error) {
147
cursor_pos = code.length;
148
}
149
} else {
150
cursor_pos = code.length;
151
}
152
153
return { code, cursor_pos };
154
}
155
156