Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/jupyter/kernel/kernel-data.ts
1447 views
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
Use nteracts kernelspecs module to get data about all installed Jupyter kernels.
8
9
The result is cached for a few seconds to avoid wasted effort in case
10
of a flurry of calls.
11
12
Specs: https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-specs
13
14
This is supposed to be basically the same as "jupyter kernelspec list --json", but it is NOT always.
15
E.g., on my dev system the "ptyhon3" system-wide kernel is just completely missed. Also,
16
"jupyter kernelspec list --json" is MUCH slower, taking almost a second, versus only
17
a few ms for this. We stick with this for now, but may need to improve.
18
*/
19
20
import { findAll } from "kernelspecs";
21
import LRU from "lru-cache";
22
import type { KernelSpec } from "@cocalc/jupyter/types/types";
23
import { field_cmp } from "@cocalc/util/misc";
24
import { reuseInFlight } from "@cocalc/util/reuse-in-flight";
25
26
const cache = new LRU<"kernel_data", KernelSpec[]>({
27
ttl: 15 * 1000,
28
max: 5 /* silly since only one possible key */,
29
});
30
31
/**
32
* just an edge case for Macaulay2. Full data looks like this:
33
* m2: {
34
name: 'm2',
35
files: [ ... ],
36
resources_dir: '...',
37
spec: {
38
argv: [...],
39
display_name: 'M2',
40
language: 'text/x-macaulay2',
41
codemirror_mode: 'macaulay2'
42
}
43
},
44
*/
45
function spec2language(spec): string {
46
if (spec.language === "text/x-macaulay2") {
47
return "Macaulay2";
48
} else {
49
return spec.language;
50
}
51
}
52
53
// this is very expensive and can get called many times at once before
54
// the cache is set, which is very bad... so reuseInFlight.
55
export const get_kernel_data = reuseInFlight(
56
async ({ noCache }: { noCache?: boolean } = {}): Promise<KernelSpec[]> => {
57
if (!noCache) {
58
let x = cache.get("kernel_data");
59
if (x != null) {
60
return x;
61
}
62
}
63
const kernel_data = await findAll();
64
const v: KernelSpec[] = [];
65
for (const kernel in kernel_data) {
66
const value = kernel_data[kernel];
67
v.push({
68
name: kernel.toLowerCase(),
69
display_name: value.spec.display_name,
70
language: spec2language(value.spec),
71
// @ts-ignore
72
interrupt_mode: value.spec.interrupt_mode,
73
env: value.spec.env ?? {},
74
// @ts-ignore
75
metadata: value.spec.metadata,
76
// kernelspecs incorrectly calls it resources_dir instead of resource_dir.
77
// See https://github.com/nteract/kernelspecs/issues/25
78
// @ts-ignore
79
resource_dir: value.resource_dir ?? value.resources_dir,
80
argv: value.spec.argv,
81
});
82
}
83
v.sort(field_cmp("display_name"));
84
cache.set("kernel_data", v);
85
return v;
86
},
87
);
88
89
export async function getLanguage(kernelName: string): Promise<string> {
90
const kernelSpec = await get_kernel_data_by_name(kernelName);
91
if (kernelSpec != null) {
92
return kernelSpec.language;
93
}
94
throw Error(`unknown kernel ${kernelName}`);
95
}
96
97
export async function get_kernel_data_by_name(
98
name: string,
99
): Promise<KernelSpec> {
100
name = name.toLowerCase();
101
const kernel_data = await get_kernel_data();
102
for (const kernel of kernel_data) {
103
if (kernel.name == name) {
104
return kernel;
105
}
106
}
107
throw Error(`no such kernel '${name}'`);
108
}
109
110
// return the name of a python kernel -- very useful for unit testing.
111
export async function getPythonKernelName() {
112
const kernels = await get_kernel_data();
113
for (const x of kernels) {
114
const name = x.name.toLowerCase();
115
if (name.includes("python") && !name.includes("python2")) {
116
return x.name;
117
}
118
}
119
throw Error("no python kernels");
120
}
121
122