UNPKG

@jupyterlab/apputils

Version:
1,225 lines 45 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { PathExt } from '@jupyterlab/coreutils'; import { nullTranslator } from '@jupyterlab/translation'; import { find } from '@lumino/algorithm'; import { JSONExt, PromiseDelegate, UUID } from '@lumino/coreutils'; import { Signal } from '@lumino/signaling'; import { Widget } from '@lumino/widgets'; import * as React from 'react'; import { Dialog, showDialog } from './dialog'; /** * The default implementation for a session context object. */ export class SessionContext { /** * Construct a new session context. */ constructor(options) { var _a, _b, _c, _d; this._path = ''; this._name = ''; this._type = ''; this._prevKernelName = ''; this._isDisposed = false; this._disposed = new Signal(this); this._session = null; this._ready = new PromiseDelegate(); this._initializing = false; this._initStarted = new PromiseDelegate(); this._initPromise = new PromiseDelegate(); this._isReady = false; this._isTerminating = false; this._isRestarting = false; this._kernelChanged = new Signal(this); this._preferenceChanged = new Signal(this); this._sessionChanged = new Signal(this); this._statusChanged = new Signal(this); this._connectionStatusChanged = new Signal(this); this._pendingInput = false; this._iopubMessage = new Signal(this); this._unhandledMessage = new Signal(this); this._propertyChanged = new Signal(this); this._dialog = null; this._busyDisposable = null; this._pendingKernelName = ''; this._pendingSessionRequest = ''; this.kernelManager = options.kernelManager; this.sessionManager = options.sessionManager; this.specsManager = options.specsManager; this.translator = options.translator || nullTranslator; this._trans = this.translator.load('jupyterlab'); this._path = (_a = options.path) !== null && _a !== void 0 ? _a : UUID.uuid4(); this._type = (_b = options.type) !== null && _b !== void 0 ? _b : ''; this._name = (_c = options.name) !== null && _c !== void 0 ? _c : ''; this._setBusy = options.setBusy; this._kernelPreference = (_d = options.kernelPreference) !== null && _d !== void 0 ? _d : {}; } /** * The current session connection. */ get session() { var _a; return (_a = this._session) !== null && _a !== void 0 ? _a : null; } /** * The session path. * * #### Notes * Typically `.session.path` should be used. This attribute is useful if * there is no current session. */ get path() { return this._path; } /** * The session type. * * #### Notes * Typically `.session.type` should be used. This attribute is useful if * there is no current session. */ get type() { return this._type; } /** * The session name. * * #### Notes * Typically `.session.name` should be used. This attribute is useful if * there is no current session. */ get name() { return this._name; } /** * A signal emitted when the kernel connection changes, proxied from the session connection. */ get kernelChanged() { return this._kernelChanged; } /** * A signal emitted when the session connection changes. */ get sessionChanged() { return this._sessionChanged; } /** * A signal emitted when the kernel status changes, proxied from the kernel. */ get statusChanged() { return this._statusChanged; } /** * A flag indicating if the session has pending input, proxied from the kernel. */ get pendingInput() { return this._pendingInput; } /** * A signal emitted when the kernel status changes, proxied from the kernel. */ get connectionStatusChanged() { return this._connectionStatusChanged; } /** * A signal emitted for iopub kernel messages, proxied from the kernel. */ get iopubMessage() { return this._iopubMessage; } /** * A signal emitted for an unhandled kernel message, proxied from the kernel. */ get unhandledMessage() { return this._unhandledMessage; } /** * A signal emitted when a session property changes, proxied from the current session. */ get propertyChanged() { return this._propertyChanged; } /** * The kernel preference of this client session. * * This is used when selecting a new kernel, and should reflect the sort of * kernel the activity prefers. */ get kernelPreference() { return this._kernelPreference; } set kernelPreference(value) { if (!JSONExt.deepEqual(value, this._kernelPreference)) { const oldValue = this._kernelPreference; this._kernelPreference = value; this._preferenceChanged.emit({ name: 'kernelPreference', oldValue, newValue: JSONExt.deepCopy(value) }); } } /** * Signal emitted if the kernel preference changes. */ get kernelPreferenceChanged() { return this._preferenceChanged; } /** * Whether the context is ready. */ get isReady() { return this._isReady; } /** * A promise that is fulfilled when the context is ready. */ get ready() { return this._ready.promise; } /** * Whether the context is terminating. */ get isTerminating() { return this._isTerminating; } /** * Whether the context is restarting. */ get isRestarting() { return this._isRestarting; } /** * Whether the kernel is "No Kernel" or not. * * #### Notes * As the displayed name is translated, this can be used directly. */ get hasNoKernel() { return this.kernelDisplayName === this.noKernelName; } /** * The display name of the current kernel, or a sensible alternative. * * #### Notes * This is a convenience function to have a consistent sensible name for the * kernel. */ get kernelDisplayName() { var _a, _b, _c, _d, _e, _f, _g; const kernel = (_a = this.session) === null || _a === void 0 ? void 0 : _a.kernel; if (this._pendingKernelName === this.noKernelName) { return this.noKernelName; } if (this._pendingKernelName) { return ((_d = (_c = (_b = this.specsManager.specs) === null || _b === void 0 ? void 0 : _b.kernelspecs[this._pendingKernelName]) === null || _c === void 0 ? void 0 : _c.display_name) !== null && _d !== void 0 ? _d : this._pendingKernelName); } if (!kernel) { return this.noKernelName; } return ((_g = (_f = (_e = this.specsManager.specs) === null || _e === void 0 ? void 0 : _e.kernelspecs[kernel.name]) === null || _f === void 0 ? void 0 : _f.display_name) !== null && _g !== void 0 ? _g : kernel.name); } /** * A sensible status to display * * #### Notes * This combines the status and connection status into a single status for * the user. */ get kernelDisplayStatus() { var _a, _b; const kernel = (_a = this.session) === null || _a === void 0 ? void 0 : _a.kernel; if (this._isTerminating) { return 'terminating'; } if (this._isRestarting) { return 'restarting'; } if (this._pendingKernelName === this.noKernelName) { return 'unknown'; } if (!kernel && this._pendingKernelName) { return 'initializing'; } if (!kernel && !this.isReady && this.kernelPreference.canStart !== false && this.kernelPreference.shouldStart !== false) { return 'initializing'; } return ((_b = ((kernel === null || kernel === void 0 ? void 0 : kernel.connectionStatus) === 'connected' ? kernel === null || kernel === void 0 ? void 0 : kernel.status : kernel === null || kernel === void 0 ? void 0 : kernel.connectionStatus)) !== null && _b !== void 0 ? _b : 'unknown'); } /** * The name of the previously started kernel. */ get prevKernelName() { return this._prevKernelName; } /** * Test whether the context is disposed. */ get isDisposed() { return this._isDisposed; } /** * A signal emitted when the poll is disposed. */ get disposed() { return this._disposed; } /** * Get the constant displayed name for "No Kernel" */ get noKernelName() { return this._trans.__('No Kernel'); } /** * Dispose of the resources held by the context. */ dispose() { if (this._isDisposed) { return; } this._isDisposed = true; this._disposed.emit(); if (this._session) { if (this.kernelPreference.shutdownOnDispose) { // Fire and forget the session shutdown request this.sessionManager.shutdown(this._session.id).catch(reason => { console.error(`Kernel not shut down ${reason}`); }); } // Dispose the session connection this._session.dispose(); this._session = null; } if (this._dialog) { this._dialog.dispose(); } if (this._busyDisposable) { this._busyDisposable.dispose(); this._busyDisposable = null; } Signal.clearData(this); } /** * Starts new Kernel. * * @returns Whether to ask the user to pick a kernel. */ async startKernel() { const preference = this.kernelPreference; if (!preference.autoStartDefault && preference.shouldStart === false) { return true; } let options; if (preference.id) { options = { id: preference.id }; } else { const name = Private.getDefaultKernel({ specs: this.specsManager.specs, sessions: this.sessionManager.running(), preference }); if (name) { options = { name }; } } if (options) { try { await this._changeKernel(options); return false; } catch (err) { /* no-op */ } } // Always fall back to selecting a kernel return true; } /** * Restart the current Kernel. * * @returns A promise that resolves when the kernel is restarted. */ async restartKernel() { var _a, _b, _c, _d, _e, _f; const kernel = ((_a = this.session) === null || _a === void 0 ? void 0 : _a.kernel) || null; if (this._isRestarting) { return; } this._isRestarting = true; this._isReady = false; this._statusChanged.emit('restarting'); try { await ((_c = (_b = this.session) === null || _b === void 0 ? void 0 : _b.kernel) === null || _c === void 0 ? void 0 : _c.restart()); this._isReady = true; } catch (e) { console.error(e); } this._isRestarting = false; this._statusChanged.emit(((_e = (_d = this.session) === null || _d === void 0 ? void 0 : _d.kernel) === null || _e === void 0 ? void 0 : _e.status) || 'unknown'); this._kernelChanged.emit({ name: 'kernel', oldValue: kernel, newValue: ((_f = this.session) === null || _f === void 0 ? void 0 : _f.kernel) || null }); } /** * Change the current kernel associated with the session. */ async changeKernel(options = {}) { if (this.isDisposed) { throw new Error('Disposed'); } // Wait for the initialization method to try // and start its kernel first to ensure consistent // ordering. await this._initStarted.promise; return this._changeKernel(options); } /** * Kill the kernel and shutdown the session. * * @returns A promise that resolves when the session is shut down. */ async shutdown() { if (this.isDisposed || !this._initializing) { return; } await this._initStarted.promise; this._pendingSessionRequest = ''; this._pendingKernelName = this.noKernelName; return this._shutdownSession(); } /** * Initialize the session context * * @returns A promise that resolves with whether to ask the user to select a kernel. * * #### Notes * If a server session exists on the current path, we will connect to it. * If preferences include disabling `canStart` or `shouldStart`, no * server session will be started. * If a kernel id is given, we attempt to start a session with that id. * If a default kernel is available, we connect to it. * Otherwise we ask the user to select a kernel. */ async initialize() { if (this._initializing) { return this._initPromise.promise; } this._initializing = true; const needsSelection = await this._initialize(); if (!needsSelection) { this._isReady = true; this._ready.resolve(undefined); } if (!this._pendingSessionRequest) { this._initStarted.resolve(void 0); } this._initPromise.resolve(needsSelection); return needsSelection; } /** * Inner initialize function that doesn't handle promises. * This makes it easier to consolidate promise handling logic. */ async _initialize() { const manager = this.sessionManager; await manager.ready; await manager.refreshRunning(); const model = find(manager.running(), item => { return item.path === this._path; }); if (model) { try { const session = manager.connectTo({ model }); this._handleNewSession(session); } catch (err) { void this._handleSessionError(err); return Promise.reject(err); } } return await this._startIfNecessary(); } /** * Shut down the current session. */ async _shutdownSession() { var _a; const session = this._session; // Capture starting values in case an error is raised. const isTerminating = this._isTerminating; const isReady = this._isReady; this._isTerminating = true; this._isReady = false; this._statusChanged.emit('terminating'); try { await (session === null || session === void 0 ? void 0 : session.shutdown()); this._isTerminating = false; session === null || session === void 0 ? void 0 : session.dispose(); this._session = null; const kernel = (session === null || session === void 0 ? void 0 : session.kernel) || null; this._statusChanged.emit('unknown'); this._kernelChanged.emit({ name: 'kernel', oldValue: kernel, newValue: null }); this._sessionChanged.emit({ name: 'session', oldValue: session, newValue: null }); } catch (err) { this._isTerminating = isTerminating; this._isReady = isReady; const status = (_a = session === null || session === void 0 ? void 0 : session.kernel) === null || _a === void 0 ? void 0 : _a.status; if (status === undefined) { this._statusChanged.emit('unknown'); } else { this._statusChanged.emit(status); } throw err; } return; } /** * Start the session if necessary. * * @returns Whether to ask the user to pick a kernel. */ async _startIfNecessary() { var _a; const preference = this.kernelPreference; if (this.isDisposed || ((_a = this.session) === null || _a === void 0 ? void 0 : _a.kernel) || preference.shouldStart === false || preference.canStart === false) { // Not necessary to start a kernel return false; } return this.startKernel(); } /** * Change the kernel. */ async _changeKernel(model = {}) { if (model.name) { this._pendingKernelName = model.name; } if (!this._session) { this._kernelChanged.emit({ name: 'kernel', oldValue: null, newValue: null }); } // Guarantee that the initialized kernel // will be started first. if (!this._pendingSessionRequest) { this._initStarted.resolve(void 0); } // If we already have a session, just change the kernel. if (this._session && !this._isTerminating) { try { await this._session.changeKernel(model); return this._session.kernel; } catch (err) { void this._handleSessionError(err); throw err; } } // Use a UUID for the path to overcome a race condition on the server // where it will re-use a session for a given path but only after // the kernel finishes starting. // We later switch to the real path below. // Use the correct directory so the kernel will be started in that directory. const dirName = PathExt.dirname(this._path); const requestId = (this._pendingSessionRequest = PathExt.join(dirName, UUID.uuid4())); try { this._statusChanged.emit('starting'); const session = await this.sessionManager.startNew({ path: requestId, type: this._type, name: this._name, kernel: model }); // Handle a preempt. if (this._pendingSessionRequest !== session.path) { await session.shutdown(); session.dispose(); return null; } // Change to the real path. await session.setPath(this._path); // Update the name in case it has changed since we launched the session. await session.setName(this._name); if (this._session && !this._isTerminating) { await this._shutdownSession(); } return this._handleNewSession(session); } catch (err) { void this._handleSessionError(err); throw err; } } /** * Handle a new session object. */ _handleNewSession(session) { var _a, _b, _c; if (this.isDisposed) { throw Error('Disposed'); } if (!this._isReady) { this._isReady = true; this._ready.resolve(undefined); } if (this._session) { this._session.dispose(); } this._session = session; this._pendingKernelName = ''; if (session) { this._prevKernelName = (_b = (_a = session.kernel) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : ''; session.disposed.connect(this._onSessionDisposed, this); session.propertyChanged.connect(this._onPropertyChanged, this); session.kernelChanged.connect(this._onKernelChanged, this); session.statusChanged.connect(this._onStatusChanged, this); session.connectionStatusChanged.connect(this._onConnectionStatusChanged, this); session.pendingInput.connect(this._onPendingInput, this); session.iopubMessage.connect(this._onIopubMessage, this); session.unhandledMessage.connect(this._onUnhandledMessage, this); if (session.path !== this._path) { this._onPropertyChanged(session, 'path'); } if (session.name !== this._name) { this._onPropertyChanged(session, 'name'); } if (session.type !== this._type) { this._onPropertyChanged(session, 'type'); } } // Any existing session/kernel connection was disposed above when the session was // disposed, so the oldValue should be null. this._sessionChanged.emit({ name: 'session', oldValue: null, newValue: session }); this._kernelChanged.emit({ oldValue: null, newValue: (session === null || session === void 0 ? void 0 : session.kernel) || null, name: 'kernel' }); this._statusChanged.emit(((_c = session === null || session === void 0 ? void 0 : session.kernel) === null || _c === void 0 ? void 0 : _c.status) || 'unknown'); return (session === null || session === void 0 ? void 0 : session.kernel) || null; } /** * Handle an error in session startup. */ async _handleSessionError(err) { this._handleNewSession(null); let traceback = ''; let message = ''; try { traceback = err.traceback; message = err.message; } catch (err) { // no-op } await this._displayKernelError(message, traceback); } /** * Display kernel error */ async _displayKernelError(message, traceback) { const body = (React.createElement("div", null, message && React.createElement("pre", null, message), traceback && (React.createElement("details", { className: "jp-mod-wide" }, React.createElement("pre", null, traceback))))); const dialog = (this._dialog = new Dialog({ title: this._trans.__('Error Starting Kernel'), body, buttons: [Dialog.okButton()] })); await dialog.launch(); this._dialog = null; } /** * Handle a session termination. */ _onSessionDisposed() { if (this._session) { const oldValue = this._session; this._session = null; const newValue = this._session; this._sessionChanged.emit({ name: 'session', oldValue, newValue }); } } /** * Handle a change to a session property. */ _onPropertyChanged(sender, property) { switch (property) { case 'path': this._path = sender.path; break; case 'name': this._name = sender.name; break; case 'type': this._type = sender.type; break; default: throw new Error(`unrecognized property ${property}`); } this._propertyChanged.emit(property); } /** * Handle a change to the kernel. */ _onKernelChanged(sender, args) { this._kernelChanged.emit(args); } /** * Handle a change to the session status. */ _onStatusChanged(sender, status) { var _a; if (status === 'dead') { const model = (_a = sender.kernel) === null || _a === void 0 ? void 0 : _a.model; if (model === null || model === void 0 ? void 0 : model.reason) { const traceback = model.traceback || ''; void this._displayKernelError(model.reason, traceback); } } // Set that this kernel is busy, if we haven't already // If we have already, and now we aren't busy, dispose // of the busy disposable. if (this._setBusy) { if (status === 'busy') { if (!this._busyDisposable) { this._busyDisposable = this._setBusy(); } } else { if (this._busyDisposable) { this._busyDisposable.dispose(); this._busyDisposable = null; } } } // Proxy the signal this._statusChanged.emit(status); } /** * Handle a change to the session status. */ _onConnectionStatusChanged(sender, status) { // Proxy the signal this._connectionStatusChanged.emit(status); } /** * Handle a change to the pending input. */ _onPendingInput(sender, value) { // Set the signal value this._pendingInput = value; } /** * Handle an iopub message. */ _onIopubMessage(sender, message) { if (message.header.msg_type === 'shutdown_reply') { this.session.kernel.removeInputGuard(); } this._iopubMessage.emit(message); } /** * Handle an unhandled message. */ _onUnhandledMessage(sender, message) { this._unhandledMessage.emit(message); } } /** * A namespace for `SessionContext` statics. */ (function (SessionContext) { /** * Get the default kernel name given select options. */ function getDefaultKernel(options) { const { preference } = options; const { shouldStart } = preference; if (shouldStart === false) { return null; } return Private.getDefaultKernel(options); } SessionContext.getDefaultKernel = getDefaultKernel; })(SessionContext || (SessionContext = {})); /** * The default implementation of the client session dialog provider. */ export class SessionContextDialogs { constructor(options = {}) { var _a; this._translator = (_a = options.translator) !== null && _a !== void 0 ? _a : nullTranslator; this._settingRegistry = options.settingRegistry || null; } /** * Select a kernel for the session. */ async selectKernel(sessionContext) { if (sessionContext.isDisposed) { return Promise.resolve(); } const translator = this._translator; const trans = translator.load('jupyterlab'); // If there is no existing kernel, offer the option to keep no kernel. let label = trans.__('Cancel'); if (sessionContext.hasNoKernel) { label = sessionContext.kernelDisplayName; } const buttons = [ Dialog.cancelButton({ label }), Dialog.okButton({ label: trans.__('Select'), ariaLabel: trans.__('Select Kernel') }) ]; const autoStartDefault = sessionContext.kernelPreference.autoStartDefault; const hasCheckbox = typeof autoStartDefault === 'boolean'; const dialog = new Dialog({ title: trans.__('Select Kernel'), body: Private.createKernelSelector(sessionContext, translator), buttons, checkbox: hasCheckbox ? { label: trans.__('Always start the preferred kernel'), caption: trans.__('Remember my choice and always start the preferred kernel'), checked: autoStartDefault } : null }); const result = await dialog.launch(); if (sessionContext.isDisposed || !result.button.accept) { return; } if (hasCheckbox && result.isChecked !== null) { sessionContext.kernelPreference = { ...sessionContext.kernelPreference, autoStartDefault: result.isChecked }; } const model = result.value; if (model === null && !sessionContext.hasNoKernel) { return sessionContext.shutdown(); } if (model) { await sessionContext.changeKernel(model); } } /** * Restart the session. * * @returns A promise that resolves with whether the kernel has restarted. * * #### Notes * If there is a running kernel, present a dialog. * If there is no kernel, we start a kernel with the last run * kernel name and resolves with `true`. */ async restart(sessionContext) { var _a, _b, _c, _d, _e; const trans = this._translator.load('jupyterlab'); await sessionContext.initialize(); if (sessionContext.isDisposed) { throw new Error('session already disposed'); } const kernel = (_a = sessionContext.session) === null || _a === void 0 ? void 0 : _a.kernel; if (!kernel && sessionContext.prevKernelName) { await sessionContext.changeKernel({ name: sessionContext.prevKernelName }); return true; } // Bail if there is no previous kernel to start. if (!kernel) { throw new Error('No kernel to restart'); } // Skip the dialog and restart the kernel const kernelPluginId = '@jupyterlab/apputils-extension:sessionDialogs'; const skipKernelRestartDialog = (_c = (_b = sessionContext.kernelPreference) === null || _b === void 0 ? void 0 : _b.skipKernelRestartDialog) !== null && _c !== void 0 ? _c : false; const skipKernelRestartDialogSetting = (_e = (await ((_d = this._settingRegistry) === null || _d === void 0 ? void 0 : _d.get(kernelPluginId, 'skipKernelRestartDialog')))) === null || _e === void 0 ? void 0 : _e.composite; if (skipKernelRestartDialogSetting || skipKernelRestartDialog) { await sessionContext.restartKernel(); return true; } const restartBtn = Dialog.warnButton({ label: trans.__('Restart'), ariaLabel: trans.__('Confirm Kernel Restart') }); const result = await showDialog({ title: trans.__('Restart Kernel?'), body: trans.__('Do you want to restart the kernel of %1? All variables will be lost.', sessionContext.name), buttons: [ Dialog.cancelButton({ ariaLabel: trans.__('Cancel Kernel Restart') }), restartBtn ], checkbox: { label: trans.__('Do not ask me again.'), caption: trans.__('If checked, the kernel will restart without confirmation prompt in the future; you can change this back in the settings.') } }); if (kernel.isDisposed) { return false; } if (result.button.accept) { if (typeof result.isChecked === 'boolean' && result.isChecked == true) { sessionContext.kernelPreference = { ...sessionContext.kernelPreference, skipKernelRestartDialog: true }; } await sessionContext.restartKernel(); return true; } return false; } } (function (SessionContextDialogs) { /** * Returns available kernel options grouped based on session context. * * #### Notes * If a language preference is set in the given session context, the options * returned are grouped with the language preference at the top: * * - (Start %1 Kernel, language) * - { all kernelspecs whose language matches in alphabetical order } * - (Use No Kernel) * - `No Kernel` * - (Start Kernel) * - { all other kernelspecs in alphabetical order } * - (Connect to Existing %1 Kernel, language) * - { all running kernels whose language matches in alphabetical order } * - (Connect to Kernel) * - { all other running kernels in alphabetical order } * * If no language preference is set, these groups and options are returned: * * - (Start Kernel) * - { all kernelspecs in alphabetical order } * - (Use No Kernel) * - `No Kernel` * - (Connect to Existing Kernel) * - { all running kernels in alphabetical order } * * If the session has a kernel ID and a kernel exists with that id, its * corresponding option has `selected` set to `true`. Otherwise if the session * context language preference is set, the first kernelspec that matches it is * selected. */ function kernelOptions(sessionContext, translator = null) { var _a, _b, _c, _d, _e, _f, _g; const options = { disabled: false, groups: [] }; // Create mapping of sessions and kernel ids. const kernels = Array.from((_b = (_a = sessionContext.kernelManager) === null || _a === void 0 ? void 0 : _a.running()) !== null && _b !== void 0 ? _b : // If kernel manager is unavailable use kernels from running sessions. // TODO: Remove this (next version) when kernel manager is guaranteed. Array.from(sessionContext.sessionManager.running()) .filter(session => !!session.kernel) .map(session => session.kernel)); const sessions = Array.from((_c = sessionContext.sessionManager.running()) !== null && _c !== void 0 ? _c : []).reduce((sessions, session) => { var _a; if ((_a = session.kernel) === null || _a === void 0 ? void 0 : _a.id) sessions[session.kernel.id] = session; return sessions; }, {}); const preference = { ...sessionContext.kernelPreference, id: (_e = (_d = sessionContext.session) === null || _d === void 0 ? void 0 : _d.kernel) === null || _e === void 0 ? void 0 : _e.id }; const currentKernelDisplayName = !sessionContext.hasNoKernel ? sessionContext.kernelDisplayName : null; const specs = { default: '', kernelspecs: Object.create(null), ...sessionContext.specsManager.specs }; // Create mapping of languages and kernel names. const sorted = []; const languages = Object.create(null); for (const name in specs.kernelspecs) { sorted.push(specs.kernelspecs[name]); languages[name] = specs.kernelspecs[name].language; } sorted.sort((a, b) => a.display_name.localeCompare(b.display_name)); translator = translator || nullTranslator; const trans = translator.load('jupyterlab'); const language = preference.language || languages[preference.name] || (preference.id ? languages[(_f = sessions[preference.id]) === null || _f === void 0 ? void 0 : _f.name] : ''); const labels = { connectKernel: trans.__('Connect to Existing Kernel'), startPreferred: trans.__('Start %1 Kernel', language), startOther: trans.__('Start Kernel'), connectToPreferred: trans.__('Connect to Existing %1 Kernel', language), connectToOther: trans.__('Connect to Other Kernel'), noKernel: trans.__('No Kernel'), startKernel: trans.__('Start Kernel'), useNoKernel: trans.__('Use No Kernel') }; const noKernel = { label: labels.useNoKernel, options: [ { text: labels.noKernel, title: labels.noKernel, value: JSON.stringify(null) } ] }; const optionForKernel = (kernel, displayName, session) => { const sessionName = session ? session.name || PathExt.basename(session.path) : kernel.name || trans.__('Unknown Kernel'); return { text: `${sessionName} (${kernel.id.split('-')[0]})`, title: (session ? `${trans.__('Path: %1', session.path)}\n` : ``) + `${trans.__('Name: %1', sessionName)}\n` + `${trans.__('Kernel Name: %1', displayName !== null && displayName !== void 0 ? displayName : kernel.name)}\n` + `${trans.__('Kernel Id: %1', kernel.id)}`, value: JSON.stringify({ id: kernel.id }) }; }; const optionForSpec = (spec) => ({ text: spec.display_name, value: JSON.stringify({ name: spec.name }) }); // If a kernel cannot be started, bail. if (preference.canStart === false) { options.disabled = true; options.groups.push(noKernel); return options; } // Create kernel option groups based on whether language preference exists. if (language) { // Add all kernelspecs, separating out the preferred language first. const preferred = { label: labels.startPreferred, options: [] }; const other = { label: labels.startOther, options: [] }; const preferredRunning = { label: labels.connectToPreferred, options: [] }; const otherRunning = { label: labels.connectToOther, options: [] }; for (const spec of sorted) { (spec.language === language ? preferred : other).options.push(optionForSpec(spec)); } options.groups.push(preferred); options.groups.push(noKernel); options.groups.push(other); kernels .map(kernel => { var _a, _b; return ({ option: optionForKernel(kernel, (_b = (_a = specs.kernelspecs[kernel.name]) === null || _a === void 0 ? void 0 : _a.display_name) !== null && _b !== void 0 ? _b : '', sessions[kernel.id]), language: languages[kernel.name] }); }) .sort((a, b) => a.option.text.localeCompare(b.option.text)) .forEach(kernel => (language === kernel.language ? preferredRunning : otherRunning).options.push(kernel.option)); if (preferredRunning.options.length) options.groups.push(preferredRunning); if (otherRunning.options.length) options.groups.push(otherRunning); } else { // Add kernelspecs first. options.groups.push({ label: labels.startKernel, options: sorted.map(spec => optionForSpec(spec)) }); // Next add the no kernel option. options.groups.push(noKernel); // Add running kernels. options.groups.push({ label: labels.connectKernel, options: kernels .map(kernel => { var _a, _b; return optionForKernel(kernel, (_b = (_a = specs.kernelspecs[kernel.name]) === null || _a === void 0 ? void 0 : _a.display_name) !== null && _b !== void 0 ? _b : '', sessions[kernel.id]); }) .sort((a, b) => a.text.localeCompare(b.text)) }); } // Set the selected option. if (preference.id || currentKernelDisplayName || preference.name) { for (const group of options.groups) { for (const option of group.options) { const choice = JSON.parse(option.value); if (!choice) continue; if (preference.id) { if (preference.id === choice.id) { option.selected = true; return options; } continue; } if (currentKernelDisplayName) { if (currentKernelDisplayName === ((_g = specs.kernelspecs[choice.name]) === null || _g === void 0 ? void 0 : _g.display_name)) { option.selected = true; return options; } continue; } if (preference.name) { if (preference.name === choice.name) { option.selected = true; return options; } continue; } } } } return options; } SessionContextDialogs.kernelOptions = kernelOptions; })(SessionContextDialogs || (SessionContextDialogs = {})); /** * The namespace for module private data. */ var Private; (function (Private) { /** * Return a kernel selector widget. */ Private.createKernelSelector = (sessionContext, translator) => new KernelSelector({ node: createSelectorNode(sessionContext, translator) }); /** * A widget that provides a kernel selection. */ class KernelSelector extends Widget { /** * Get the value of the kernel selector widget. */ getValue() { const selector = this.node.querySelector('select'); return JSON.parse(selector.value); } } /** * Create an HTML node for a kernel selector widget. */ function createSelectorNode(sessionContext, translator) { // Create the dialog body. translator = translator || nullTranslator; const trans = translator.load('jupyterlab'); const body = document.createElement('div'); const text = document.createElement('label'); text.textContent = `${trans.__('Select kernel for:')} "${sessionContext.name}"`; body.appendChild(text); const select = document.createElement('select'); const options = SessionContextDialogs.kernelOptions(sessionContext, translator); if (options.disabled) select.disabled = true; for (const group of options.groups) { const { label, options } = group; const optgroup = document.createElement('optgroup'); optgroup.label = label; for (const { selected, text, title, value } of options) { const option = document.createElement('option'); if (selected) option.selected = true; if (title) option.title = title; option.text = text; option.value = value; optgroup.appendChild(option); } select.appendChild(optgroup); } body.appendChild(select); return body; } /** * Get the default kernel name given select options. */ function getDefaultKernel(options) { var _a; const { specs, preference } = options; const { name, language, canStart, autoStartDefault } = preference; if (!specs || canStart === false) { return null; } const defaultName = autoStartDefault ? specs.default : null; if (!name && !language) { return defaultName; } // Look for an exact match of a spec name. for (const specName in specs.kernelspecs) { if (specName === name) { return name; } } // Bail if there is no language. if (!language) { return defaultName; } // Check for a single kernel matching the language. const matches = []; for (const specName in specs.kernelspecs) { const kernelLanguage = (_a = specs.kernelspecs[specName]) === null || _a === void 0 ? void 0 : _a.language; if (language === kernelLanguage) { matches.push(specName); } } if (matches.length === 1) { const specName = matches[0]; console.warn('No exact match found for ' + specName + ', using kernel ' + specName + ' that matches ' + 'language=' + language); return specName; } // No matches found. return defaultName; } Private.getDefaultKernel = getDefaultKernel; })(Private || (Private = {})); //# sourceMappingURL=sessioncontext.js.map