diff --git a/news/2 Fixes/11752.md b/news/2 Fixes/11752.md new file mode 100644 index 000000000000..a0b52754e1a3 --- /dev/null +++ b/news/2 Fixes/11752.md @@ -0,0 +1 @@ +When switching to an invalid kernel (one that is registered but cannot start) in raw mode respect the launch timeout that is passed in. \ No newline at end of file diff --git a/package.nls.json b/package.nls.json index 22da0d5060f5..0da7d3e53b31 100644 --- a/package.nls.json +++ b/package.nls.json @@ -494,5 +494,6 @@ "DataScience.libraryRequiredToLaunchJupyterKernelNotInstalledInterpreter": "{0} requires {1} to be installed.", "DataScience.runByLine": "Run by line", "DataScience.continueRunByLine": "Stop", - "DataScience.couldNotInstallLibrary": "Could not install {0}. If pip is not available, please use the package manager of your choice to manually install this library into your Python environment." + "DataScience.couldNotInstallLibrary": "Could not install {0}. If pip is not available, please use the package manager of your choice to manually install this library into your Python environment.", + "DataScience.rawKernelSessionFailed": "Unable to start session for kernel {0}. Select another kernel to launch with." } diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index ccd477fa6e79..2da2df56bd49 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -939,6 +939,10 @@ export namespace DataScience { export const kernelStarted = localize('DataScience.kernelStarted', 'Started kernel {0}.'); export const runByLine = localize('DataScience.runByLine', 'Run by line'); export const continueRunByLine = localize('DataScience.continueRunByLine', 'Stop'); + export const rawKernelSessionFailed = localize( + 'DataScience.rawKernelSessionFailed', + 'Unable to start session for kernel {0}. Select another kernel to launch with.' + ); } export namespace DebugConfigStrings { diff --git a/src/client/datascience/jupyter/kernels/kernelSwitcher.ts b/src/client/datascience/jupyter/kernels/kernelSwitcher.ts index a319908c58ac..71ba27bfb292 100644 --- a/src/client/datascience/jupyter/kernels/kernelSwitcher.ts +++ b/src/client/datascience/jupyter/kernels/kernelSwitcher.ts @@ -11,6 +11,7 @@ import { Common, DataScience } from '../../../common/utils/localize'; import { StopWatch } from '../../../common/utils/stopWatch'; import { JupyterSessionStartError } from '../../baseJupyterSession'; import { Commands, Settings } from '../../constants'; +import { RawKernelSessionStartError } from '../../raw-kernel/rawJupyterSession'; import { IJupyterConnection, IJupyterKernelSpec, @@ -125,7 +126,9 @@ export class KernelSwitcher { } catch (ex) { if ( isLocalConnection && - (ex instanceof JupyterSessionStartError || ex instanceof JupyterInvalidKernelError) + (ex instanceof JupyterSessionStartError || + ex instanceof JupyterInvalidKernelError || + ex instanceof RawKernelSessionStartError) ) { // Looks like we were unable to start a session for the local connection. // Possibly something wrong with the kernel. diff --git a/src/client/datascience/raw-kernel/rawJupyterSession.ts b/src/client/datascience/raw-kernel/rawJupyterSession.ts index 8fc77e3a3d97..9e2556e2ea8a 100644 --- a/src/client/datascience/raw-kernel/rawJupyterSession.ts +++ b/src/client/datascience/raw-kernel/rawJupyterSession.ts @@ -22,6 +22,13 @@ import { ReportableAction } from '../progress/types'; import { RawSession } from '../raw-kernel/rawSession'; import { IJupyterKernelSpec, ISessionWithSocket } from '../types'; +// Error thrown when we are unable to start a raw kernel session +export class RawKernelSessionStartError extends Error { + constructor(kernelTitle: string) { + super(localize.DataScience.rawKernelSessionFailed().format(kernelTitle)); + } +} + /* RawJupyterSession is the implementation of IJupyterSession that instead of connecting to JupyterLab services it instead connects to a kernel directly @@ -91,7 +98,7 @@ export class RawJupyterSession extends BaseJupyterSession { } else if (newSession === null) { sendTelemetryEvent(Telemetry.RawKernelSessionStartTimeout); traceError('Raw session failed to start in given timeout'); - throw new Error(localize.DataScience.sessionDisposed()); + throw new RawKernelSessionStartError(kernelSpec.display_name || kernelSpec.name); } else { sendTelemetryEvent(Telemetry.RawKernelSessionStartSuccess); traceInfo('Raw session started and connected'); @@ -121,7 +128,7 @@ export class RawJupyterSession extends BaseJupyterSession { public async createNewKernelSession( kernel: IJupyterKernelSpec | LiveKernelModel, - _timeoutMS: number, + timeoutMS: number, interpreter?: PythonInterpreter ): Promise { if (!kernel || 'session' in kernel) { @@ -131,7 +138,13 @@ export class RawJupyterSession extends BaseJupyterSession { this.outputChannel.appendLine(localize.DataScience.kernelStarted().format(kernel.display_name || kernel.name)); - return this.startRawSession(kernel, interpreter); + const newSession = await waitForPromise(this.startRawSession(kernel, interpreter), timeoutMS); + + if (!newSession) { + throw new RawKernelSessionStartError(kernel.display_name || kernel.name); + } + + return newSession; } protected shutdownSession( @@ -209,6 +222,9 @@ export class RawJupyterSession extends BaseJupyterSession { // Create our raw session, it will own the process lifetime const result = new RawSession(process); + // When our kernel connects and gets a status message it triggers the ready promise + await result.kernel.ready; + // So that we don't have problems with ipywidgets, always register the default ipywidgets comm target. // Restart sessions and retries might make this hard to do correctly otherwise. result.kernel.registerCommTarget(Identifiers.DefaultCommTarget, noop);