From 3fd938923f3ca4ae8f65c2d5a93ab27ee1feb833 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 18 Nov 2021 10:21:37 -0800 Subject: [PATCH 1/4] Misc --- src/client/datascience/errors/errorHandler.ts | 89 +++++++++++++++---- .../jupyter/kernels/cellExecutionQueue.ts | 3 + .../datascience/jupyter/kernels/kernel.ts | 15 ++-- .../jupyter/kernels/kernelExecution.ts | 4 +- .../datascience/jupyter/kernels/types.ts | 1 + src/client/datascience/types.ts | 3 +- .../datascience/errorHandler.unit.test.ts | 6 +- 7 files changed, 94 insertions(+), 27 deletions(-) diff --git a/src/client/datascience/errors/errorHandler.ts b/src/client/datascience/errors/errorHandler.ts index 8450bc2a0b9..1312dcfcc72 100644 --- a/src/client/datascience/errors/errorHandler.ts +++ b/src/client/datascience/errors/errorHandler.ts @@ -18,7 +18,7 @@ import { IKernelDependencyService, KernelInterpreterDependencyResponse } from '../types'; -import { CancellationError as VscCancellationError, CancellationTokenSource, ConfigurationTarget } from 'vscode'; +import { CancellationError as VscCancellationError, CancellationTokenSource, ConfigurationTarget, NotebookCell, NotebookCellOutput, NotebookCellOutputItem } from 'vscode'; import { CancellationError } from '../../common/cancellation'; import { KernelConnectionTimeoutError } from './kernelConnectionTimeoutError'; import { KernelDiedError } from './kernelDiedError'; @@ -30,12 +30,16 @@ import { getErrorMessageFromPythonTraceback, KernelFailureReason } from '../../common/errors/errorUtils'; -import { KernelConnectionMetadata } from '../jupyter/kernels/types'; +import { IKernelProvider, KernelConnectionMetadata } from '../jupyter/kernels/types'; import { getDisplayPath } from '../../common/platform/fs-paths'; import { IBrowserService, IConfigurationService, Resource } from '../../common/types'; import { Telemetry } from '../constants'; import { sendTelemetryEvent } from '../../telemetry'; import { DisplayOptions } from '../displayOptions'; +import { IServiceContainer } from '../../ioc/types'; +import { StopWatch } from '../../common/utils/stopWatch'; +import { sleep } from '../../common/utils/async'; +import { INotebookControllerManager } from '../notebook/types'; @injectable() export class DataScienceErrorHandler implements IDataScienceErrorHandler { @@ -46,8 +50,9 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { @inject(IWorkspaceService) private readonly workspace: IWorkspaceService, @inject(IBrowserService) private readonly browser: IBrowserService, @inject(IConfigurationService) private readonly configuration: IConfigurationService, - @inject(IKernelDependencyService) private readonly kernelDependency: IKernelDependencyService - ) {} + @inject(IKernelDependencyService) private readonly kernelDependency: IKernelDependencyService, + @inject(IServiceContainer) private readonly serviceContainer: IServiceContainer + ) { } public async handleError(err: Error): Promise { traceError('DataScience Error', err); await this.handleErrorImplementation(err); @@ -57,9 +62,10 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { err: Error, purpose: 'start' | 'restart' | 'interrupt' | 'execution', kernelConnection: KernelConnectionMetadata, - resource: Resource + resource: Resource, + cellToDisplayErrors?: NotebookCell ): Promise { - await this.handleErrorImplementation(err, purpose, async (error: BaseError, defaultErrorMessage?: string) => { + await this.handleErrorImplementation(err, purpose, cellToDisplayErrors, async (error: BaseError, defaultErrorMessage?: string) => { if ( err instanceof IpyKernelNotInstalledError && err.reason === KernelInterpreterDependencyResponse.uiHidden && @@ -92,7 +98,8 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { DataScience.fileSeemsToBeInterferingWithKernelStartup().format( getDisplayPath(failureInfo.fileName, this.workspace.workspaceFolders || []) ), - 'https://aka.ms/kernelFailuresOverridingBuiltInModules' + 'https://aka.ms/kernelFailuresOverridingBuiltInModules', + cellToDisplayErrors ); break; } @@ -121,7 +128,8 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { } else { await this.showMessageWithMoreInfo( DataScience.failedToStartKernelDueToMissingModule().format(failureInfo.moduleName), - 'https://aka.ms/kernelFailuresMissingModule' + 'https://aka.ms/kernelFailuresMissingModule', + cellToDisplayErrors ); } break; @@ -136,12 +144,14 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { failureInfo.moduleName, fileName ), - 'https://aka.ms/kernelFailuresModuleImportErrFromFile' + 'https://aka.ms/kernelFailuresModuleImportErrFromFile', + cellToDisplayErrors ); } else { await this.showMessageWithMoreInfo( DataScience.failedToStartKernelDueToImportFailure().format(failureInfo.moduleName), - 'https://aka.ms/kernelFailuresModuleImportErr' + 'https://aka.ms/kernelFailuresModuleImportErr', + cellToDisplayErrors ); } break; @@ -150,47 +160,55 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { const message = failureInfo.moduleName ? DataScience.failedToStartKernelDueToDllLoadFailure().format(failureInfo.moduleName) : DataScience.failedToStartKernelDueToUnknowDllLoadFailure(); - await this.showMessageWithMoreInfo(message, 'https://aka.ms/kernelFailuresDllLoad'); + await this.showMessageWithMoreInfo(message, 'https://aka.ms/kernelFailuresDllLoad', + cellToDisplayErrors); break; } case KernelFailureReason.importWin32apiFailure: { await this.showMessageWithMoreInfo( DataScience.failedToStartKernelDueToWin32APIFailure(), - 'https://aka.ms/kernelFailuresWin32Api' + 'https://aka.ms/kernelFailuresWin32Api', + cellToDisplayErrors ); break; } case KernelFailureReason.zmqModuleFailure: { await this.showMessageWithMoreInfo( DataScience.failedToStartKernelDueToPyZmqFailure(), - 'https://aka.ms/kernelFailuresPyzmq' + 'https://aka.ms/kernelFailuresPyzmq', + cellToDisplayErrors ); break; } case KernelFailureReason.oldIPythonFailure: { await this.showMessageWithMoreInfo( DataScience.failedToStartKernelDueToOldIPython(), - 'https://aka.ms/kernelFailuresOldIPython' + 'https://aka.ms/kernelFailuresOldIPython', + cellToDisplayErrors ); break; } case KernelFailureReason.oldIPyKernelFailure: { await this.showMessageWithMoreInfo( DataScience.failedToStartKernelDueToOldIPyKernel(), - 'https://aka.ms/kernelFailuresOldIPyKernel' + 'https://aka.ms/kernelFailuresOldIPyKernel', + cellToDisplayErrors ); break; } default: if (defaultErrorMessage) { + void this.displayErrorsInCell(defaultErrorMessage, cellToDisplayErrors); await this.applicationShell.showErrorMessage(defaultErrorMessage); } } }); } - private async showMessageWithMoreInfo(message: string, moreInfoLink: string) { + private async showMessageWithMoreInfo(message: string, moreInfoLink: string, cellToDisplayErrors?: NotebookCell) { + message = `${message} \n${DataScience.viewJupyterLogForFurtherInfo()}`; + void this.displayErrorsInCell(message, cellToDisplayErrors); await this.applicationShell - .showErrorMessage(`${message} \n${DataScience.viewJupyterLogForFurtherInfo()}`, Common.learnMore()) + .showErrorMessage(message, Common.learnMore()) .then((selection) => { if (selection === Common.learnMore()) { this.browser.launch(moreInfoLink); @@ -200,6 +218,7 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { private async handleErrorImplementation( err: Error, purpose?: 'start' | 'restart' | 'interrupt' | 'execution', + cellToDisplayErrors?: NotebookCell, handler?: (error: BaseError, defaultErrorMessage?: string) => Promise ): Promise { const errorPrefix = getErrorMessagePrefix(purpose); @@ -242,6 +261,7 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { // Don't show the message for cancellation errors traceWarning(`Cancelled by user`, err); } else if (err instanceof KernelConnectionTimeoutError || err instanceof KernelPortNotUsedTimeoutError) { + void this.displayErrorsInCell(err.message, cellToDisplayErrors); this.applicationShell.showErrorMessage(err.message).then(noop, noop); } else if ( err instanceof KernelDiedError || @@ -256,16 +276,49 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { if ((purpose === 'restart' || purpose === 'start') && handler) { await handler(err, defaultErrorMessage); } else { + void this.displayErrorsInCell(defaultErrorMessage, cellToDisplayErrors); this.applicationShell.showErrorMessage(defaultErrorMessage).then(noop, noop); } } else { // Some errors have localized and/or formatted error messages. + const message = getCombinedErrorMessage(errorPrefix, err.message || err.toString()); + void this.displayErrorsInCell(message, cellToDisplayErrors); this.applicationShell - .showErrorMessage(getCombinedErrorMessage(errorPrefix, err.message || err.toString())) + .showErrorMessage(message) .then(noop, noop); } traceError('DataScience Error', err); } + private async displayErrorsInCell(errorMessage: string, cellToDisplayErrors?: NotebookCell) { + if (!cellToDisplayErrors) { + return; + } + const associatedkernel = this.serviceContainer.get(IKernelProvider).get(cellToDisplayErrors.notebook); + if (!associatedkernel) { + return; + } + // Sometimes the cells are still running, wait for 1s for cells to finish & get cleared, + // Then display the error in the cell. + const stopWatch = new StopWatch(); + while (stopWatch.elapsedTime <= 2_000 || !associatedkernel.hasPendingCells) { + await sleep(100); + } + if (associatedkernel.hasPendingCells) { + return; + } + const controllers = this.serviceContainer.get(INotebookControllerManager); + const controller = controllers.getSelectedNotebookController(cellToDisplayErrors.notebook); + // Possible it changed. + if (!controller || controller.connection !== associatedkernel.kernelConnectionMetadata) { + return; + } + const execution = controller.controller.createNotebookCellExecution(cellToDisplayErrors); + execution.start(); + void execution.clearOutput(cellToDisplayErrors); + const output = new NotebookCellOutput([NotebookCellOutputItem.text(errorMessage, 'text/markdown')]) + void execution.appendOutput(output); + execution.end(undefined); + } } function getCombinedErrorMessage(prefix?: string, message?: string) { const errorMessage = [prefix || '', message || ''] diff --git a/src/client/datascience/jupyter/kernels/cellExecutionQueue.ts b/src/client/datascience/jupyter/kernels/cellExecutionQueue.ts index 55ded8f273b..f4d94bc5274 100644 --- a/src/client/datascience/jupyter/kernels/cellExecutionQueue.ts +++ b/src/client/datascience/jupyter/kernels/cellExecutionQueue.ts @@ -33,6 +33,9 @@ export class CellExecutionQueue implements Disposable { public get failed(): boolean { return this.cancelledOrCompletedWithErrors; } + public get queue(): Readonly { + return this.queueOfCellsToExecute.map(cell => cell.cell); + } constructor( private readonly session: Promise, private readonly executionFactory: CellExecutionFactory, diff --git a/src/client/datascience/jupyter/kernels/kernel.ts b/src/client/datascience/jupyter/kernels/kernel.ts index 87ef7b25e50..f61eaa0cafc 100644 --- a/src/client/datascience/jupyter/kernels/kernel.ts +++ b/src/client/datascience/jupyter/kernels/kernel.ts @@ -121,6 +121,9 @@ export class Kernel implements IKernel { public get session(): IJupyterSession | undefined { return this.notebook?.session; } + public get hasPendingCells() { + return this.kernelExecution.queue.length > 0; + } private _disposed?: boolean; private _disposing?: boolean; private _ignoreNotebookDisposedErrors?: boolean; @@ -177,7 +180,7 @@ export class Kernel implements IKernel { const isPreferredKernel = getResourceType(resourceUri) === 'notebook' ? notebookControllerManager.getPreferredNotebookController(this.notebookDocument)?.controller === - controller + controller : undefined; trackKernelResourceInformation(resourceUri, { kernelConnection: kernelConnectionMetadata, @@ -244,8 +247,8 @@ export class Kernel implements IKernel { this.notebook = this.notebook ? this.notebook : this._notebookPromise - ? await this._notebookPromise - : undefined; + ? await this._notebookPromise + : undefined; this._notebookPromise = undefined; const promises: Promise[] = []; if (this.notebook) { @@ -410,9 +413,10 @@ export class Kernel implements IKernel { // errors about startup failures. traceWarning(`Ignoring kernel startup failure as kernel was disposed`, ex); } else { + const cellForErrorDisplay = this.kernelExecution.queue.length ? this.kernelExecution.queue[0] : undefined; void this.errorHandler // eslint-disable-next-line @typescript-eslint/no-explicit-any - .handleKernelError(ex as any, 'start', this.kernelConnectionMetadata, this.resourceUri); // Just a notification, so don't await this + .handleKernelError(ex as any, 'start', this.kernelConnectionMetadata, this.resourceUri, cellForErrorDisplay); // Just a notification, so don't await this } traceError(`failed to start INotebook in kernel, UI Disabled = ${this.startupUI.disableUI}`, ex); this.startCancellation.cancel(); @@ -830,8 +834,7 @@ export async function executeSilently(session: IJupyterSession, code: string): P outputs.push(output); } else if (jupyterLab.KernelMessage.isErrorMsg(msg)) { traceInfoIfCI( - `Got io pub message (error), ${msg.content.ename},${ - msg.content.evalue + `Got io pub message (error), ${msg.content.ename},${msg.content.evalue }, ${msg.content.traceback.join().substring(0, 100)}}` ); const output: nbformat.IError = { diff --git a/src/client/datascience/jupyter/kernels/kernelExecution.ts b/src/client/datascience/jupyter/kernels/kernelExecution.ts index 69bf1953841..9677a892763 100644 --- a/src/client/datascience/jupyter/kernels/kernelExecution.ts +++ b/src/client/datascience/jupyter/kernels/kernelExecution.ts @@ -57,7 +57,9 @@ export class KernelExecution implements IDisposable { public get onPreExecute() { return this._onPreExecute.event; } - + public get queue(){ + return this.documentExecutions.get(this.kernel.notebookDocument)?.queue || []; + } public async executeCell( sessionPromise: Promise, cell: NotebookCell diff --git a/src/client/datascience/jupyter/kernels/types.ts b/src/client/datascience/jupyter/kernels/types.ts index 6beadb2459e..4c0323d7baf 100644 --- a/src/client/datascience/jupyter/kernels/types.ts +++ b/src/client/datascience/jupyter/kernels/types.ts @@ -108,6 +108,7 @@ export interface IKernel extends IAsyncDisposable { readonly onWillInterrupt: Event; readonly onPreExecute: Event; readonly status: KernelMessage.Status; + readonly hasPendingCells: boolean; readonly disposed: boolean; readonly disposing: boolean; /** diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index 4e1b5d1a2c3..badb6844ed6 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -389,7 +389,8 @@ export interface IDataScienceErrorHandler { err: Error, context: 'start' | 'restart' | 'interrupt' | 'execution', kernelConnection: KernelConnectionMetadata, - resource: Resource + resource: Resource, + cellToDisplayErrors?: NotebookCell ): Promise; } diff --git a/src/test/datascience/errorHandler.unit.test.ts b/src/test/datascience/errorHandler.unit.test.ts index 8754735afc2..6256bb0782e 100644 --- a/src/test/datascience/errorHandler.unit.test.ts +++ b/src/test/datascience/errorHandler.unit.test.ts @@ -16,6 +16,7 @@ import { KernelDiedError } from '../../client/datascience/errors/kernelDiedError import { KernelConnectionMetadata } from '../../client/datascience/jupyter/kernels/types'; import { IJupyterInterpreterDependencyManager, IKernelDependencyService } from '../../client/datascience/types'; import { getOSType, OSType } from '../common'; +import { IServiceContainer } from '../../client/ioc/types'; suite('DataScience Error Handler Unit Tests', () => { let applicationShell: IApplicationShell; @@ -25,12 +26,14 @@ suite('DataScience Error Handler Unit Tests', () => { let browser: IBrowserService; let configuration: IConfigurationService; let kernelDependencyInstaller: IKernelDependencyService; + let svcContainer: IServiceContainer; setup(() => { applicationShell = mock(); worksapceService = mock(); dependencyManager = mock(); configuration = mock(); browser = mock(); + svcContainer = mock(); kernelDependencyInstaller = mock(); when(dependencyManager.installMissingDependencies(anything())).thenResolve(); when(worksapceService.workspaceFolders).thenReturn([]); @@ -40,7 +43,8 @@ suite('DataScience Error Handler Unit Tests', () => { instance(worksapceService), instance(browser), instance(configuration), - instance(kernelDependencyInstaller) + instance(kernelDependencyInstaller), + instance(svcContainer) ); }); const message = 'Test error message.'; From afb80b44ebb77d4c4475d0560c7329df9bd4bdae Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 18 Nov 2021 12:41:18 -0800 Subject: [PATCH 2/4] Display kernel startup errors in output of a cell. --- src/client/datascience/errors/errorHandler.ts | 300 ++++++++++-------- .../datascience/errors/errorRendererComms.ts | 85 +++++ .../interactive-common/linkProvider.ts | 2 + .../interactive-window/interactiveWindow.ts | 80 +---- .../interactiveWindowProvider.ts | 2 - .../jupyter/kernels/cellExecutionQueue.ts | 3 +- .../kernel-launcher/kernelProcess.ts | 3 + src/client/datascience/serviceRegistry.ts | 2 + src/datascience-ui/error-renderer/index.ts | 21 +- 9 files changed, 276 insertions(+), 222 deletions(-) create mode 100644 src/client/datascience/errors/errorRendererComms.ts diff --git a/src/client/datascience/errors/errorHandler.ts b/src/client/datascience/errors/errorHandler.ts index 1312dcfcc72..cbdb2f28edd 100644 --- a/src/client/datascience/errors/errorHandler.ts +++ b/src/client/datascience/errors/errorHandler.ts @@ -18,7 +18,14 @@ import { IKernelDependencyService, KernelInterpreterDependencyResponse } from '../types'; -import { CancellationError as VscCancellationError, CancellationTokenSource, ConfigurationTarget, NotebookCell, NotebookCellOutput, NotebookCellOutputItem } from 'vscode'; +import { + CancellationError as VscCancellationError, + CancellationTokenSource, + ConfigurationTarget, + NotebookCell, + NotebookCellOutput, + NotebookCellOutputItem +} from 'vscode'; import { CancellationError } from '../../common/cancellation'; import { KernelConnectionTimeoutError } from './kernelConnectionTimeoutError'; import { KernelDiedError } from './kernelDiedError'; @@ -52,7 +59,7 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { @inject(IConfigurationService) private readonly configuration: IConfigurationService, @inject(IKernelDependencyService) private readonly kernelDependency: IKernelDependencyService, @inject(IServiceContainer) private readonly serviceContainer: IServiceContainer - ) { } + ) {} public async handleError(err: Error): Promise { traceError('DataScience Error', err); await this.handleErrorImplementation(err); @@ -65,155 +72,161 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { resource: Resource, cellToDisplayErrors?: NotebookCell ): Promise { - await this.handleErrorImplementation(err, purpose, cellToDisplayErrors, async (error: BaseError, defaultErrorMessage?: string) => { - if ( - err instanceof IpyKernelNotInstalledError && - err.reason === KernelInterpreterDependencyResponse.uiHidden && - (purpose === 'start' || purpose === 'restart') && - kernelConnection.interpreter - ) { - // Its possible auto start ran and UI was disabled, but subsequently - // user attempted to run a cell, & the prompt wasn't displayed to the user. - const token = new CancellationTokenSource(); - await this.kernelDependency - .installMissingDependencies( - resource, - kernelConnection.interpreter, - new DisplayOptions(false), - token.token, - true - ) - .finally(() => token.dispose()); - return; - } - - const failureInfo = analyzeKernelErrors( - error.stdErr || '', - this.workspace.workspaceFolders, - kernelConnection.interpreter?.sysPrefix - ); - switch (failureInfo?.reason) { - case KernelFailureReason.overridingBuiltinModules: { - await this.showMessageWithMoreInfo( - DataScience.fileSeemsToBeInterferingWithKernelStartup().format( - getDisplayPath(failureInfo.fileName, this.workspace.workspaceFolders || []) - ), - 'https://aka.ms/kernelFailuresOverridingBuiltInModules', - cellToDisplayErrors - ); - break; - } - case KernelFailureReason.moduleNotFoundFailure: { - // if ipykernel or ipykernle_launcher is missing, then install it - // Provided we know for a fact that it is missing, else we could end up spamming the user unnecessarily. - if ( - failureInfo.moduleName.toLowerCase().includes('ipykernel') && - kernelConnection.interpreter && - !(await this.kernelDependency.areDependenciesInstalled( + await this.handleErrorImplementation( + err, + purpose, + cellToDisplayErrors, + async (error: BaseError, defaultErrorMessage?: string) => { + if ( + err instanceof IpyKernelNotInstalledError && + err.reason === KernelInterpreterDependencyResponse.uiHidden && + (purpose === 'start' || purpose === 'restart') && + kernelConnection.interpreter + ) { + // Its possible auto start ran and UI was disabled, but subsequently + // user attempted to run a cell, & the prompt wasn't displayed to the user. + const token = new CancellationTokenSource(); + await this.kernelDependency + .installMissingDependencies( + resource, kernelConnection.interpreter, - undefined, + new DisplayOptions(false), + token.token, true - )) - ) { - const token = new CancellationTokenSource(); - await this.kernelDependency - .installMissingDependencies( - resource, + ) + .finally(() => token.dispose()); + return; + } + + const failureInfo = analyzeKernelErrors( + error.stdErr || '', + this.workspace.workspaceFolders, + kernelConnection.interpreter?.sysPrefix + ); + switch (failureInfo?.reason) { + case KernelFailureReason.overridingBuiltinModules: { + await this.showMessageWithMoreInfo( + DataScience.fileSeemsToBeInterferingWithKernelStartup().format( + getDisplayPath(failureInfo.fileName, this.workspace.workspaceFolders || []) + ), + 'https://aka.ms/kernelFailuresOverridingBuiltInModules', + cellToDisplayErrors + ); + break; + } + case KernelFailureReason.moduleNotFoundFailure: { + // if ipykernel or ipykernle_launcher is missing, then install it + // Provided we know for a fact that it is missing, else we could end up spamming the user unnecessarily. + if ( + failureInfo.moduleName.toLowerCase().includes('ipykernel') && + kernelConnection.interpreter && + !(await this.kernelDependency.areDependenciesInstalled( kernelConnection.interpreter, - new DisplayOptions(false), - token.token, + undefined, true - ) - .finally(() => token.dispose()); - } else { + )) + ) { + const token = new CancellationTokenSource(); + await this.kernelDependency + .installMissingDependencies( + resource, + kernelConnection.interpreter, + new DisplayOptions(false), + token.token, + true + ) + .finally(() => token.dispose()); + } else { + await this.showMessageWithMoreInfo( + DataScience.failedToStartKernelDueToMissingModule().format(failureInfo.moduleName), + 'https://aka.ms/kernelFailuresMissingModule', + cellToDisplayErrors + ); + } + break; + } + case KernelFailureReason.importFailure: { + const fileName = failureInfo.fileName + ? getDisplayPath(failureInfo.fileName, this.workspace.workspaceFolders || []) + : ''; + if (fileName) { + await this.showMessageWithMoreInfo( + DataScience.failedToStartKernelDueToImportFailureFromFile().format( + failureInfo.moduleName, + fileName + ), + 'https://aka.ms/kernelFailuresModuleImportErrFromFile', + cellToDisplayErrors + ); + } else { + await this.showMessageWithMoreInfo( + DataScience.failedToStartKernelDueToImportFailure().format(failureInfo.moduleName), + 'https://aka.ms/kernelFailuresModuleImportErr', + cellToDisplayErrors + ); + } + break; + } + case KernelFailureReason.dllLoadFailure: { + const message = failureInfo.moduleName + ? DataScience.failedToStartKernelDueToDllLoadFailure().format(failureInfo.moduleName) + : DataScience.failedToStartKernelDueToUnknowDllLoadFailure(); await this.showMessageWithMoreInfo( - DataScience.failedToStartKernelDueToMissingModule().format(failureInfo.moduleName), - 'https://aka.ms/kernelFailuresMissingModule', + message, + 'https://aka.ms/kernelFailuresDllLoad', cellToDisplayErrors ); + break; } - break; - } - case KernelFailureReason.importFailure: { - const fileName = failureInfo.fileName - ? getDisplayPath(failureInfo.fileName, this.workspace.workspaceFolders || []) - : ''; - if (fileName) { + case KernelFailureReason.importWin32apiFailure: { await this.showMessageWithMoreInfo( - DataScience.failedToStartKernelDueToImportFailureFromFile().format( - failureInfo.moduleName, - fileName - ), - 'https://aka.ms/kernelFailuresModuleImportErrFromFile', + DataScience.failedToStartKernelDueToWin32APIFailure(), + 'https://aka.ms/kernelFailuresWin32Api', cellToDisplayErrors ); - } else { + break; + } + case KernelFailureReason.zmqModuleFailure: { await this.showMessageWithMoreInfo( - DataScience.failedToStartKernelDueToImportFailure().format(failureInfo.moduleName), - 'https://aka.ms/kernelFailuresModuleImportErr', + DataScience.failedToStartKernelDueToPyZmqFailure(), + 'https://aka.ms/kernelFailuresPyzmq', cellToDisplayErrors ); + break; } - break; - } - case KernelFailureReason.dllLoadFailure: { - const message = failureInfo.moduleName - ? DataScience.failedToStartKernelDueToDllLoadFailure().format(failureInfo.moduleName) - : DataScience.failedToStartKernelDueToUnknowDllLoadFailure(); - await this.showMessageWithMoreInfo(message, 'https://aka.ms/kernelFailuresDllLoad', - cellToDisplayErrors); - break; - } - case KernelFailureReason.importWin32apiFailure: { - await this.showMessageWithMoreInfo( - DataScience.failedToStartKernelDueToWin32APIFailure(), - 'https://aka.ms/kernelFailuresWin32Api', - cellToDisplayErrors - ); - break; - } - case KernelFailureReason.zmqModuleFailure: { - await this.showMessageWithMoreInfo( - DataScience.failedToStartKernelDueToPyZmqFailure(), - 'https://aka.ms/kernelFailuresPyzmq', - cellToDisplayErrors - ); - break; - } - case KernelFailureReason.oldIPythonFailure: { - await this.showMessageWithMoreInfo( - DataScience.failedToStartKernelDueToOldIPython(), - 'https://aka.ms/kernelFailuresOldIPython', - cellToDisplayErrors - ); - break; - } - case KernelFailureReason.oldIPyKernelFailure: { - await this.showMessageWithMoreInfo( - DataScience.failedToStartKernelDueToOldIPyKernel(), - 'https://aka.ms/kernelFailuresOldIPyKernel', - cellToDisplayErrors - ); - break; - } - default: - if (defaultErrorMessage) { - void this.displayErrorsInCell(defaultErrorMessage, cellToDisplayErrors); - await this.applicationShell.showErrorMessage(defaultErrorMessage); + case KernelFailureReason.oldIPythonFailure: { + await this.showMessageWithMoreInfo( + DataScience.failedToStartKernelDueToOldIPython(), + 'https://aka.ms/kernelFailuresOldIPython', + cellToDisplayErrors + ); + break; } + case KernelFailureReason.oldIPyKernelFailure: { + await this.showMessageWithMoreInfo( + DataScience.failedToStartKernelDueToOldIPyKernel(), + 'https://aka.ms/kernelFailuresOldIPyKernel', + cellToDisplayErrors + ); + break; + } + default: + if (defaultErrorMessage) { + void this.displayErrorsInCell(defaultErrorMessage, cellToDisplayErrors); + await this.applicationShell.showErrorMessage(defaultErrorMessage); + } + } } - }); + ); } private async showMessageWithMoreInfo(message: string, moreInfoLink: string, cellToDisplayErrors?: NotebookCell) { message = `${message} \n${DataScience.viewJupyterLogForFurtherInfo()}`; void this.displayErrorsInCell(message, cellToDisplayErrors); - await this.applicationShell - .showErrorMessage(message, Common.learnMore()) - .then((selection) => { - if (selection === Common.learnMore()) { - this.browser.launch(moreInfoLink); - } - }); + await this.applicationShell.showErrorMessage(message, Common.learnMore()).then((selection) => { + if (selection === Common.learnMore()) { + this.browser.launch(moreInfoLink); + } + }); } private async handleErrorImplementation( err: Error, @@ -283,24 +296,24 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { // Some errors have localized and/or formatted error messages. const message = getCombinedErrorMessage(errorPrefix, err.message || err.toString()); void this.displayErrorsInCell(message, cellToDisplayErrors); - this.applicationShell - .showErrorMessage(message) - .then(noop, noop); + this.applicationShell.showErrorMessage(message).then(noop, noop); } traceError('DataScience Error', err); } private async displayErrorsInCell(errorMessage: string, cellToDisplayErrors?: NotebookCell) { - if (!cellToDisplayErrors) { + if (!cellToDisplayErrors || !errorMessage) { return; } - const associatedkernel = this.serviceContainer.get(IKernelProvider).get(cellToDisplayErrors.notebook); + const associatedkernel = this.serviceContainer + .get(IKernelProvider) + .get(cellToDisplayErrors.notebook); if (!associatedkernel) { return; } // Sometimes the cells are still running, wait for 1s for cells to finish & get cleared, // Then display the error in the cell. const stopWatch = new StopWatch(); - while (stopWatch.elapsedTime <= 2_000 || !associatedkernel.hasPendingCells) { + while (stopWatch.elapsedTime <= 1_000 && associatedkernel.hasPendingCells) { await sleep(100); } if (associatedkernel.hasPendingCells) { @@ -312,10 +325,23 @@ export class DataScienceErrorHandler implements IDataScienceErrorHandler { if (!controller || controller.connection !== associatedkernel.kernelConnectionMetadata) { return; } + const regex = /\[(?.*)\]\((?command:\S*)\)/gm; + let matches: RegExpExecArray | undefined | null; + while ((matches = regex.exec(errorMessage)) !== null) { + if (matches.length === 3) { + errorMessage = errorMessage.replace(matches[0], `${matches[1]}`); + } + } const execution = controller.controller.createNotebookCellExecution(cellToDisplayErrors); execution.start(); void execution.clearOutput(cellToDisplayErrors); - const output = new NotebookCellOutput([NotebookCellOutputItem.text(errorMessage, 'text/markdown')]) + const output = new NotebookCellOutput([ + NotebookCellOutputItem.error({ + message: '', + name: '', + stack: `\u001b[1;31m${errorMessage.trim()}` + }) + ]); void execution.appendOutput(output); execution.end(undefined); } diff --git a/src/client/datascience/errors/errorRendererComms.ts b/src/client/datascience/errors/errorRendererComms.ts new file mode 100644 index 00000000000..585ebb4a669 --- /dev/null +++ b/src/client/datascience/errors/errorRendererComms.ts @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { inject, injectable } from 'inversify'; +import { commands, notebooks, Position, Range, Selection, TextEditorRevealType, Uri } from 'vscode'; +import { IExtensionSyncActivationService } from '../../activation/types'; +import { IApplicationShell, ICommandManager, IDocumentManager } from '../../common/application/types'; +import { IFileSystem } from '../../common/platform/types'; +import { IDisposableRegistry } from '../../common/types'; +import { InteractiveWindowMessages } from '../interactive-common/interactiveWindowTypes'; +import { LineQueryRegex, linkCommandAllowList } from '../interactive-common/linkProvider'; + +@injectable() +export class ErrorRendererCommunicationHandler implements IExtensionSyncActivationService { + constructor( + @inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry, + @inject(IDocumentManager) private readonly documentManager: IDocumentManager, + @inject(ICommandManager) private readonly commandManager: ICommandManager, + @inject(IFileSystem) private readonly fs: IFileSystem, + @inject(IApplicationShell) private readonly applicationShell: IApplicationShell + ) {} + + activate(): void { + const messageChannel = notebooks.createRendererMessaging('jupyter-error-renderer'); + this.disposables.push( + messageChannel.onDidReceiveMessage(async (e) => { + const message = e.message; + if (message.message === InteractiveWindowMessages.OpenLink) { + const href = message.payload; + if (href.startsWith('file')) { + await this.openFile(href); + } else if (href.startsWith('https://command:') || href.startsWith('command:')) { + const temp: string = href.startsWith('https://command:') + ? href.split(':')[2] + : href.split(':')[1]; + const params: string[] = temp.includes('/?') ? temp.split('/?')[1].split(',') : []; + let command = temp.split('/?')[0]; + if (command.endsWith('/')) { + command = command.substring(0, command.length - 1); + } + if (linkCommandAllowList.includes(command)) { + await commands.executeCommand(command, params); + } + } else { + this.applicationShell.openUrl(href); + } + } + }) + ); + } + + private async openFile(fileUri: string) { + const uri = Uri.parse(fileUri); + let selection: Range = new Range(new Position(0, 0), new Position(0, 0)); + if (uri.query) { + // Might have a line number query on the file name + const lineMatch = LineQueryRegex.exec(uri.query); + if (lineMatch) { + const lineNumber = parseInt(lineMatch[1], 10); + selection = new Range(new Position(lineNumber, 0), new Position(lineNumber, 0)); + } + } + + // Show the matching editor if there is one + let editor = this.documentManager.visibleTextEditors.find((e) => this.fs.arePathsSame(e.document.uri, uri)); + if (editor) { + return this.documentManager + .showTextDocument(editor.document, { selection, viewColumn: editor.viewColumn }) + .then((e) => { + e.revealRange(selection, TextEditorRevealType.InCenter); + }); + } else { + // Not a visible editor, try opening otherwise + return this.commandManager.executeCommand('vscode.open', uri).then(() => { + // See if that opened a text document + editor = this.documentManager.visibleTextEditors.find((e) => this.fs.arePathsSame(e.document.uri, uri)); + if (editor) { + // Force the selection to change + editor.revealRange(selection); + editor.selection = new Selection(selection.start, selection.start); + } + }); + } + } +} diff --git a/src/client/datascience/interactive-common/linkProvider.ts b/src/client/datascience/interactive-common/linkProvider.ts index 62d5c63817a..9aab7f1bb68 100644 --- a/src/client/datascience/interactive-common/linkProvider.ts +++ b/src/client/datascience/interactive-common/linkProvider.ts @@ -9,5 +9,7 @@ export const LineQueryRegex = /line=(\d+)/; // in a markdown cell using the syntax: https://command:[my.vscode.command]. export const linkCommandAllowList = [ 'jupyter.latestExtension', + 'jupyter.viewOutput', + 'workbench.action.openSettings', 'jupyter.enableLoadingWidgetScriptsFromThirdPartySource' ]; diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index ffdc09154e0..4a1a555a275 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -15,11 +15,6 @@ import { workspace, WorkspaceEdit, notebooks, - Position, - Range, - Selection, - commands, - TextEditorRevealType, ViewColumn, NotebookEditor, Disposable, @@ -27,26 +22,20 @@ import { ThemeColor } from 'vscode'; import { IPythonExtensionChecker } from '../../api/types'; -import { - IApplicationShell, - ICommandManager, - IDocumentManager, - IWorkspaceService -} from '../../common/application/types'; +import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../common/application/types'; import { JVSC_EXTENSION_ID, MARKDOWN_LANGUAGE, PYTHON_LANGUAGE } from '../../common/constants'; import '../../common/extensions'; import { traceInfo, traceInfoIfCI } from '../../common/logger'; import { IFileSystem } from '../../common/platform/types'; import * as uuid from 'uuid/v4'; -import { IConfigurationService, IDisposableRegistry, InteractiveWindowMode, Resource } from '../../common/types'; +import { IConfigurationService, InteractiveWindowMode, Resource } from '../../common/types'; import { createDeferred, Deferred } from '../../common/utils/async'; import { noop } from '../../common/utils/misc'; import { generateCellsFromNotebookDocument } from '../cellFactory'; import { CellMatcher } from '../cellMatcher'; import { Commands, defaultNotebookFormat } from '../constants'; import { ExportFormat, IExportDialog } from '../export/types'; -import { InteractiveWindowMessages } from '../interactive-common/interactiveWindowTypes'; import { IKernel, IKernelProvider, NotebookCellRunState } from '../jupyter/kernels/types'; import { INotebookControllerManager } from '../notebook/types'; import { VSCodeNotebookController } from '../notebook/vscodeNotebookController'; @@ -55,7 +44,6 @@ import { IInteractiveWindowLoadable, IInteractiveWindowDebugger, INotebookExport import { getInteractiveWindowTitle } from './identity'; import { generateMarkdownFromCodeLines } from '../../../datascience-ui/common'; import { chainWithPendingUpdates } from '../notebook/helpers/notebookUpdater'; -import { LineQueryRegex, linkCommandAllowList } from '../interactive-common/linkProvider'; import { INativeInteractiveWindow } from './types'; import { generateInteractiveCode } from '../../../datascience-ui/common/cellFactory'; import { initializeInteractiveOrNotebookTelemetryBasedOnUserAction } from '../telemetry/telemetry'; @@ -120,7 +108,6 @@ export class InteractiveWindow implements IInteractiveWindowLoadable { private _inputUri: Uri | undefined; constructor( - private readonly applicationShell: IApplicationShell, private readonly documentManager: IDocumentManager, private readonly fs: IFileSystem, private readonly configuration: IConfigurationService, @@ -133,7 +120,6 @@ export class InteractiveWindow implements IInteractiveWindowLoadable { private readonly exportDialog: IExportDialog, private readonly notebookControllerManager: INotebookControllerManager, private readonly kernelProvider: IKernelProvider, - private readonly disposables: IDisposableRegistry, private readonly interactiveWindowDebugger: IInteractiveWindowDebugger ) { // Set our owner and first submitter @@ -220,71 +206,9 @@ export class InteractiveWindow implements IInteractiveWindowLoadable { }) ); this.listenForControllerSelection(notebookEditor.document); - this.initializeRendererCommunication(); return notebookEditor; } - private initializeRendererCommunication() { - const messageChannel = notebooks.createRendererMessaging('jupyter-error-renderer'); - this.disposables.push( - messageChannel.onDidReceiveMessage(async (e) => { - const message = e.message; - if (message.message === InteractiveWindowMessages.OpenLink) { - const href = message.payload; - if (href.startsWith('file')) { - await this.openFile(href); - } else if (href.startsWith('https://command:')) { - const temp: string = href.split(':')[2]; - const params: string[] = temp.includes('/?') ? temp.split('/?')[1].split(',') : []; - let command = temp.split('/?')[0]; - if (command.endsWith('/')) { - command = command.substring(0, command.length - 1); - } - if (linkCommandAllowList.includes(command)) { - await commands.executeCommand(command, params); - } - } else { - this.applicationShell.openUrl(href); - } - } - }) - ); - } - - private async openFile(fileUri: string) { - const uri = Uri.parse(fileUri); - let selection: Range = new Range(new Position(0, 0), new Position(0, 0)); - if (uri.query) { - // Might have a line number query on the file name - const lineMatch = LineQueryRegex.exec(uri.query); - if (lineMatch) { - const lineNumber = parseInt(lineMatch[1], 10); - selection = new Range(new Position(lineNumber, 0), new Position(lineNumber, 0)); - } - } - - // Show the matching editor if there is one - let editor = this.documentManager.visibleTextEditors.find((e) => this.fs.arePathsSame(e.document.uri, uri)); - if (editor) { - return this.documentManager - .showTextDocument(editor.document, { selection, viewColumn: editor.viewColumn }) - .then((e) => { - e.revealRange(selection, TextEditorRevealType.InCenter); - }); - } else { - // Not a visible editor, try opening otherwise - return this.commandManager.executeCommand('vscode.open', uri).then(() => { - // See if that opened a text document - editor = this.documentManager.visibleTextEditors.find((e) => this.fs.arePathsSame(e.document.uri, uri)); - if (editor) { - // Force the selection to change - editor.revealRange(selection); - editor.selection = new Selection(selection.start, selection.start); - } - }); - } - } - private registerControllerChangeListener(controller: VSCodeNotebookController, notebookDocument: NotebookDocument) { const controllerChangeListener = controller.controller.onDidChangeSelectedNotebooks( (selectedEvent: { notebook: NotebookDocument; selected: boolean }) => { diff --git a/src/client/datascience/interactive-window/interactiveWindowProvider.ts b/src/client/datascience/interactive-window/interactiveWindowProvider.ts index f9997c98085..ffa890bcc91 100644 --- a/src/client/datascience/interactive-window/interactiveWindowProvider.ts +++ b/src/client/datascience/interactive-window/interactiveWindowProvider.ts @@ -120,7 +120,6 @@ export class InteractiveWindowProvider implements IInteractiveWindowProvider, IA // Set it as soon as we create it. The .ctor for the interactive window // may cause a subclass to talk to the IInteractiveWindowProvider to get the active interactive window. const result = new InteractiveWindow( - this.serviceContainer.get(IApplicationShell), this.serviceContainer.get(IDocumentManager), this.serviceContainer.get(IFileSystem), this.serviceContainer.get(IConfigurationService), @@ -133,7 +132,6 @@ export class InteractiveWindowProvider implements IInteractiveWindowProvider, IA this.serviceContainer.get(IExportDialog), this.notebookControllerManager, this.kernelProvider, - this.disposables, this.serviceContainer.get(IInteractiveWindowDebugger) ); this._windows.push(result); diff --git a/src/client/datascience/jupyter/kernels/cellExecutionQueue.ts b/src/client/datascience/jupyter/kernels/cellExecutionQueue.ts index f4d94bc5274..5a832dfe05b 100644 --- a/src/client/datascience/jupyter/kernels/cellExecutionQueue.ts +++ b/src/client/datascience/jupyter/kernels/cellExecutionQueue.ts @@ -34,7 +34,7 @@ export class CellExecutionQueue implements Disposable { return this.cancelledOrCompletedWithErrors; } public get queue(): Readonly { - return this.queueOfCellsToExecute.map(cell => cell.cell); + return this.queueOfCellsToExecute.map((cell) => cell.cell); } constructor( private readonly session: Promise, @@ -79,6 +79,7 @@ export class CellExecutionQueue implements Disposable { this.cancelledOrCompletedWithErrors = true; traceInfo('Cancel pending cells'); await Promise.all(this.queueOfCellsToExecute.map((item) => item.cancel(forced))); + this.queueOfCellsToExecute.splice(0, this.queueOfCellsToExecute.length); } /** * Wait for cells to complete (for for the queue of cells to be processed) diff --git a/src/client/datascience/kernel-launcher/kernelProcess.ts b/src/client/datascience/kernel-launcher/kernelProcess.ts index c40b10f4237..59fa3601247 100644 --- a/src/client/datascience/kernel-launcher/kernelProcess.ts +++ b/src/client/datascience/kernel-launcher/kernelProcess.ts @@ -215,6 +215,9 @@ export class KernelProcess implements IKernelProcess { tcpPortUsed.waitUntilUsed(this.connection.shell_port, 200, timeout), tcpPortUsed.waitUntilUsed(this.connection.iopub_port, 200, timeout) ]).catch((ex) => { + if (cancelToken.isCancellationRequested) { + return; + } traceError(`waitUntilUsed timed out`, ex); // Throw an error we recognize. return Promise.reject(new KernelPortNotUsedTimeoutError(this.kernelConnectionMetadata)); diff --git a/src/client/datascience/serviceRegistry.ts b/src/client/datascience/serviceRegistry.ts index f96a010b75d..6456aa091f1 100644 --- a/src/client/datascience/serviceRegistry.ts +++ b/src/client/datascience/serviceRegistry.ts @@ -164,6 +164,7 @@ import { HostRawNotebookProvider } from './raw-kernel/liveshare/hostRawNotebookP import { KernelCommandListener } from './jupyter/kernels/kernelCommandListener'; import { CellHashProviderFactory } from './editor-integration/cellHashProviderFactory'; import { ExportToPythonPlain } from './export/exportToPythonPlain'; +import { ErrorRendererCommunicationHandler } from './errors/errorRendererComms'; // README: Did you make sure "dataScienceIocContainer.ts" has also been updated appropriately? @@ -292,6 +293,7 @@ export function registerTypes(serviceManager: IServiceManager, inNotebookApiExpe serviceManager.addSingleton(IJupyterServerUriStorage, JupyterServerUriStorage); serviceManager.addSingleton(INotebookWatcher, NotebookWatcher); serviceManager.addSingleton(IExtensionSyncActivationService, ExtensionRecommendationService); + serviceManager.addSingleton(IExtensionSyncActivationService, ErrorRendererCommunicationHandler); serviceManager.addSingleton(IDebuggingManager, DebuggingManager, undefined, [IExtensionSingleActivationService]); registerNotebookTypes(serviceManager); diff --git a/src/datascience-ui/error-renderer/index.ts b/src/datascience-ui/error-renderer/index.ts index 6c9899957af..6ebccc8c7fa 100644 --- a/src/datascience-ui/error-renderer/index.ts +++ b/src/datascience-ui/error-renderer/index.ts @@ -15,8 +15,15 @@ const handleInnerClick = (event: MouseEvent, context: RendererContext) => { for (const pathElement of event.composedPath()) { const node: any = pathElement; - if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href && node.href.indexOf('file') === 0) { - if (context.postMessage) { + if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href && context.postMessage) { + if (node.href.indexOf('file') === 0) { + context.postMessage({ + message: 'open_link', + payload: node.href + }); + event.preventDefault(); + return; + } else if (node.href.indexOf('command') === 0) { context.postMessage({ message: 'open_link', payload: node.href @@ -72,10 +79,11 @@ export const activate: ActivationFunction = (_context) => { // RegEx `line number` // When we escape, the links would be escaped as well. // We need to unescape them. - const regExp = new RegExp(/<a href='file:(.*(?=\?))\?line=(\d*)'>(\d*)<\/a>/); + const fileLinkRegExp = new RegExp(/<a href='file:(.*(?=\?))\?line=(\d*)'>(\d*)<\/a>/); + const commandRegEx = new RegExp(/<a href='command:(.*)'>(.*)<\/a>/); traceback = traceback.map((line) => { let matches: RegExpExecArray | undefined | null; - while ((matches = regExp.exec(line)) !== null) { + while ((matches = fileLinkRegExp.exec(line)) !== null) { if (matches.length === 4) { line = line.replace( matches[0], @@ -83,6 +91,11 @@ export const activate: ActivationFunction = (_context) => { ); } } + while ((matches = commandRegEx.exec(line)) !== null) { + if (matches.length === 3) { + line = line.replace(matches[0], `${matches[2]}`); + } + } return line; }); From fec809a3bbdefb2860e703b53d5efcf4443dc8a9 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 18 Nov 2021 13:44:08 -0800 Subject: [PATCH 3/4] fix formatting --- .../datascience/jupyter/kernels/kernel.ts | 21 +++++++++++++------ .../jupyter/kernels/kernelExecution.ts | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/client/datascience/jupyter/kernels/kernel.ts b/src/client/datascience/jupyter/kernels/kernel.ts index f61eaa0cafc..df965c1515e 100644 --- a/src/client/datascience/jupyter/kernels/kernel.ts +++ b/src/client/datascience/jupyter/kernels/kernel.ts @@ -180,7 +180,7 @@ export class Kernel implements IKernel { const isPreferredKernel = getResourceType(resourceUri) === 'notebook' ? notebookControllerManager.getPreferredNotebookController(this.notebookDocument)?.controller === - controller + controller : undefined; trackKernelResourceInformation(resourceUri, { kernelConnection: kernelConnectionMetadata, @@ -247,8 +247,8 @@ export class Kernel implements IKernel { this.notebook = this.notebook ? this.notebook : this._notebookPromise - ? await this._notebookPromise - : undefined; + ? await this._notebookPromise + : undefined; this._notebookPromise = undefined; const promises: Promise[] = []; if (this.notebook) { @@ -413,10 +413,18 @@ export class Kernel implements IKernel { // errors about startup failures. traceWarning(`Ignoring kernel startup failure as kernel was disposed`, ex); } else { - const cellForErrorDisplay = this.kernelExecution.queue.length ? this.kernelExecution.queue[0] : undefined; + const cellForErrorDisplay = this.kernelExecution.queue.length + ? this.kernelExecution.queue[0] + : undefined; void this.errorHandler // eslint-disable-next-line @typescript-eslint/no-explicit-any - .handleKernelError(ex as any, 'start', this.kernelConnectionMetadata, this.resourceUri, cellForErrorDisplay); // Just a notification, so don't await this + .handleKernelError( + ex as any, + 'start', + this.kernelConnectionMetadata, + this.resourceUri, + cellForErrorDisplay + ); // Just a notification, so don't await this } traceError(`failed to start INotebook in kernel, UI Disabled = ${this.startupUI.disableUI}`, ex); this.startCancellation.cancel(); @@ -834,7 +842,8 @@ export async function executeSilently(session: IJupyterSession, code: string): P outputs.push(output); } else if (jupyterLab.KernelMessage.isErrorMsg(msg)) { traceInfoIfCI( - `Got io pub message (error), ${msg.content.ename},${msg.content.evalue + `Got io pub message (error), ${msg.content.ename},${ + msg.content.evalue }, ${msg.content.traceback.join().substring(0, 100)}}` ); const output: nbformat.IError = { diff --git a/src/client/datascience/jupyter/kernels/kernelExecution.ts b/src/client/datascience/jupyter/kernels/kernelExecution.ts index 9677a892763..b049e8c73aa 100644 --- a/src/client/datascience/jupyter/kernels/kernelExecution.ts +++ b/src/client/datascience/jupyter/kernels/kernelExecution.ts @@ -57,7 +57,7 @@ export class KernelExecution implements IDisposable { public get onPreExecute() { return this._onPreExecute.event; } - public get queue(){ + public get queue() { return this.documentExecutions.get(this.kernel.notebookDocument)?.queue || []; } public async executeCell( From b348eeafccddf91a4726a6207008d5ff06ef339b Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 18 Nov 2021 15:19:10 -0800 Subject: [PATCH 4/4] fix formatting --- src/client/datascience/jupyter/kernels/kernel.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/client/datascience/jupyter/kernels/kernel.ts b/src/client/datascience/jupyter/kernels/kernel.ts index df965c1515e..7c75e99e13d 100644 --- a/src/client/datascience/jupyter/kernels/kernel.ts +++ b/src/client/datascience/jupyter/kernels/kernel.ts @@ -416,15 +416,14 @@ export class Kernel implements IKernel { const cellForErrorDisplay = this.kernelExecution.queue.length ? this.kernelExecution.queue[0] : undefined; - void this.errorHandler + void this.errorHandler.handleKernelError( // eslint-disable-next-line @typescript-eslint/no-explicit-any - .handleKernelError( - ex as any, - 'start', - this.kernelConnectionMetadata, - this.resourceUri, - cellForErrorDisplay - ); // Just a notification, so don't await this + ex as any, + 'start', + this.kernelConnectionMetadata, + this.resourceUri, + cellForErrorDisplay + ); // Just a notification, so don't await this } traceError(`failed to start INotebook in kernel, UI Disabled = ${this.startupUI.disableUI}`, ex); this.startCancellation.cancel();