UNPKG

@jupyterlab/services

Version:

Client APIs for the Jupyter services REST APIs

338 lines 11.4 kB
"use strict"; // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. Object.defineProperty(exports, "__esModule", { value: true }); exports.KernelManager = void 0; const polling_1 = require("@lumino/polling"); const signaling_1 = require("@lumino/signaling"); const __1 = require(".."); const basemanager_1 = require("../basemanager"); const restapi_1 = require("./restapi"); const default_1 = require("./default"); /** * An implementation of a kernel manager. */ class KernelManager extends basemanager_1.BaseManager { /** * Construct a new kernel manager. * * @param options - The default options for kernel. */ constructor(options = {}) { var _a; super(options); this._isReady = false; this._kernelConnections = new Set(); this._models = new Map(); this._runningChanged = new signaling_1.Signal(this); this._connectionFailure = new signaling_1.Signal(this); // Start model and specs polling with exponential backoff. this._pollModels = new polling_1.Poll({ auto: false, factory: () => this.requestRunning(), frequency: { interval: 10 * 1000, backoff: true, max: 300 * 1000 }, name: `@jupyterlab/services:KernelManager#models`, standby: (_a = options.standby) !== null && _a !== void 0 ? _a : 'when-hidden' }); // Initialize internal data. this._ready = (async () => { await this._pollModels.start(); await this._pollModels.tick; this._isReady = true; })(); } /** * Test whether the manager is ready. */ get isReady() { return this._isReady; } /** * A promise that fulfills when the manager is ready. */ get ready() { return this._ready; } /** * A signal emitted when the running kernels change. */ get runningChanged() { return this._runningChanged; } /** * A signal emitted when there is a connection failure. */ get connectionFailure() { return this._connectionFailure; } /** * Dispose of the resources used by the manager. */ dispose() { if (this.isDisposed) { return; } this._models.clear(); this._kernelConnections.forEach(x => x.dispose()); this._pollModels.dispose(); super.dispose(); } /** * Connect to an existing kernel. * * @returns The new kernel connection. * * #### Notes * This will use the manager's server settings and ignore any server * settings passed in the options. */ connectTo(options) { var _a; const { id } = options.model; let handleComms = (_a = options.handleComms) !== null && _a !== void 0 ? _a : true; // By default, handle comms only if no other kernel connection is. if (options.handleComms === undefined) { for (const kc of this._kernelConnections) { if (kc.id === id && kc.handleComms) { handleComms = false; break; } } } const kernelConnection = new default_1.KernelConnection({ handleComms, ...options, serverSettings: this.serverSettings }); this._onStarted(kernelConnection); if (!this._models.has(id)) { // We trust the user to connect to an existing kernel, but we verify // asynchronously. void this.refreshRunning().catch(() => { /* no-op */ }); } return kernelConnection; } /** * Create an iterator over the most recent running kernels. * * @returns A new iterator over the running kernels. */ running() { return this._models.values(); } /** * Force a refresh of the running kernels. * * @returns A promise that resolves when the running list has been refreshed. * * #### Notes * This is not typically meant to be called by the user, since the * manager maintains its own internal state. */ async refreshRunning() { await this._pollModels.refresh(); await this._pollModels.tick; } /** * Start a new kernel. * * @param createOptions - The kernel creation options * * @param connectOptions - The kernel connection options * * @returns A promise that resolves with the kernel connection. * * #### Notes * The manager `serverSettings` will be always be used. */ async startNew(createOptions = {}, connectOptions = {}) { const model = await (0, restapi_1.startNew)(createOptions, this.serverSettings); return this.connectTo({ ...connectOptions, model }); } /** * Shut down a kernel by id. * * @param id - The id of the target kernel. * * @returns A promise that resolves when the operation is complete. */ async shutdown(id) { await (0, restapi_1.shutdownKernel)(id, this.serverSettings); await this.refreshRunning(); } /** * Shut down all kernels. * * @returns A promise that resolves when all of the kernels are shut down. */ async shutdownAll() { // Update the list of models to make sure our list is current. await this.refreshRunning(); // Shut down all models. await Promise.all([...this._models.keys()].map(id => (0, restapi_1.shutdownKernel)(id, this.serverSettings))); // Update the list of models to clear out our state. await this.refreshRunning(); } /** * Find a kernel by id. * * @param id - The id of the target kernel. * * @returns A promise that resolves with the kernel's model. */ async findById(id) { if (this._models.has(id)) { return this._models.get(id); } await this.refreshRunning(); return this._models.get(id); } /** * Execute a request to the server to poll running kernels and update state. */ async requestRunning() { var _a, _b; let models; try { models = await (0, restapi_1.listRunning)(this.serverSettings); } catch (err) { // Handle network errors, as well as cases where we are on a // JupyterHub and the server is not running. JupyterHub returns a // 503 (<2.0) or 424 (>2.0) in that case. if (err instanceof __1.ServerConnection.NetworkError || ((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 503 || ((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 424) { this._connectionFailure.emit(err); } throw err; } if (this.isDisposed) { return; } if (this._models.size === models.length && models.every(model => { const existing = this._models.get(model.id); if (!existing) { return false; } return (existing.connections === model.connections && existing.execution_state === model.execution_state && existing.last_activity === model.last_activity && existing.name === model.name && existing.reason === model.reason && existing.traceback === model.traceback); })) { // Identical models list (presuming models does not contain duplicate // ids), so just return return; } this._models = new Map(models.map(x => [x.id, x])); // For any kernel connection to a kernel that doesn't exist, notify it of // the shutdown. this._kernelConnections.forEach(kc => { if (!this._models.has(kc.id)) { kc.handleShutdown(); } }); this._runningChanged.emit(models); } /** * Handle a kernel starting. */ _onStarted(kernelConnection) { this._kernelConnections.add(kernelConnection); kernelConnection.statusChanged.connect(this._onStatusChanged, this); kernelConnection.disposed.connect(this._onDisposed, this); } _onDisposed(kernelConnection) { this._kernelConnections.delete(kernelConnection); // A dispose emission could mean the server session is deleted, or that // the kernel JS object is disposed and the kernel still exists on the // server, so we refresh from the server to make sure we reflect the // server state. void this.refreshRunning().catch(() => { /* no-op */ }); } _onStatusChanged(kernelConnection, status) { if (status === 'dead') { // We asynchronously update our list of kernels, which asynchronously // will dispose them. We do not want to immediately dispose them because // there may be other signal handlers that want to be called. void this.refreshRunning().catch(() => { /* no-op */ }); } } } exports.KernelManager = KernelManager; /** * The namespace for `KernelManager` class statics. */ (function (KernelManager) { /** * A no-op kernel manager to be used when starting kernels. */ class NoopManager extends KernelManager { constructor() { super(...arguments); this._readyPromise = new Promise(() => { /* no-op */ }); } /** * Whether the manager is active. */ get isActive() { return false; } /** * Used for testing. */ get parentReady() { return super.ready; } /** * Start a new kernel - throws an error since it is not supported. */ async startNew(createOptions = {}, connectOptions = {}) { return Promise.reject(new Error('Not implemented in no-op Kernel Manager')); } /** * Connect to an existing kernel - throws an error since it is not supported. */ connectTo(options) { throw new Error('Not implemented in no-op Kernel Manager'); } /** * Shut down a kernel by id - throws an error since it is not supported. */ async shutdown(id) { return Promise.reject(new Error('Not implemented in no-op Kernel Manager')); } /** * A promise that fulfills when the manager is ready (never). */ get ready() { return this.parentReady.then(() => this._readyPromise); } /** * Execute a request to the server to poll running kernels and update state. */ async requestRunning() { return Promise.resolve(); } } KernelManager.NoopManager = NoopManager; })(KernelManager || (exports.KernelManager = KernelManager = {})); //# sourceMappingURL=manager.js.map