UNPKG

@nteract/epics

Version:
331 lines 14.4 kB
"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.restartWebSocketKernelEpic = exports.killKernelEpic = exports.interruptKernelEpic = exports.changeWebSocketKernelEpic = exports.launchWebSocketKernelEpic = void 0; const messaging_1 = require("@nteract/messaging"); const redux_observable_1 = require("redux-observable"); const rx_jupyter_1 = require("rx-jupyter"); 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 kernel_lifecycle_1 = require("./kernel-lifecycle"); exports.launchWebSocketKernelEpic = (action$, state$) => action$.pipe(redux_observable_1.ofType(actions.LAUNCH_KERNEL_BY_NAME), // Only accept jupyter servers for the host with this epic operators_1.filter(() => selectors.isCurrentHostJupyter(state$.value)), // TODO: When a switchMap happens, we need to close down the originating // kernel, likely by sending a different action. Right now this gets // coordinated in a different way. operators_1.switchMap((action) => { const state = state$.value; const host = selectors.currentHost(state); if (host.type !== "jupyter") { // Dismiss any usage that isn't targeting a jupyter server return rxjs_1.empty(); } const serverConfig = selectors.serverConfig(host); const hostRef = selectors.hostRefByHostRecord(state, { host }); const { payload: { kernelSpecName, cwd, kernelRef, contentRef } } = action; const content = selectors.content(state, { contentRef }); if (!content || content.type !== "notebook") { return rxjs_1.empty(); } // TODO: Create a START_SESSION action instead (?) const sessionPayload = { kernel: { id: null, name: kernelSpecName }, name: "", // TODO: Figure where the leading slash comes from in the content store path: content.filepath.replace(/^\/+/g, ""), type: "notebook" }; // TODO: Handle failure cases here return rx_jupyter_1.sessions.create(serverConfig, sessionPayload).pipe(operators_1.mergeMap(data => { const session = data.response; const sessionId = types_1.castToSessionId(kernelRef); const remoteSessionId = types_1.castToSessionId(session.id); const kernel = Object.assign({}, session.kernel, { type: "websocket", info: null, sessionId, remoteSessionId, cwd, channels: rx_jupyter_1.kernels.connect(serverConfig, session.kernel.id, sessionId), kernelSpecName, hostRef, status: session.kernel.execution_state }); kernel.channels.next(messaging_1.kernelInfoRequest()); return rxjs_1.of(actions.launchKernelSuccessful({ kernel, kernelRef, contentRef: action.payload.contentRef, selectNextKernel: true })); }), operators_1.catchError(error => { return rxjs_1.of(actions.launchKernelFailed({ error })); })); })); exports.changeWebSocketKernelEpic = (action$, state$) => action$.pipe(redux_observable_1.ofType(actions.CHANGE_KERNEL_BY_NAME), // Only accept jupyter servers for the host with this epic operators_1.filter(() => selectors.isCurrentHostJupyter(state$.value)), // TODO: When a switchMap happens, we need to close down the originating // kernel, likely by sending a different action. Right now this gets // coordinated in a different way. operators_1.switchMap((action) => { const { payload: { contentRef, oldKernelRef, kernelSpecName } } = action; const state = state$.value; const host = selectors.currentHost(state); if (host.type !== "jupyter") { // Dismiss any usage that isn't targeting a jupyter server return rxjs_1.empty(); } const serverConfig = selectors.serverConfig(host); // TODO: This is the case where we didn't have a kernel before // and they chose to switch kernels. Instead we need to allow // "switching" by disregarding the previous kernel and creating a // new session if (!oldKernelRef) { return rxjs_1.empty(); } const oldKernel = selectors.kernel(state, { kernelRef: oldKernelRef }); if (!oldKernel || oldKernel.type !== "websocket") { return rxjs_1.empty(); } const { sessionId, remoteSessionId } = oldKernel; if (!sessionId || !remoteSessionId) { return rxjs_1.empty(); } const content = selectors.content(state, { contentRef }); if (!content || content.type !== "notebook") { return rxjs_1.empty(); } const { filepath, model: { notebook } } = content; const { cwd } = kernel_lifecycle_1.extractNewKernel(filepath, notebook); const kernelRef = types_2.createKernelRef(); return rx_jupyter_1.kernels.start(serverConfig, kernelSpecName, cwd).pipe(operators_1.mergeMap(({ response }) => { const { id: kernelId } = response; const sessionPayload = { kernel: { id: kernelId, name: kernelSpecName } }; // The sessions API will close down the old kernel for us if it is // on this session return rx_jupyter_1.sessions.update(serverConfig, remoteSessionId, sessionPayload).pipe(operators_1.mergeMap(({ response: session }) => { const kernel = Object.assign({}, session.kernel, { type: "websocket", sessionId, remoteSessionId, cwd, channels: rx_jupyter_1.kernels.connect(serverConfig, session.kernel.id, sessionId), kernelSpecName }); return rxjs_1.of(actions.launchKernelSuccessful({ kernel, kernelRef, contentRef: action.payload.contentRef, selectNextKernel: true })); }), operators_1.catchError(error => rxjs_1.of(actions.launchKernelFailed({ error, kernelRef, contentRef })))); }), operators_1.catchError(error => rxjs_1.of(actions.launchKernelFailed({ error, kernelRef, contentRef })))); })); exports.interruptKernelEpic = (action$, state$) => action$.pipe(redux_observable_1.ofType(actions.INTERRUPT_KERNEL), // This epic can only interrupt kernels on jupyter websockets operators_1.filter(() => selectors.isCurrentHostJupyter(state$.value)), // If the user fires off _more_ interrupts, we shouldn't interrupt the in-flight // interrupt, instead doing it after the last one happens operators_1.concatMap((action) => { const state = state$.value; const host = selectors.currentHost(state); if (host.type !== "jupyter") { // Dismiss any usage that isn't targeting a jupyter server return rxjs_1.empty(); } const serverConfig = selectors.serverConfig(host); const { contentRef } = action.payload; let kernel; if (contentRef) { kernel = selectors.kernelByContentRef(state$.value, { contentRef }); } else { kernel = selectors.currentKernel(state$.value); } if (!kernel) { return rxjs_1.of(actions.interruptKernelFailed({ error: new Error("Can't interrupt a kernel we don't have"), kernelRef: action.payload.kernelRef })); } if (kernel.type !== "websocket") { return rxjs_1.of(actions.interruptKernelFailed({ error: new Error("Invalid kernel type for interrupting"), kernelRef: action.payload.kernelRef })); } if (!kernel.id) { return rxjs_1.of(actions.interruptKernelFailed({ error: new Error("Kernel does not have ID set"), kernelRef: action.payload.kernelRef })); } const id = kernel.id; return rx_jupyter_1.kernels.interrupt(serverConfig, id).pipe(operators_1.map(() => actions.interruptKernelSuccessful({ kernelRef: action.payload.kernelRef, contentRef })), operators_1.catchError(err => rxjs_1.of(actions.interruptKernelFailed({ error: err, kernelRef: action.payload.kernelRef })))); })); exports.killKernelEpic = (action$, state$) => // TODO: Use the sessions API for this action$.pipe(redux_observable_1.ofType(actions.KILL_KERNEL), // This epic can only interrupt kernels on jupyter websockets operators_1.filter(() => selectors.isCurrentHostJupyter(state$.value)), // If the user fires off _more_ kills, we shouldn't interrupt the in-flight // kill, instead doing it after the last one happens operators_1.concatMap((action) => { const state = state$.value; const host = selectors.currentHost(state); if (host.type !== "jupyter") { // Dismiss any usage that isn't targeting a jupyter server return rxjs_1.empty(); } const serverConfig = selectors.serverConfig(host); const { contentRef, kernelRef } = action.payload; let kernel; if (contentRef) { kernel = selectors.kernelByContentRef(state, { contentRef }); } else if (kernelRef) { kernel = selectors.kernel(state, { kernelRef }); } else { kernel = selectors.currentKernel(state); } if (!kernel) { return rxjs_1.of(actions.killKernelFailed({ error: new Error("kernel not available for killing"), kernelRef })); } if (kernel.type !== "websocket") { return rxjs_1.of(actions.killKernelFailed({ error: new Error("websocket kernel epic can only kill websocket kernels with an id"), kernelRef: action.payload.kernelRef })); } if (!kernel.id || !kernel.remoteSessionId) { return rxjs_1.of(actions.killKernelFailed({ error: new Error("websocket kernel epic can only kill websocket kernels with an id"), kernelRef: action.payload.kernelRef })); } // TODO: If this was a kernel language change, we shouldn't be using this // kill kernel epic because we need to make sure that creation happens // after deletion return rx_jupyter_1.sessions.destroy(serverConfig, kernel.remoteSessionId).pipe(operators_1.mergeMap(() => action.payload.dispose && action.payload.kernelRef ? rxjs_1.of(actions.killKernelSuccessful({ kernelRef: action.payload.kernelRef }), actions.disposeKernel({ kernelRef: action.payload.kernelRef })) : rxjs_1.of(actions.killKernelSuccessful({ kernelRef: action.payload.kernelRef }))), operators_1.catchError(err => rxjs_1.of(actions.killKernelFailed({ error: err, kernelRef: action.payload.kernelRef })))); })); exports.restartWebSocketKernelEpic = (action$, state$) => action$.pipe(redux_observable_1.ofType(actions.RESTART_KERNEL), operators_1.concatMap((action) => { const state = state$.value; const { contentRef, outputHandling } = action.payload; const kernelRef = selectors.kernelRefByContentRef(state, { contentRef }) || action.payload.kernelRef; /** * If there is still no KernelRef, then throw an error. */ if (!kernelRef) { return rxjs_1.of(actions.restartKernelFailed({ error: new Error("Can't execute restart without kernel ref."), kernelRef: "none provided", contentRef })); } const host = selectors.currentHost(state); if (host.type !== "jupyter") { return rxjs_1.of(actions.restartKernelFailed({ error: new Error("Can't restart a kernel with no Jupyter host."), kernelRef, contentRef })); } const serverConfig = selectors.serverConfig(host); const kernel = selectors.kernel(state, { kernelRef }); if (!kernel) { return rxjs_1.of(actions.restartKernelFailed({ error: new Error("Can't restart a kernel that does not exist."), kernelRef, contentRef })); } if (kernel.type !== "websocket" || !kernel.id) { return rxjs_1.of(actions.restartKernelFailed({ error: new Error("Can only restart Websocket kernels via API."), kernelRef, contentRef })); } const id = kernel.id; return rx_jupyter_1.kernels.restart(serverConfig, id).pipe(operators_1.mergeMap((response) => { if (response.status !== 200) { return rxjs_1.of(actions.restartKernelFailed({ error: new Error("Unsuccessful kernel restart."), kernelRef, contentRef })); } else { if (outputHandling === "Run All") { return rxjs_1.of(actions.restartKernelSuccessful({ kernelRef, contentRef }), actions.executeAllCells({ contentRef })); } else if (outputHandling === "Clear All") { return rxjs_1.of(actions.restartKernelSuccessful({ kernelRef, contentRef }), actions.clearAllOutputs({ contentRef })); } else { return rxjs_1.of(actions.restartKernelSuccessful({ kernelRef, contentRef })); } } }), operators_1.catchError(error => rxjs_1.of(actions.restartKernelFailed({ error, kernelRef, contentRef })))); })); //# sourceMappingURL=websocket-kernel.js.map