@nteract/epics
Version:
Redux-Observable epics for nteract apps
246 lines • 10.8 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.restartKernelEpic = exports.launchKernelWhenNotebookSetEpic = exports.extractNewKernel = exports.acquireKernelInfoEpic = exports.acquireKernelInfo = exports.watchExecutionStateEpic = void 0;
const messaging_1 = require("@nteract/messaging");
const mythic_notifications_1 = require("@nteract/mythic-notifications");
const redux_observable_1 = require("redux-observable");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const actions = __importStar(require("@nteract/actions"));
const selectors = __importStar(require("@nteract/selectors"));
const types_1 = require("@nteract/types");
const types_2 = require("@nteract/types");
const path = require("path");
/**
* Sets the execution state after a kernel has been launched.
*
* @oaram {ActionObservable} action$ ActionObservable for LAUNCH_KERNEL_SUCCESSFUL action
*/
exports.watchExecutionStateEpic = (action$) => action$.pipe(redux_observable_1.ofType(actions.LAUNCH_KERNEL_SUCCESSFUL), operators_1.switchMap((action) => action.payload.kernel.channels.pipe(operators_1.filter((msg) => msg.header.msg_type === "status"), operators_1.map((msg) => actions.setExecutionState({
kernelStatus: msg.content.execution_state,
kernelRef: action.payload.kernelRef
})), operators_1.takeUntil(action$.pipe(redux_observable_1.ofType(actions.KILL_KERNEL_SUCCESSFUL), operators_1.filter((killAction) => killAction.payload.kernelRef === action.payload.kernelRef))), operators_1.catchError((error) => {
return rxjs_1.of(actions.executeFailed({
error: new Error("The WebSocket connection has unexpectedly disconnected."),
code: types_2.errors.EXEC_WEBSOCKET_ERROR,
contentRef: action.payload.contentRef
}));
}))));
/**
* Send a kernel_info_request to the kernel.
*
* @param {Object} channels A object containing the kernel channels
* @returns {Observable} The reply from the server
*/
function acquireKernelInfo(channels, kernelRef, contentRef, state, kernelSpecName) {
const message = messaging_1.createMessage("kernel_info_request");
const obs = channels.pipe(messaging_1.childOf(message), messaging_1.ofMessageType("kernel_info_reply"), operators_1.first(), operators_1.mergeMap(msg => {
const c = msg.content;
const l = c.language_info;
const info = {
protocolVersion: c.protocol_version,
implementation: c.implementation,
implementationVersion: c.implementation_version,
banner: c.banner,
helpLinks: c.help_links,
languageName: l.name,
languageVersion: l.version,
mimetype: l.mimetype,
fileExtension: l.file_extension,
pygmentsLexer: l.pygments_lexer,
codemirrorMode: l.codemirror_mode,
nbconvertExporter: l.nbconvert_exporter
};
let result;
if (!c.protocol_version.startsWith("5")) {
result = [
actions.launchKernelFailed({
kernelRef,
contentRef,
error: new Error("The kernel that you are attempting to launch does not support the latest version (v5) of the messaging protocol.")
})
];
}
else {
result = [
// The original action we were using
actions.setLanguageInfo({
langInfo: msg.content.language_info,
kernelRef,
contentRef
}),
actions.setKernelInfo({
kernelRef,
info
}),
actions.setExecutionState({ kernelStatus: types_1.KernelStatus.Launched, kernelRef })
];
if (kernelSpecName) {
const kernelspec = selectors.kernelspecByName(state, {
name: kernelSpecName
});
if (kernelspec) {
result.push(actions.setKernelMetadata({
contentRef,
kernelInfo: kernelspec
}));
}
}
}
return rxjs_1.of(...result);
}));
return rxjs_1.Observable.create((observer) => {
const subscription = obs.subscribe(observer);
channels.next(message);
return subscription;
});
}
exports.acquireKernelInfo = acquireKernelInfo;
/**
* Gets information about newly launched kernel.
*
* @param {ActionObservable} The action type
*/
exports.acquireKernelInfoEpic = (action$, state$) => action$.pipe(redux_observable_1.ofType(actions.LAUNCH_KERNEL_SUCCESSFUL), operators_1.switchMap((action) => {
const { payload: { kernel: { channels, kernelSpecName }, kernelRef, contentRef } } = action;
return acquireKernelInfo(channels, kernelRef, contentRef, state$.value, kernelSpecName);
}));
exports.extractNewKernel = (filepath, notebook) => {
const cwd = (filepath && path.dirname(filepath)) || "/";
const kernelSpecName = notebook.getIn(["metadata", "kernelspec", "name"]) ||
notebook.getIn(["metadata", "language_info", "name"]) ||
"python3";
return {
cwd,
kernelSpecName
};
};
/**
* NOTE: This function is _exactly_ the same as the desktop loading.js version
* with one strong exception -- extractNewKernel
* Can they be combined without incurring a penalty on the web app?
* The native functions used are `path.dirname`, `path.resolve`, and `process.cwd()`
* We could always inject those dependencies separately...
*/
exports.launchKernelWhenNotebookSetEpic = (action$, state$) => action$.pipe(redux_observable_1.ofType(actions.FETCH_CONTENT_FULFILLED), operators_1.mergeMap((action) => {
const state = state$.value;
const contentRef = action.payload.contentRef;
const content = selectors.content(state, { contentRef });
if (!content ||
content.type !== "notebook" ||
content.model.type !== "notebook") {
// This epic only handles notebook content
return rxjs_1.EMPTY;
}
/**
* Avoid relaunching kernels for notebooks that have already
* launched their content.
*/
if (content.model.kernelRef) {
const kernel = selectors.kernel(state, {
kernelRef: content.model.kernelRef
});
if (kernel && kernel.channels) {
return rxjs_1.EMPTY;
}
}
const filepath = content.filepath;
const notebook = content.model.notebook;
const { cwd, kernelSpecName } = exports.extractNewKernel(filepath, notebook);
return rxjs_1.of(actions.launchKernelByName({
kernelSpecName,
cwd,
kernelRef: action.payload.kernelRef,
selectNextKernel: true,
contentRef: action.payload.contentRef
}));
}));
/**
* Restarts a Jupyter kernel in the local scenario, where a restart requires
* killing the existing kernel process and starting an ew one.
*/
exports.restartKernelEpic = (action$, state$) => action$.pipe(redux_observable_1.ofType(actions.RESTART_KERNEL), operators_1.concatMap((action) => {
var _a;
const state = state$.value;
const oldKernelRef = selectors.kernelRefByContentRef(state$.value, {
contentRef: action.payload.contentRef
});
if (!oldKernelRef) {
return rxjs_1.of(mythic_notifications_1.sendNotification.create({
title: "Failure to Restart",
message: "Unable to restart kernel, please select a new kernel.",
level: "error"
}));
}
const oldKernel = selectors.kernel(state, { kernelRef: oldKernelRef });
if (oldKernel && oldKernel.type === "websocket") {
return rxjs_1.EMPTY;
}
if (!oldKernelRef || !oldKernel) {
return rxjs_1.of(mythic_notifications_1.sendNotification.create({
title: "Failure to Restart",
message: "Unable to restart kernel, please select a new kernel.",
level: "error"
}));
}
const newKernelRef = types_2.createKernelRef();
const initiatingContentRef = action.payload.contentRef;
const successNotification = mythic_notifications_1.sendNotification.create({
title: "Kernel Restarting...",
message: `Kernel ${oldKernel.kernelSpecName ||
"unknown"} is restarting.`,
level: "success"
});
const kill = actions.killKernel({
restarting: true,
kernelRef: oldKernelRef
});
const relaunch = actions.launchKernelByName({
kernelSpecName: (_a = oldKernel.kernelSpecName) !== null && _a !== void 0 ? _a : undefined,
cwd: oldKernel.cwd,
kernelRef: newKernelRef,
selectNextKernel: true,
contentRef: initiatingContentRef
});
const awaitKernelReady = action$.pipe(redux_observable_1.ofType(actions.LAUNCH_KERNEL_SUCCESSFUL), operators_1.filter((action) => action.payload.kernelRef === newKernelRef), operators_1.take(1), operators_1.timeout(60000), // If kernel doesn't come up within this interval we will abort follow-on actions.
operators_1.concatMap(() => {
const restartSuccess = actions.restartKernelSuccessful({
kernelRef: newKernelRef,
contentRef: initiatingContentRef
});
if (action.payload.outputHandling ===
"Run All") {
return rxjs_1.of(restartSuccess, actions.executeAllCells({ contentRef: initiatingContentRef }));
}
else {
return rxjs_1.of(restartSuccess);
}
}), operators_1.catchError(error => {
return rxjs_1.of(actions.restartKernelFailed({
error,
kernelRef: newKernelRef,
contentRef: initiatingContentRef
}));
}));
return rxjs_1.merge(rxjs_1.of(kill, relaunch, successNotification), awaitKernelReady);
}));
//# sourceMappingURL=kernel-lifecycle.js.map