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