From 13ecb40701a65787aaaffd559ff1e145a36e9e28 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 30 May 2022 14:44:21 +1000 Subject: [PATCH] Shared kernelconnection wrapper --- TELEMETRY.md | 227 ++++++++++-------- src/kernels/common/baseJupyterSession.ts | 63 +++-- src/kernels/common/kernelConnectionWrapper.ts | 73 ++++++ src/kernels/common/kernelSocketWrapper.ts | 5 +- .../debugger/kernelDebugAdapterBase.ts | 27 ++- .../baseKernelConnectionWrapper.ts} | 96 ++------ src/kernels/jupyter/session/jupyterSession.ts | 19 +- .../raw/session/rawJupyterSession.node.ts | 3 - src/kernels/raw/session/rawSocket.node.ts | 8 +- src/kernels/types.ts | 7 +- src/platform/api/kernelApi.ts | 2 +- src/platform/api/kernelConnectionWrapper.ts | 90 +++++++ src/test/common.node.ts | 87 +------ src/test/common.ts | 82 +++++++ src/test/datascience/notebook/helper.ts | 1 + .../notebook/kernelEvents.vscode.common.ts | 172 +++++++++++++ .../notebook/kernelEvents.vscode.test.ts | 10 + .../notebook/kernelEvents.vscode.web.test.ts | 10 + 18 files changed, 663 insertions(+), 319 deletions(-) create mode 100644 src/kernels/common/kernelConnectionWrapper.ts rename src/kernels/{kernelConnectionWrapper.ts => jupyter/baseKernelConnectionWrapper.ts} (72%) create mode 100644 src/platform/api/kernelConnectionWrapper.ts create mode 100644 src/test/datascience/notebook/kernelEvents.vscode.common.ts create mode 100644 src/test/datascience/notebook/kernelEvents.vscode.test.ts create mode 100644 src/test/datascience/notebook/kernelEvents.vscode.web.test.ts diff --git a/TELEMETRY.md b/TELEMETRY.md index 3cdc29c70ce..317eb470bc1 100644 --- a/TELEMETRY.md +++ b/TELEMETRY.md @@ -147,7 +147,7 @@ No properties for event ## Locations Used -[src/interactive-window/interactiveWindowCommandListener.ts#L357](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L357) +[src/interactive-window/interactiveWindowCommandListener.ts#L358](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L358) ```typescript } } @@ -178,7 +178,7 @@ No properties for event ## Locations Used -[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L326](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L326) +[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L322](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L322) ```typescript private maybeSendSliceDataDimensionalityTelemetry(numberOfDimensions: number) { @@ -210,7 +210,7 @@ No properties for event ## Locations Used -[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L208](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L208) +[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L204](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L204) ```typescript break; @@ -241,7 +241,7 @@ No properties for event ## Locations Used -[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L269](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L269) +[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L265](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L265) ```typescript if (payload.shape?.length) { this.maybeSendSliceDataDimensionalityTelemetry(payload.shape.length); @@ -421,7 +421,7 @@ No properties for event ## Locations Used -[src/platform/debugger/jupyter/notebook/debuggingManager.ts#L481](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/notebook/debuggingManager.ts#L481) +[src/kernels/debugger/debuggingManagerBase.ts#L198](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugger/debuggingManagerBase.ts#L198) ```typescript ); @@ -449,7 +449,7 @@ No properties for event ## Locations Used -[src/platform/debugger/jupyter/notebook/debuggingManager.ts#L161](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/notebook/debuggingManager.ts#L161) +[src/notebooks/debugger/debuggingManager.ts#L144](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/debugger/debuggingManager.ts#L144) ```typescript }), @@ -477,7 +477,7 @@ No properties for event ## Locations Used -[src/platform/debugger/jupyter/notebook/debuggingManager.ts#L108](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/notebook/debuggingManager.ts#L108) +[src/notebooks/debugger/debuggingManager.ts#L91](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/debugger/debuggingManager.ts#L91) ```typescript }), @@ -505,7 +505,7 @@ No properties for event ## Locations Used -[src/platform/debugger/jupyter/notebook/debuggingManager.ts#L486](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/notebook/debuggingManager.ts#L486) +[src/kernels/debugger/debuggingManagerBase.ts#L203](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugger/debuggingManagerBase.ts#L203) ```typescript 'https://github.com/microsoft/vscode-jupyter/wiki/Setting-Up-Run-by-Line-and-Debugging-for-Notebooks' ); @@ -532,7 +532,7 @@ No description provided ## Locations Used -[src/platform/debugger/jupyter/kernelDebugAdapter.ts#L94](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/kernelDebugAdapter.ts#L94) +[src/kernels/debugger/kernelDebugAdapterBase.ts#L104](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugger/kernelDebugAdapterBase.ts#L104) ```typescript this.kernel.onDisposed(() => { void debug.stopDebugging(this.session); @@ -544,7 +544,7 @@ No description provided ``` -[src/platform/debugger/jupyter/kernelDebugAdapter.ts#L108](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/kernelDebugAdapter.ts#L108) +[src/kernels/debugger/kernelDebugAdapterBase.ts#L118](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugger/kernelDebugAdapterBase.ts#L118) ```typescript cellStateChange.state === NotebookCellExecutionState.Idle && !this.disconnected @@ -556,7 +556,7 @@ No description provided ``` -[src/platform/debugger/jupyter/notebook/debuggingManager.ts#L152](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/notebook/debuggingManager.ts#L152) +[src/notebooks/debugger/debuggingManager.ts#L135](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/debugger/debuggingManager.ts#L135) ```typescript if (editor) { const controller = this.notebookToRunByLineController.get(editor.notebook); @@ -583,7 +583,7 @@ No description provided ## Locations Used -[src/platform/debugger/jupyter/notebook/debuggingManager.ts#L463](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/notebook/debuggingManager.ts#L463) +[src/kernels/debugger/debuggingManagerBase.ts#L180](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugger/debuggingManagerBase.ts#L180) ```typescript } @@ -594,6 +594,27 @@ No description provided return result; ``` + +
+ DATASCIENCE.DEBUGGING.SUCCESSFULLY_STARTED_IW_JUPYTER + +## Description + + + + + Telemetry sent when we have managed to successfully start the Interactive Window debugger using the Jupyter protocol. + +## Properties + + +No properties for event + + +## Locations Used + +Event can be removed. Not referenced anywhere +
DATASCIENCE.DEBUGGING.SUCCESSFULLY_STARTED_RUN_AND_DEBUG_CELL @@ -611,7 +632,7 @@ No properties for event ## Locations Used -[src/platform/debugger/jupyter/notebook/debugCellControllers.ts#L20](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/notebook/debugCellControllers.ts#L20) +[src/notebooks/debugger/debugCellControllers.ts#L20](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/debugger/debugCellControllers.ts#L20) ```typescript private readonly kernel: IKernel, private readonly commandManager: ICommandManager @@ -622,6 +643,18 @@ No properties for event public async willSendEvent(_msg: DebugProtocolMessage): Promise { ``` + +[src/interactive-window/debugger/jupyter/debugCellControllers.ts#L23](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/debugger/jupyter/debugCellControllers.ts#L23) +```typescript + public readonly debugCell: NotebookCell, + private readonly kernel: IKernel + ) { + sendTelemetryEvent(DebuggingTelemetry.successfullyStartedRunAndDebugCell); + } + + public async willSendEvent(_msg: DebugProtocolMessage): Promise { +``` +
DATASCIENCE.DEBUGGING.SUCCESSFULLY_STARTED_RUNBYLINE @@ -639,7 +672,7 @@ No properties for event ## Locations Used -[src/platform/debugger/jupyter/notebook/runByLineController.ts#L29](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/debugger/jupyter/notebook/runByLineController.ts#L29) +[src/notebooks/debugger/runByLineController.ts#L29](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/debugger/runByLineController.ts#L29) ```typescript private readonly kernel: IKernel, private readonly settings: IConfigurationService @@ -916,7 +949,7 @@ No properties for event ``` -[src/notebooks/controllers/vscodeNotebookController.ts#L257](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/controllers/vscodeNotebookController.ts#L257) +[src/notebooks/controllers/vscodeNotebookController.ts#L256](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/controllers/vscodeNotebookController.ts#L256) ```typescript return; } @@ -928,7 +961,7 @@ No properties for event ``` -[src/kernels/kernel.base.ts#L191](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L191) +[src/kernels/kernel.base.ts#L197](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L197) ```typescript } public async executeCell(cell: NotebookCell): Promise { @@ -1050,7 +1083,7 @@ No description provided ``` -[src/platform/export/fileConverter.ts#L64](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/export/fileConverter.ts#L64) +[src/platform/export/fileConverter.ts#L65](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/export/fileConverter.ts#L65) ```typescript } @@ -1102,7 +1135,7 @@ No description provided ## Locations Used -[src/platform/export/fileConverter.ts#L84](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/export/fileConverter.ts#L84) +[src/platform/export/fileConverter.ts#L85](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/export/fileConverter.ts#L85) ```typescript await this.performExport(format, sourceDocument, target, token, candidateInterpreter); } catch (e) { @@ -1130,7 +1163,7 @@ No properties for event ## Locations Used -[src/interactive-window/interactiveWindowCommandListener.ts#L198](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L198) +[src/interactive-window/interactiveWindowCommandListener.ts#L199](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L199) ```typescript return result; } @@ -1158,7 +1191,7 @@ No properties for event ## Locations Used -[src/interactive-window/interactiveWindowCommandListener.ts#L247](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L247) +[src/interactive-window/interactiveWindowCommandListener.ts#L248](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L248) ```typescript } } @@ -1186,7 +1219,7 @@ No properties for event ## Locations Used -[src/webviews/extension-side/variablesView/variableView.node.ts#L174](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/variablesView/variableView.node.ts#L174) +[src/webviews/extension-side/variablesView/variableView.node.ts#L170](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/variablesView/variableView.node.ts#L170) ```typescript } } catch (e) { @@ -1215,9 +1248,9 @@ No properties for event ## Locations Used -[src/notebooks/controllers/notebookControllerManager.ts#L899](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/controllers/notebookControllerManager.ts#L899) +[src/notebooks/controllers/notebookControllerManager.ts#L918](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/controllers/notebookControllerManager.ts#L918) ```typescript - } catch (ex) { + } // We know that this fails when we have xeus kernels installed (untill that's resolved thats one instance when we can have duplicates). sendTelemetryEvent( Telemetry.FailedToCreateNotebookController, @@ -1266,7 +1299,7 @@ No properties for event ## Locations Used -[src/kernels/kernelFinder.base.ts#L236](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernelFinder.base.ts#L236) +[src/kernels/kernelFinder.base.ts#L242](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernelFinder.base.ts#L242) ```typescript const key = `${kind}:${useCache}`; if (this.startTimeForFetching && !this.fetchingTelemetrySent.has(key)) { @@ -1294,10 +1327,10 @@ No properties for event ## Locations Used -[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L33](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L33) +[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L39](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L39) ```typescript - @inject(IJupyterRequestCreator) private readonly requestCreator: IJupyterRequestCreator - ) {} + this.serverUriStorage.onDidRemoveUris(this.onDidRemoveUris, this, this.disposables); + } @captureTelemetry(Telemetry.GetPasswordAttempt) public getPasswordConnectionInfo(url: string): Promise { @@ -1395,7 +1428,7 @@ No description provided ## Locations Used -[src/interactive-window/interactiveWindowCommandListener.ts#L372](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L372) +[src/interactive-window/interactiveWindowCommandListener.ts#L373](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L373) ```typescript return this.statusProvider.waitWithStatus(promise, message, undefined, canceled); } @@ -1407,7 +1440,7 @@ No description provided ``` -[src/interactive-window/interactiveWindowCommandListener.ts#L395](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L395) +[src/interactive-window/interactiveWindowCommandListener.ts#L396](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/interactiveWindowCommandListener.ts#L396) ```typescript } } @@ -1436,7 +1469,7 @@ No description provided ## Locations Used -[src/kernels/debugging/interactiveWindowDebugger.node.ts#L112](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugging/interactiveWindowDebugger.node.ts#L112) +[src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L111](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L111) ```typescript executeSilently(kernel.session, this.tracingEnableCode, { traceErrors: true, @@ -1448,7 +1481,7 @@ No description provided ``` -[src/kernels/debugging/interactiveWindowDebugger.node.ts#L123](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugging/interactiveWindowDebugger.node.ts#L123) +[src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L122](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L122) ```typescript executeSilently(kernel.session, this.tracingDisableCode, { traceErrors: true, @@ -1460,7 +1493,7 @@ No description provided ``` -[src/kernels/debugging/interactiveWindowDebugger.node.ts#L152](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugging/interactiveWindowDebugger.node.ts#L152) +[src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L151](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L151) ```typescript const importResults = await executeSilently(kernel.session, this.waitForDebugClientCode, { traceErrors: true, @@ -1472,7 +1505,7 @@ No description provided ``` -[src/kernels/debugging/interactiveWindowDebugger.node.ts#L275](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugging/interactiveWindowDebugger.node.ts#L275) +[src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L268](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L268) ```typescript { traceErrors: true, @@ -1484,7 +1517,7 @@ No description provided ``` -[src/kernels/debugging/interactiveWindowDebugger.node.ts#L304](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/debugging/interactiveWindowDebugger.node.ts#L304) +[src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L297](https://github.com/microsoft/vscode-jupyter/tree/main/src/interactive-window/debugger/interactiveWindowDebugger.node.ts#L297) ```typescript ? await executeSilently(kernel.session, this.enableDebuggerCode, { traceErrors: true, @@ -1512,7 +1545,7 @@ No properties for event ## Locations Used -[src/notebooks/execution/kernelExecution.ts#L207](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L207) +[src/notebooks/execution/kernelExecution.ts#L199](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L199) ```typescript this.documentExecutions.set(document, newCellExecutionQueue); return newCellExecutionQueue; @@ -1856,7 +1889,7 @@ No description provided ## Locations Used -[src/kernels/kernel.base.ts#L527](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L527) +[src/kernels/kernel.base.ts#L545](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L545) ```typescript await this.executeSilently(session, startupCode, { traceErrors: true, @@ -2656,7 +2689,7 @@ function resetData(resource: Resource, eventName: string, properties: any) { ``` -[src/notebooks/execution/kernelExecution.ts#L263](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L263) +[src/notebooks/execution/kernelExecution.ts#L255](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L255) ```typescript // Otherwise a real error occurred. sendKernelTelemetryEvent( @@ -2668,7 +2701,7 @@ function resetData(resource: Resource, eventName: string, properties: any) { ``` -[src/notebooks/execution/kernelExecution.ts#L275](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L275) +[src/notebooks/execution/kernelExecution.ts#L267](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L267) ```typescript })(); @@ -2765,7 +2798,7 @@ No properties for event ``` -[src/kernels/kernel.base.ts#L282](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L282) +[src/kernels/kernel.base.ts#L291](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L291) ```typescript await (this._jupyterSessionPromise ? this.kernelExecution.restart(this._jupyterSessionPromise) @@ -2777,7 +2810,7 @@ No properties for event ``` -[src/kernels/kernel.base.ts#L293](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L293) +[src/kernels/kernel.base.ts#L302](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L302) ```typescript this.restarting = undefined; // If we get a kernel promise failure, then restarting timed out. Just shutdown and restart the entire server. @@ -3114,7 +3147,7 @@ No properties for event ## Locations Used -[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L204](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L204) +[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L200](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L200) ```typescript case DataViewerMessages.RefreshDataViewer: @@ -3307,7 +3340,7 @@ No properties for event ## Locations Used -[src/kernels/variables/debuggerVariables.node.ts#L121](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/variables/debuggerVariables.node.ts#L121) +[src/kernels/variables/debuggerVariables.node.ts#L120](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/variables/debuggerVariables.node.ts#L120) ```typescript // Note, full variable results isn't necessary for this call. It only really needs the variable value. const result = this.lastKnownVariables.find((v) => v.name === name); @@ -4119,7 +4152,7 @@ No properties for event ``` -[src/platform/errors/errorHandler.ts#L302](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/errors/errorHandler.ts#L302) +[src/platform/errors/errorHandler.ts#L301](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/errors/errorHandler.ts#L301) ```typescript ConfigurationTarget.Workspace ); @@ -4131,7 +4164,7 @@ No properties for event ``` -[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L394](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L394) +[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L395](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L395) ```typescript ); return this.requestCreator.getFetchMethod()(url, this.addAllowUnauthorized(url, true, options)); @@ -4183,7 +4216,7 @@ No properties for event ``` -[src/platform/errors/errorHandler.ts#L294](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/errors/errorHandler.ts#L294) +[src/platform/errors/errorHandler.ts#L293](https://github.com/microsoft/vscode-jupyter/tree/main/src/platform/errors/errorHandler.ts#L293) ```typescript .showErrorMessage(DataScience.jupyterSelfCertFail().format(err.message), enableOption, closeOption) .then((value) => { @@ -4195,7 +4228,7 @@ No properties for event ``` -[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L385](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L385) +[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L386](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L386) ```typescript closeOption ); @@ -4307,7 +4340,7 @@ No description provided ## Locations Used -[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L237](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L237) +[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L233](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L233) ```typescript // Log telemetry about number of rows @@ -4319,7 +4352,7 @@ No description provided ``` -[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L320](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L320) +[src/webviews/extension-side/dataviewer/dataViewer.node.ts#L316](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/dataviewer/dataViewer.node.ts#L316) ```typescript private sendElapsedTimeTelemetry() { @@ -4574,7 +4607,7 @@ No description provided ## Locations Used -[src/kernels/kernel.base.ts#L534](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L534) +[src/kernels/kernel.base.ts#L552](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L552) ```typescript await this.executeSilently(session, this.getUserStartupCommands(), { traceErrors: true, @@ -5205,7 +5238,7 @@ No properties for event ## Locations Used -[src/notebooks/execution/cellExecution.ts#L463](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/cellExecution.ts#L463) +[src/notebooks/execution/cellExecution.ts#L449](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/cellExecution.ts#L449) ```typescript const props = { notebook: true }; if (!CellExecution.sentExecuteCellTelemetry) { @@ -5248,7 +5281,7 @@ No properties for event ## Locations Used -[src/notebooks/execution/cellExecution.ts#L465](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/cellExecution.ts#L465) +[src/notebooks/execution/cellExecution.ts#L451](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/cellExecution.ts#L451) ```typescript CellExecution.sentExecuteCellTelemetry = true; sendTelemetryEvent(Telemetry.ExecuteCellPerceivedCold, this.stopWatchForTelemetry.elapsedTime, props); @@ -5300,7 +5333,7 @@ No properties for event ## Locations Used -[src/kernels/jupyter/jupyterKernelService.node.ts#L216](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/jupyterKernelService.node.ts#L216) +[src/kernels/jupyter/jupyterKernelService.node.ts#L217](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/jupyterKernelService.node.ts#L217) ```typescript await this.fs.writeLocalFile(kernelSpecFilePath.fsPath, JSON.stringify(contents, undefined, 4)); } catch (ex) { @@ -5312,7 +5345,7 @@ No properties for event ``` -[src/kernels/jupyter/jupyterKernelService.node.ts#L356](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/jupyterKernelService.node.ts#L356) +[src/kernels/jupyter/jupyterKernelService.node.ts#L349](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/jupyterKernelService.node.ts#L349) ```typescript await this.fs.writeLocalFile(kernelSpecFilePath, JSON.stringify(specModel, undefined, 2)); } catch (ex) { @@ -5506,7 +5539,7 @@ No properties for event ## Locations Used -[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L258](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L258) +[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L259](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L259) ```typescript const requestHeaders = { Cookie: cookieString, 'X-XSRFToken': xsrfCookie }; return { requestHeaders }; @@ -5534,7 +5567,7 @@ No properties for event ## Locations Used -[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L253](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L253) +[src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L254](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/jupyterPasswordConnect.ts#L254) ```typescript // If we found everything return it all back if not, undefined as partial is useless @@ -5766,7 +5799,7 @@ No properties for event ## Locations Used -[src/notebooks/execution/kernelExecution.ts#L208](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L208) +[src/notebooks/execution/kernelExecution.ts#L200](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L200) ```typescript return newCellExecutionQueue; } @@ -6378,7 +6411,7 @@ No properties for event ## Locations Used -[src/kernels/jupyter/jupyterKernelService.node.ts#L159](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/jupyterKernelService.node.ts#L159) +[src/kernels/jupyter/jupyterKernelService.node.ts#L160](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/jupyterKernelService.node.ts#L160) ```typescript */ // eslint-disable-next-line @@ -6512,7 +6545,7 @@ No properties for event ## Locations Used -[src/kernels/raw/launcher/kernelLauncher.node.ts#L116](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/launcher/kernelLauncher.node.ts#L116) +[src/kernels/raw/launcher/kernelLauncher.node.ts#L117](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/launcher/kernelLauncher.node.ts#L117) ```typescript // Should be available now, wait with a timeout return await this.launchProcess(kernelConnectionMetadata, resource, workingDirectory, timeout, cancelToken); @@ -6737,14 +6770,14 @@ No properties for event ## Locations Used -[src/webviews/extension-side/variablesView/variableView.node.ts#L84](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/variablesView/variableView.node.ts#L84) +[src/webviews/extension-side/variablesView/variableView.node.ts#L80](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/variablesView/variableView.node.ts#L80) ```typescript console.log(`Done initing variables`); } @captureTelemetry(Telemetry.NativeVariableViewLoaded) public async load(codeWebview: vscodeWebviewView) { - await super.loadWebview(process.cwd(), codeWebview).catch(traceError); + await super.loadWebview(Uri.file(process.cwd()), codeWebview).catch(traceError); ``` @@ -6765,7 +6798,7 @@ No properties for event ## Locations Used -[src/webviews/extension-side/variablesView/variableView.node.ts#L143](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/variablesView/variableView.node.ts#L143) +[src/webviews/extension-side/variablesView/variableView.node.ts#L139](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/variablesView/variableView.node.ts#L139) ```typescript // I've we've been made visible, make sure that we are updated @@ -6939,7 +6972,7 @@ No properties for event ## Locations Used -[src/kernels/kernel.base.ts#L323](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L323) +[src/kernels/kernel.base.ts#L332](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L332) ```typescript // Setup telemetry if (!this.perceivedJupyterStartupTelemetryCaptured) { @@ -6951,7 +6984,7 @@ No properties for event ``` -[src/kernels/kernel.base.ts#L406](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L406) +[src/kernels/kernel.base.ts#L424](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L424) ```typescript sendKernelTelemetryEvent( @@ -6981,7 +7014,7 @@ No properties for event ## Locations Used -[src/notebooks/controllers/notebookControllerManager.ts#L768](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/controllers/notebookControllerManager.ts#L768) +[src/notebooks/controllers/notebookControllerManager.ts#L774](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/controllers/notebookControllerManager.ts#L774) ```typescript ? PYTHON_LANGUAGE : getTelemetrySafeLanguage(getLanguageInNotebookMetadata(notebookMetadata) || ''); @@ -7008,7 +7041,7 @@ No description provided ## Locations Used -[src/notebooks/controllers/notebookControllerManager.ts#L748](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/controllers/notebookControllerManager.ts#L748) +[src/notebooks/controllers/notebookControllerManager.ts#L754](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/controllers/notebookControllerManager.ts#L754) ```typescript onlyConnection && (matchReason |= PreferredKernelExactMatchReason.OnlyKernel); topMatchIsPreferredInterpreter && (matchReason |= PreferredKernelExactMatchReason.WasPreferredInterpreter); @@ -7479,7 +7512,7 @@ No properties for event ## Locations Used -[src/kernels/raw/session/rawJupyterSession.node.ts#L347](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L347) +[src/kernels/raw/session/rawJupyterSession.node.ts#L350](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L350) ```typescript } else { traceWarning(`Didn't get response for requestKernelInfo after ${stopWatch.elapsedTime}ms.`); @@ -7507,7 +7540,7 @@ No properties for event ## Locations Used -[src/kernels/raw/launcher/kernelProcess.node.ts#L131](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/launcher/kernelProcess.node.ts#L131) +[src/kernels/raw/launcher/kernelProcess.node.ts#L135](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/launcher/kernelProcess.node.ts#L135) ```typescript } } @@ -7536,7 +7569,7 @@ No properties for event ## Locations Used -[src/kernels/raw/session/rawJupyterSession.node.ts#L140](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L140) +[src/kernels/raw/session/rawJupyterSession.node.ts#L142](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L142) ```typescript throw error; } @@ -7597,7 +7630,7 @@ No properties for event ## Locations Used -[src/kernels/raw/launcher/kernelLauncher.node.ts#L219](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/launcher/kernelLauncher.node.ts#L219) +[src/kernels/raw/launcher/kernelLauncher.node.ts#L224](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/launcher/kernelLauncher.node.ts#L224) ```typescript const disposable = kernelProcess.exited( @@ -7621,7 +7654,7 @@ No properties for event ``` -[src/kernels/raw/session/rawJupyterSession.node.ts#L184](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L184) +[src/kernels/raw/session/rawJupyterSession.node.ts#L186](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L186) ```typescript if (session !== this.session) { return; @@ -7649,7 +7682,7 @@ No properties for event ## Locations Used -[src/kernels/kernelConnector.ts#L143](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernelConnector.ts#L143) +[src/kernels/kernelConnector.ts#L144](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernelConnector.ts#L144) ```typescript const rawNotebookProvider = serviceContainer.tryGet(IRawNotebookProvider); const rawLocalKernel = rawNotebookProvider?.isSupported && isLocal; @@ -7680,7 +7713,7 @@ No properties for event ## Locations Used -[src/kernels/raw/session/rawJupyterSession.node.ts#L158](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L158) +[src/kernels/raw/session/rawJupyterSession.node.ts#L160](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L160) ```typescript // We want to know why we got shut down const stacktrace = new Error().stack; @@ -7709,7 +7742,7 @@ No properties for event ## Locations Used -[src/kernels/raw/session/rawJupyterSession.node.ts#L83](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L83) +[src/kernels/raw/session/rawJupyterSession.node.ts#L85](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L85) ```typescript Cancellation.throwIfCanceled(options.token); // Only connect our session if we didn't cancel or timeout @@ -7721,9 +7754,9 @@ No properties for event ``` -[src/kernels/raw/session/rawJupyterSession.node.ts#L98](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L98) +[src/kernels/raw/session/rawJupyterSession.node.ts#L100](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L100) ```typescript - if (error instanceof CancellationError) { + if (isCancellationError(error) || options.token.isCancellationRequested) { sendKernelTelemetryEvent( this.resource, Telemetry.RawKernelSessionStart, @@ -7733,7 +7766,7 @@ No properties for event ``` -[src/kernels/raw/session/rawJupyterSession.node.ts#L109](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L109) +[src/kernels/raw/session/rawJupyterSession.node.ts#L111](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L111) ```typescript } else if (error instanceof TimedOutError) { sendKernelTelemetryEvent( @@ -7745,7 +7778,7 @@ No properties for event ``` -[src/kernels/raw/session/rawJupyterSession.node.ts#L121](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L121) +[src/kernels/raw/session/rawJupyterSession.node.ts#L123](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L123) ```typescript // Send our telemetry event with the error included sendKernelTelemetryEvent( @@ -7773,7 +7806,7 @@ No properties for event ## Locations Used -[src/kernels/raw/session/rawJupyterSession.node.ts#L129](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L129) +[src/kernels/raw/session/rawJupyterSession.node.ts#L131](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L131) ```typescript ); sendKernelTelemetryEvent( @@ -7801,7 +7834,7 @@ No properties for event ## Locations Used -[src/kernels/raw/session/rawJupyterSession.node.ts#L82](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L82) +[src/kernels/raw/session/rawJupyterSession.node.ts#L84](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L84) ```typescript newSession = await this.startRawSession(options); Cancellation.throwIfCanceled(options.token); @@ -7829,7 +7862,7 @@ No properties for event ## Locations Used -[src/kernels/raw/session/rawJupyterSession.node.ts#L114](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L114) +[src/kernels/raw/session/rawJupyterSession.node.ts#L116](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L116) ```typescript undefined, error @@ -7857,13 +7890,13 @@ No properties for event ## Locations Used -[src/kernels/raw/session/rawJupyterSession.node.ts#L103](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L103) +[src/kernels/raw/session/rawJupyterSession.node.ts#L105](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L105) ```typescript undefined, error ); sendKernelTelemetryEvent(this.resource, Telemetry.RawKernelSessionStartUserCancel); - traceInfo('Starting of raw session cancelled by user'); + traceVerbose('Starting of raw session cancelled by user'); throw error; } else if (error instanceof TimedOutError) { ``` @@ -7885,7 +7918,7 @@ No properties for event ## Locations Used -[src/kernels/raw/session/rawJupyterSession.node.ts#L235](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L235) +[src/kernels/raw/session/rawJupyterSession.node.ts#L238](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/raw/session/rawJupyterSession.node.ts#L238) ```typescript return this.startRawSession({ token: cancelToken, ui: new DisplayOptions(disableUI) }); } @@ -7913,7 +7946,7 @@ No properties for event ## Locations Used -[src/kernels/jupyter/jupyterKernelService.node.ts#L238](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/jupyterKernelService.node.ts#L238) +[src/kernels/jupyter/jupyterKernelService.node.ts#L239](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/jupyterKernelService.node.ts#L239) ```typescript ); } @@ -7962,7 +7995,7 @@ No properties for event ## Locations Used -[src/notebooks/execution/kernelExecution.ts#L283](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L283) +[src/notebooks/execution/kernelExecution.ts#L275](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L275) ```typescript } @@ -7990,7 +8023,7 @@ No properties for event ## Locations Used -[src/notebooks/execution/kernelExecution.ts#L282](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L282) +[src/notebooks/execution/kernelExecution.ts#L274](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/execution/kernelExecution.ts#L274) ```typescript }); } @@ -8278,7 +8311,7 @@ No properties for event ## Locations Used -[src/kernels/kernel.base.ts#L325](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L325) +[src/kernels/kernel.base.ts#L334](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/kernel.base.ts#L334) ```typescript this.perceivedJupyterStartupTelemetryCaptured = true; sendTelemetryEvent(Telemetry.PerceivedJupyterStartupNotebook, stopWatch.elapsedTime); @@ -8306,7 +8339,7 @@ No properties for event ## Locations Used -[src/kernels/jupyter/launcher/notebookStarter.node.ts#L152](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/notebookStarter.node.ts#L152) +[src/kernels/jupyter/launcher/notebookStarter.node.ts#L156](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/launcher/notebookStarter.node.ts#L156) ```typescript } @@ -8359,7 +8392,7 @@ No properties for event ## Locations Used -[src/kernels/common/baseJupyterSession.ts#L69](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/common/baseJupyterSession.ts#L69) +[src/kernels/common/baseJupyterSession.ts#L70](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/common/baseJupyterSession.ts#L70) ```typescript export class JupyterSessionStartError extends WrappedError { constructor(originalException: Error) { @@ -8611,7 +8644,7 @@ No description provided ## Locations Used -[src/webviews/extension-side/variablesView/variableView.node.ts#L187](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/variablesView/variableView.node.ts#L187) +[src/webviews/extension-side/variablesView/variableView.node.ts#L183](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/variablesView/variableView.node.ts#L183) ```typescript const response = await this.variables.getVariables(args, activeNotebook); @@ -8638,7 +8671,7 @@ No description provided ## Locations Used -[src/notebooks/helpers.ts#L749](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/helpers.ts#L749) +[src/notebooks/helpers.ts#L671](https://github.com/microsoft/vscode-jupyter/tree/main/src/notebooks/helpers.ts#L671) ```typescript // Unless we already know its an unknown output type. const outputType: nbformat.OutputType = @@ -8666,7 +8699,7 @@ No properties for event ## Locations Used -[src/kernels/jupyter/session/jupyterSession.ts#L74](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/session/jupyterSession.ts#L74) +[src/kernels/jupyter/session/jupyterSession.ts#L67](https://github.com/microsoft/vscode-jupyter/tree/main/src/kernels/jupyter/session/jupyterSession.ts#L67) ```typescript return true; } @@ -8722,7 +8755,7 @@ No description provided ## Locations Used -[src/webviews/extension-side/webviewHost.node.ts#L281](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/webviewHost.node.ts#L281) +[src/webviews/extension-side/webviewHost.ts#L279](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/webviewHost.ts#L279) ```typescript protected webViewRendered() { if (this.webviewInit && !this.webviewInit.resolved) { @@ -8750,15 +8783,15 @@ No properties for event ## Locations Used -[src/webviews/extension-side/webviewHost.node.ts#L299](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/webviewHost.node.ts#L299) +[src/webviews/extension-side/webviewHost.ts#L297](https://github.com/microsoft/vscode-jupyter/tree/main/src/webviews/extension-side/webviewHost.ts#L297) ```typescript this.dispose(); }; @captureTelemetry(Telemetry.WebviewStyleUpdate) - private async handleCssRequest(request: IGetCssRequest): Promise { - const settings = await this.generateDataScienceExtraSettings(); - const requestIsDark = settings.ignoreVscodeTheme ? false : request?.isDark; + private async handleCssRequest(): Promise { + const isDark = await this.isDark(); + return this.postMessageInternal(CssMessages.GetCssResponse, { ```
diff --git a/src/kernels/common/baseJupyterSession.ts b/src/kernels/common/baseJupyterSession.ts index 881c210ec71..f39f87faf4b 100644 --- a/src/kernels/common/baseJupyterSession.ts +++ b/src/kernels/common/baseJupyterSession.ts @@ -31,6 +31,7 @@ import { ChainingExecuteRequester } from './chainingExecuteRequester'; import { getResourceType } from '../../platform/common/utils'; import { KernelProgressReporter } from '../../platform/progress/kernelProgressReporter'; import { isTestExecution } from '../../platform/common/constants'; +import { KernelConnectionWrapper } from './kernelConnectionWrapper'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export function suppressShutdownErrors(realKernel: any) { @@ -71,6 +72,13 @@ export class JupyterSessionStartError extends WrappedError { } export abstract class BaseJupyterSession implements IJupyterSession { + /** + * Keep a single instance of KernelConnectionWrapper. + * This way when sessions change, we still have a single Kernel.IKernelConnection proxy (wrapper), + * which will have all of the event handlers bound to it. + * This allows consumers to add event handlers hand not worry about internals & can use the lower level Jupyter API. + */ + private _wrappedKernel?: KernelConnectionWrapper; private _isDisposed?: boolean; private readonly _disposed = new EventEmitter(); protected readonly disposables: IDisposable[] = []; @@ -83,28 +91,30 @@ export abstract class BaseJupyterSession implements IJupyterSession { protected get session(): ISessionWithSocket | undefined { return this._session; } + public get kernelId(): string { + return this.session?.kernel?.id || ''; + } public get kernel(): Kernel.IKernelConnection | undefined { - return this._session?.kernel || undefined; + if (this._wrappedKernel) { + return this._wrappedKernel; + } + if (!this._session?.kernel) { + return; + } + this._wrappedKernel = new KernelConnectionWrapper(this._session.kernel, this.disposables); + return this._wrappedKernel; } + public get kernelSocket(): Observable { return this._kernelSocket; } public get onSessionStatusChanged(): Event { return this.onStatusChangedEvent.event; } - public get onIOPubMessage(): Event { - if (!this.ioPubEventEmitter) { - this.ioPubEventEmitter = new EventEmitter(); - } - return this.ioPubEventEmitter.event; - } - public get status(): KernelMessage.Status { return this.getServerStatus(); } - public abstract get kernelId(): string; - public get isConnected(): boolean { return this.connected; } @@ -115,10 +125,9 @@ export abstract class BaseJupyterSession implements IJupyterSession { protected restartSessionPromise?: { token: CancellationTokenSource; promise: Promise }; private _session: ISessionWithSocket | undefined; private _kernelSocket = new ReplaySubject(); - private ioPubEventEmitter = new EventEmitter(); - private ioPubHandler: Slot; private unhandledMessageHandler: Slot; private chainingExecute = new ChainingExecuteRequester(); + private previousAnyMessageHandler?: IDisposable; constructor( public readonly kind: 'localRaw' | 'remoteJupyter' | 'localJupyter', @@ -128,7 +137,6 @@ export abstract class BaseJupyterSession implements IJupyterSession { private readonly interruptTimeout: number ) { this.statusHandler = this.onStatusChanged.bind(this); - this.ioPubHandler = (_s, m) => this.ioPubEventEmitter.fire(m); this.unhandledMessageHandler = (_s, m) => { traceInfo(`Unhandled message found: ${m.header.msg_type}`); }; @@ -345,10 +353,8 @@ export abstract class BaseJupyterSession implements IJupyterSession { // Changes the current session. protected setSession(session: ISessionWithSocket | undefined, forceUpdateKernelSocketInfo: boolean = false) { const oldSession = this._session; + this.previousAnyMessageHandler?.dispose(); if (oldSession) { - if (this.ioPubHandler) { - oldSession.iopubMessage.disconnect(this.ioPubHandler); - } if (this.unhandledMessageHandler) { oldSession.unhandledMessage.disconnect(this.unhandledMessageHandler); } @@ -358,11 +364,29 @@ export abstract class BaseJupyterSession implements IJupyterSession { } this._session = session; if (session) { + if (session.kernel && this._wrappedKernel) { + this._wrappedKernel.changeKernel(session.kernel); + } + // Listen for session status changes session.statusChanged.connect(this.statusHandler); - - if (session.iopubMessage) { - session.iopubMessage.connect(this.ioPubHandler); + if (session.kernelSocketInformation.socket?.onAnyMessage) { + // These messages are sent directly to the kernel bypassing the Jupyter lab npm libraries. + // As a result, we don't get any notification that messages were sent (on the anymessage signal). + // To ensure those signals can still be used to monitor such messages, send them via a callback so that we can emit these messages on the anymessage signal. + this.previousAnyMessageHandler = session.kernelSocketInformation.socket?.onAnyMessage((msg) => { + try { + if (this._wrappedKernel) { + const jupyterLabSerialize = + require('@jupyterlab/services/lib/kernel/serialize') as typeof import('@jupyterlab/services/lib/kernel/serialize'); // NOSONAR + const message = + typeof msg.msg === 'string' ? jupyterLabSerialize.deserialize(msg.msg) : msg.msg; + this._wrappedKernel.anyMessage.emit({ direction: msg.direction, msg: message }); + } + } catch (ex) { + traceWarning(`failed to deserialize message to broadcast anymessage signal`); + } + }); } if (session.unhandledMessage) { session.unhandledMessage.connect(this.unhandledMessageHandler); @@ -443,6 +467,7 @@ export abstract class BaseJupyterSession implements IJupyterSession { this._disposed.fire(); this._disposed.dispose(); this.onStatusChangedEvent.dispose(); + this.previousAnyMessageHandler?.dispose(); } disposeAllDisposables(this.disposables); traceVerbose('Shutdown session -- complete'); diff --git a/src/kernels/common/kernelConnectionWrapper.ts b/src/kernels/common/kernelConnectionWrapper.ts new file mode 100644 index 00000000000..df0b9730a43 --- /dev/null +++ b/src/kernels/common/kernelConnectionWrapper.ts @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import type { Kernel } from '@jupyterlab/services'; +import { IDisposable } from '../../platform/common/types'; +import { BaseKernelConnectionWrapper } from '../jupyter/baseKernelConnectionWrapper'; + +export class KernelConnectionWrapper extends BaseKernelConnectionWrapper { + /** + * Use `kernelConnection` to access the value as its not a constant (can change over time). + * E.g. when restarting kernels or the like. + */ + private _kernelConnection!: Kernel.IKernelConnection; + protected get possibleKernelConnection(): undefined | Kernel.IKernelConnection { + return this._kernelConnection; + } + public get kernel() { + return this._kernelConnection; + } + + constructor(kernel: Kernel.IKernelConnection, disposables: IDisposable[]) { + super(kernel, disposables); + this._kernelConnection = kernel; + } + public changeKernel(kernel: Kernel.IKernelConnection) { + if (this.kernel === kernel) { + return; + } + this.stopHandlingKernelMessages(this.possibleKernelConnection!); + this._kernelConnection = kernel; + this.startHandleKernelMessages(kernel); + } + async shutdown(): Promise { + await this._kernelConnection.shutdown(); + } + dispose(): void { + this._kernelConnection.dispose(); + } + async interrupt(): Promise { + await this._kernelConnection.interrupt(); + } + async restart(): Promise { + await this._kernelConnection.restart(); + } + protected override startHandleKernelMessages(kernelConnection: Kernel.IKernelConnection) { + super.startHandleKernelMessages(kernelConnection); + this._kernelConnection = kernelConnection; + kernelConnection.connectionStatusChanged.connect(this.onConnectionStatusChanged, this); + kernelConnection.statusChanged.connect(this.onStatusChanged, this); + kernelConnection.disposed.connect(this.onDisposed, this); + } + protected override stopHandlingKernelMessages(kernelConnection: Kernel.IKernelConnection): void { + super.stopHandlingKernelMessages(kernelConnection); + kernelConnection.connectionStatusChanged.disconnect(this.onConnectionStatusChanged, this); + kernelConnection.statusChanged.disconnect(this.onStatusChanged, this); + kernelConnection.disposed.disconnect(this.onDisposed, this); + } + private onDisposed(connection: Kernel.IKernelConnection) { + if (connection === this.possibleKernelConnection) { + this.disposed.emit(); + } + } + private onStatusChanged(connection: Kernel.IKernelConnection, args: Kernel.Status) { + if (connection === this.possibleKernelConnection) { + this.statusChanged.emit(args); + } + } + private onConnectionStatusChanged(connection: Kernel.IKernelConnection, args: Kernel.ConnectionStatus) { + if (connection === this.possibleKernelConnection) { + this.connectionStatusChanged.emit(args); + } + } +} diff --git a/src/kernels/common/kernelSocketWrapper.ts b/src/kernels/common/kernelSocketWrapper.ts index f1034f83c11..9f24a2ca6c4 100644 --- a/src/kernels/common/kernelSocketWrapper.ts +++ b/src/kernels/common/kernelSocketWrapper.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import { EventEmitter } from 'vscode'; import * as WebSocketWS from 'ws'; import { ClassType } from '../../platform/ioc/types'; import { traceError } from '../../platform/logging'; @@ -50,7 +51,8 @@ export function KernelSocketWrapper>(SuperCl private sendHooks: ((data: any, cb?: (err?: Error) => void) => Promise)[]; private msgChain: Promise; private sendChain: Promise; - + private _onAnyMessage = new EventEmitter<{ msg: string; direction: 'send' }>(); + public onAnyMessage = this._onAnyMessage.event; constructor(...rest: any[]) { super(...rest); // Make sure the message chain is initialized @@ -67,6 +69,7 @@ export function KernelSocketWrapper>(SuperCl public sendToRealKernel(data: any, a2: any) { // This will skip the send hooks. It's coming from // the UI side. + this._onAnyMessage.fire({ msg: data, direction: 'send' }); super.send(data, a2); } diff --git a/src/kernels/debugger/kernelDebugAdapterBase.ts b/src/kernels/debugger/kernelDebugAdapterBase.ts index d2a616a8e26..e07cb305dda 100644 --- a/src/kernels/debugger/kernelDebugAdapterBase.ts +++ b/src/kernels/debugger/kernelDebugAdapterBase.ts @@ -10,6 +10,7 @@ import { DebugAdapter, DebugProtocolMessage, DebugSession, + Disposable, Event, EventEmitter, NotebookCell, @@ -33,7 +34,6 @@ import { IDebugInfoResponse } from './types'; import { sendTelemetryEvent } from '../../telemetry'; -import { IDisposable } from '../../platform/common/types'; import { traceError, traceInfo, traceInfoIfCI, traceVerbose } from '../../platform/logging'; import { assertIsDebugConfig, @@ -42,6 +42,7 @@ import { getMessageSourceAndHookIt } from '../../notebooks/debugger/helper'; import { ResourceMap } from '../../platform/vscode-path/map'; +import { IDisposable } from '../../platform/common/types'; /** * For info on the custom requests implemented by jupyter see: @@ -89,18 +90,9 @@ export abstract class KernelDebugAdapterBase implements DebugAdapter, IKernelDeb this.debugCell = notebookDocument.cellAt(configuration.__cellIndex!); } + this.jupyterSession.kernel?.iopubMessage.connect(this.onIOPubMessage, this); this.disposables.push( - this.jupyterSession.onIOPubMessage(async (msg: KernelMessage.IIOPubMessage) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const anyMsg = msg as any; - traceInfoIfCI(`Debug IO Pub message: ${JSON.stringify(msg)}`); - if (anyMsg.header.msg_type === 'debug_event') { - this.trace('event', JSON.stringify(msg)); - if (!(await this.delegate?.willSendEvent(anyMsg))) { - this.sendMessage.fire(msg.content); - } - } - }) + new Disposable(() => this.jupyterSession.kernel?.iopubMessage.disconnect(this.onIOPubMessage, this)) ); if (this.kernel) { @@ -157,6 +149,17 @@ export abstract class KernelDebugAdapterBase implements DebugAdapter, IKernelDeb traceVerbose(`[Debug] ${tag}: ${msg}`); } + async onIOPubMessage(_: unknown, msg: KernelMessage.IIOPubMessage) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const anyMsg = msg as any; + traceInfoIfCI(`Debug IO Pub message: ${JSON.stringify(msg)}`); + if (anyMsg.header.msg_type === 'debug_event') { + this.trace('event', JSON.stringify(msg)); + if (!(await this.delegate?.willSendEvent(anyMsg))) { + this.sendMessage.fire(msg.content); + } + } + } async handleMessage(message: DebugProtocol.ProtocolMessage) { try { traceInfoIfCI(`KernelDebugAdapter::handleMessage ${JSON.stringify(message, undefined, ' ')}`); diff --git a/src/kernels/kernelConnectionWrapper.ts b/src/kernels/jupyter/baseKernelConnectionWrapper.ts similarity index 72% rename from src/kernels/kernelConnectionWrapper.ts rename to src/kernels/jupyter/baseKernelConnectionWrapper.ts index 8c5c0a766c4..6a828ed19e2 100644 --- a/src/kernels/kernelConnectionWrapper.ts +++ b/src/kernels/jupyter/baseKernelConnectionWrapper.ts @@ -34,11 +34,9 @@ import { ISpecModel } from '@jupyterlab/services/lib/kernelspec/restapi'; import { JSONObject } from '@lumino/coreutils'; import { Signal } from '@lumino/signaling'; import { Disposable } from 'vscode'; -import { IDisposable } from '../platform/common/types'; -import { noop } from '../platform/common/utils/misc'; -import { IKernel } from './types'; +import { IDisposable } from '../../platform/common/types'; -export class KernelConnectionWrapper implements Kernel.IKernelConnection { +export abstract class BaseKernelConnectionWrapper implements Kernel.IKernelConnection { public readonly statusChanged = new Signal(this); public readonly connectionStatusChanged = new Signal(this); public readonly iopubMessage = new Signal>(this); @@ -48,23 +46,8 @@ export class KernelConnectionWrapper implements Kernel.IKernelConnection { return (this.possibleKernelConnection || this._previousKernelConnection).serverSettings; } public readonly disposed = new Signal(this); - /** - * Use `kernelConnection` to access the value as its not a constant (can change over time). - * E.g. when restarting kernels or the like. - */ - private _kernelConnection!: Kernel.IKernelConnection; - private readonly _previousKernelConnection: Kernel.IKernelConnection; // private _isRestarting?: boolean; - private get possibleKernelConnection(): undefined | Kernel.IKernelConnection { - if (this.kernel.session?.kernel === this._kernelConnection) { - return this._kernelConnection; - } - this.stopHandlingKernelMessages(this._kernelConnection); - if (this.kernel.session?.kernel) { - this.startHandleKernelMessages(this.kernel.session.kernel); - return this._kernelConnection; - } - } + protected abstract get possibleKernelConnection(): undefined | Kernel.IKernelConnection; private getKernelConnection(): Kernel.IKernelConnection { if (!this.possibleKernelConnection) { throw new Error( @@ -74,27 +57,8 @@ export class KernelConnectionWrapper implements Kernel.IKernelConnection { return this.possibleKernelConnection; } - constructor(readonly kernel: IKernel, disposables: IDisposable[]) { - const emiStatusChangeEvents = () => { - this.statusChanged.emit(kernel.status); - if (kernel.status === 'dead' && !kernel.disposed && !kernel.disposing) { - this.connectionStatusChanged.emit('disconnected'); - } - }; - kernel.onDisposed( - () => { - // this._isRestarting = false; - emiStatusChangeEvents(); - this.disposed.emit(); - }, - this, - disposables - ); - kernel.onStarted(emiStatusChangeEvents, this, disposables); - kernel.onRestarted(emiStatusChangeEvents, this, disposables); - kernel.onStatusChanged(emiStatusChangeEvents, this, disposables); - this._previousKernelConnection = kernel.session!.kernel!; - this.startHandleKernelMessages(kernel.session!.kernel!); + constructor(private _previousKernelConnection: Kernel.IKernelConnection, disposables: IDisposable[]) { + this.startHandleKernelMessages(_previousKernelConnection); disposables.push( new Disposable(() => { if (this.possibleKernelConnection) { @@ -103,6 +67,11 @@ export class KernelConnectionWrapper implements Kernel.IKernelConnection { }) ); } + abstract shutdown(): Promise; + abstract dispose(): void; + abstract interrupt(): Promise; + abstract restart(): Promise; + public get id(): string { return (this.possibleKernelConnection || this._previousKernelConnection).id; } @@ -110,7 +79,7 @@ export class KernelConnectionWrapper implements Kernel.IKernelConnection { return (this.possibleKernelConnection || this._previousKernelConnection).name; } public get isDisposed(): boolean { - return this.kernel.disposed; + return this.possibleKernelConnection ? this.possibleKernelConnection?.isDisposed === true : true; } public get model(): Kernel.IModel { @@ -233,55 +202,18 @@ export class KernelConnectionWrapper implements Kernel.IKernelConnection { ): void { return this.getKernelConnection().removeMessageHook(msgId, hook); } - async shutdown(): Promise { - if ( - this.kernel.kernelConnectionMetadata.kind === 'startUsingRemoteKernelSpec' || - this.kernel.kernelConnectionMetadata.kind === 'connectToLiveRemoteKernel' - ) { - await this.kernel.session?.shutdown(); - } - await this.kernel.dispose(); - } + clone( _options?: Pick ): Kernel.IKernelConnection { throw new Error('Method not implemented.'); } - dispose(): void { - this.kernel.dispose().catch(noop); - } - async interrupt(): Promise { - // Sometimes we end up starting a new session. - // Hence assume a new session was created, meaning we need to bind to the kernel connection all over again. - this.stopHandlingKernelMessages(this.possibleKernelConnection!); - - await this.kernel.interrupt(); - - if (!this.kernel.session?.kernel) { - throw new Error('Restart failed'); - } - this.startHandleKernelMessages(this.kernel.session?.kernel); - } - async restart(): Promise { - if (this.possibleKernelConnection) { - this.stopHandlingKernelMessages(this.possibleKernelConnection); - } - - // If this is a remote, then we do something special. - await this.kernel.restart(); - - if (!this.kernel.session?.kernel) { - throw new Error('Restart failed'); - } - this.startHandleKernelMessages(this.kernel.session?.kernel); - } - private startHandleKernelMessages(kernelConnection: Kernel.IKernelConnection) { - this._kernelConnection = kernelConnection; + protected startHandleKernelMessages(kernelConnection: Kernel.IKernelConnection) { kernelConnection.anyMessage.connect(this.onAnyMessage, this); kernelConnection.iopubMessage.connect(this.onIOPubMessage, this); kernelConnection.unhandledMessage.connect(this.onUnhandledMessage, this); } - private stopHandlingKernelMessages(kernelConnection: Kernel.IKernelConnection) { + protected stopHandlingKernelMessages(kernelConnection: Kernel.IKernelConnection) { kernelConnection.anyMessage.disconnect(this.onAnyMessage, this); kernelConnection.iopubMessage.disconnect(this.onIOPubMessage, this); kernelConnection.unhandledMessage.disconnect(this.onUnhandledMessage, this); diff --git a/src/kernels/jupyter/session/jupyterSession.ts b/src/kernels/jupyter/session/jupyterSession.ts index 375a85277d9..93940918d5e 100644 --- a/src/kernels/jupyter/session/jupyterSession.ts +++ b/src/kernels/jupyter/session/jupyterSession.ts @@ -1,14 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. 'use strict'; -import type { - Contents, - ContentsManager, - Kernel, - KernelSpecManager, - Session, - SessionManager -} from '@jupyterlab/services'; +import type { Contents, ContentsManager, KernelSpecManager, Session, SessionManager } from '@jupyterlab/services'; import * as uuid from 'uuid/v4'; import { CancellationToken, CancellationTokenSource } from 'vscode-jsonrpc'; import { Cancellation } from '../../../platform/common/cancellation'; @@ -76,15 +69,6 @@ export class JupyterSession extends BaseJupyterSession implements IJupyterServer // Wait for idle on this session return this.waitForIdleOnSession(this.session, timeout); } - - public override get kernel(): Kernel.IKernelConnection | undefined { - return this.session?.kernel || undefined; - } - - public get kernelId(): string { - return this.session?.kernel?.id || ''; - } - public async connect(options: { token: CancellationToken; ui: IDisplayOptions }): Promise { // Start a new session this.setSession(await this.createNewKernelSession(options)); @@ -158,7 +142,6 @@ export class JupyterSession extends BaseJupyterSession implements IJupyterServer return newSession; } - protected async createRestartSession( disableUI: boolean, session: ISessionWithSocket, diff --git a/src/kernels/raw/session/rawJupyterSession.node.ts b/src/kernels/raw/session/rawJupyterSession.node.ts index 5eab7f33b16..4afc7237b31 100644 --- a/src/kernels/raw/session/rawJupyterSession.node.ts +++ b/src/kernels/raw/session/rawJupyterSession.node.ts @@ -51,9 +51,6 @@ export class RawJupyterSession extends BaseJupyterSession { } return super.status; } - public get kernelId(): string { - return this.session?.kernel?.id || ''; - } constructor( private readonly kernelLauncher: IKernelLauncher, resource: Resource, diff --git a/src/kernels/raw/session/rawSocket.node.ts b/src/kernels/raw/session/rawSocket.node.ts index b5b2d1a424e..e8a03eb7096 100644 --- a/src/kernels/raw/session/rawSocket.node.ts +++ b/src/kernels/raw/session/rawSocket.node.ts @@ -12,6 +12,7 @@ import { IWebSocketLike } from '../../common/kernelSocketWrapper'; import { IKernelSocket } from '../../types'; import { IKernelConnection } from '../types'; import type { Channel } from '@jupyterlab/services/lib/kernel/messages'; +import { EventEmitter } from 'vscode'; function formConnectionString(config: IKernelConnection, channel: string) { const portDelimiter = config.transport === 'tcp' ? ':' : '-'; @@ -34,6 +35,8 @@ interface IChannels { * it does all serialization/deserialization itself. */ export class RawSocket implements IWebSocketLike, IKernelSocket, IDisposable { + private _onAnyMessage = new EventEmitter<{ msg: string; direction: 'send' }>(); + public onAnyMessage = this._onAnyMessage.event; public onopen: (event: { target: any }) => void = noop; public onerror: (event: { error: any; message: string; type: string; target: any }) => void = noop; public onclose: (event: { wasClean: boolean; code: number; reason: string; target: any }) => void = noop; @@ -100,7 +103,10 @@ export class RawSocket implements IWebSocketLike, IKernelSocket, IDisposable { // If from ipywidgets, this will be serialized already, so turn it back into a message so // we can add the special hash to it. const message = this.deserialize(data); - + // These messages are sent directly to the kernel bypassing the Jupyter lab npm libraries. + // As a result, we don't get any notification that messages were sent (on the anymessage signal). + // To ensure those signals can still be used to monitor such messages, send them via a callback so that we can emit these messages on the anymessage signal. + this._onAnyMessage.fire({ msg: data, direction: 'send' }); // Send this directly (don't call back into the hooks) this.sendMessage(message, true); } diff --git a/src/kernels/types.ts b/src/kernels/types.ts index 7ba266a512f..92e746f7a5e 100644 --- a/src/kernels/types.ts +++ b/src/kernels/types.ts @@ -270,7 +270,6 @@ export interface IJupyterSession extends IAsyncDisposable { isServerSession(): this is IJupyterServerSession; onSessionStatusChanged: Event; onDidDispose: Event; - onIOPubMessage: Event; interrupt(): Promise; restart(): Promise; waitForIdle(timeout: number): Promise; @@ -433,6 +432,12 @@ export interface INotebookProvider { } export interface IKernelSocket { + /** + * These messages are sent directly to the kernel bypassing the Jupyter lab npm libraries. + * As a result, we don't get any notification that messages were sent (on the anymessage signal). + * To ensure those signals can still be used to monitor such messages, send them via a callback so that we can emit these messages on the anymessage signal. + */ + onAnyMessage: Event<{ msg: string | KernelMessage.IMessage; direction: 'send' }>; // eslint-disable-next-line @typescript-eslint/no-explicit-any sendToRealKernel(data: any, cb?: (err?: Error) => void): void; /** diff --git a/src/platform/api/kernelApi.ts b/src/platform/api/kernelApi.ts index 60be576b92d..eb8ffa3fbe7 100644 --- a/src/platform/api/kernelApi.ts +++ b/src/platform/api/kernelApi.ts @@ -3,7 +3,7 @@ import { injectable, inject } from 'inversify'; import { Disposable, Event, EventEmitter, Uri } from 'vscode'; -import { KernelConnectionWrapper } from '../../kernels/kernelConnectionWrapper'; +import { KernelConnectionWrapper } from './kernelConnectionWrapper'; import { IKernelProvider, IKernel, diff --git a/src/platform/api/kernelConnectionWrapper.ts b/src/platform/api/kernelConnectionWrapper.ts new file mode 100644 index 00000000000..826351b68b8 --- /dev/null +++ b/src/platform/api/kernelConnectionWrapper.ts @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import type { Kernel } from '@jupyterlab/services'; +import { IDisposable } from '../common/types'; +import { noop } from '../common/utils/misc'; +import { BaseKernelConnectionWrapper } from '../../kernels/jupyter/baseKernelConnectionWrapper'; +import { IKernel } from '../../kernels/types'; + +export class KernelConnectionWrapper extends BaseKernelConnectionWrapper { + /** + * Use `kernelConnection` to access the value as its not a constant (can change over time). + * E.g. when restarting kernels or the like. + */ + private _kernelConnection!: Kernel.IKernelConnection; + protected get possibleKernelConnection(): undefined | Kernel.IKernelConnection { + if (this.kernel.session?.kernel === this._kernelConnection) { + return this._kernelConnection; + } + this.stopHandlingKernelMessages(this._kernelConnection); + if (this.kernel.session?.kernel) { + this.startHandleKernelMessages(this.kernel.session.kernel); + return this._kernelConnection; + } + } + + constructor(readonly kernel: IKernel, disposables: IDisposable[]) { + super(kernel.session!.kernel!, disposables); + const emiStatusChangeEvents = () => { + this.statusChanged.emit(kernel.status); + if (kernel.status === 'dead' && !kernel.disposed && !kernel.disposing) { + this.connectionStatusChanged.emit('disconnected'); + } + }; + kernel.onDisposed( + () => { + // this._isRestarting = false; + emiStatusChangeEvents(); + this.disposed.emit(); + }, + this, + disposables + ); + kernel.onStarted(emiStatusChangeEvents, this, disposables); + kernel.onRestarted(emiStatusChangeEvents, this, disposables); + kernel.onStatusChanged(emiStatusChangeEvents, this, disposables); + this.startHandleKernelMessages(kernel.session!.kernel!); + } + async shutdown(): Promise { + if ( + this.kernel.kernelConnectionMetadata.kind === 'startUsingRemoteKernelSpec' || + this.kernel.kernelConnectionMetadata.kind === 'connectToLiveRemoteKernel' + ) { + await this.kernel.session?.shutdown(); + } + await this.kernel.dispose(); + } + dispose(): void { + this.kernel.dispose().catch(noop); + } + async interrupt(): Promise { + // Sometimes we end up starting a new session. + // Hence assume a new session was created, meaning we need to bind to the kernel connection all over again. + this.stopHandlingKernelMessages(this.possibleKernelConnection!); + + await this.kernel.interrupt(); + + if (!this.kernel.session?.kernel) { + throw new Error('Restart failed'); + } + this.startHandleKernelMessages(this.kernel.session?.kernel); + } + async restart(): Promise { + if (this.possibleKernelConnection) { + this.stopHandlingKernelMessages(this.possibleKernelConnection); + } + + // If this is a remote, then we do something special. + await this.kernel.restart(); + + if (!this.kernel.session?.kernel) { + throw new Error('Restart failed'); + } + this.startHandleKernelMessages(this.kernel.session?.kernel); + } + protected override startHandleKernelMessages(kernelConnection: Kernel.IKernelConnection) { + this._kernelConnection = kernelConnection; + super.startHandleKernelMessages(kernelConnection); + } +} diff --git a/src/test/common.node.ts b/src/test/common.node.ts index 6e27e840ee8..16bf55135b7 100644 --- a/src/test/common.node.ts +++ b/src/test/common.node.ts @@ -9,14 +9,14 @@ import * as fs from 'fs-extra'; import * as path from '../platform/vscode-path/path'; import * as uuid from 'uuid/v4'; import { coerce, SemVer } from 'semver'; -import type { ConfigurationTarget, Event, TextDocument, Uri } from 'vscode'; +import type { ConfigurationTarget, TextDocument, Uri } from 'vscode'; import { IProcessService } from '../platform/common/process/types.node'; -import { IDisposable } from '../platform/common/types'; import { EXTENSION_ROOT_DIR_FOR_TESTS, IS_MULTI_ROOT_TEST, IS_PERF_TEST, IS_SMOKE_TEST } from './constants.node'; import { noop } from './core'; import { isCI } from '../platform/common/constants'; import { IWorkspaceService } from '../platform/common/application/types'; -import { waitForCondition } from './common'; + +export { createEventHandler } from './common'; const StreamZip = require('node-stream-zip'); @@ -302,87 +302,6 @@ export async function openFile(file: string): Promise { assert(vscode.window.activeTextEditor, 'No active editor'); return textDocument; } - -/** - * Helper class to test events. - * - * Usage: Assume xyz.onDidSave is the event we want to test. - * const handler = new TestEventHandler(xyz.onDidSave); - * // Do something that would trigger the event. - * assert.ok(handler.fired) - * assert.equal(handler.first, 'Args Passed to first onDidSave') - * assert.equal(handler.count, 1)// Only one should have been fired. - */ -export class TestEventHandler implements IDisposable { - public get fired() { - return this.handledEvents.length > 0; - } - public get first(): T { - return this.handledEvents[0]; - } - public get second(): T { - return this.handledEvents[1]; - } - public get last(): T { - return this.handledEvents[this.handledEvents.length - 1]; - } - public get count(): number { - return this.handledEvents.length; - } - public get all(): T[] { - return this.handledEvents; - } - private readonly handler: IDisposable; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private readonly handledEvents: any[] = []; - constructor(event: Event, private readonly eventNameForErrorMessages: string, disposables: IDisposable[] = []) { - disposables.push(this); - this.handler = event(this.listener, this); - } - public reset() { - while (this.handledEvents.length) { - this.handledEvents.pop(); - } - } - public async assertFired(waitPeriod: number = 100): Promise { - await waitForCondition(async () => this.fired, waitPeriod, `${this.eventNameForErrorMessages} event not fired`); - } - public async assertFiredExactly(numberOfTimesFired: number, waitPeriod: number = 2_000): Promise { - await waitForCondition( - async () => this.count === numberOfTimesFired, - waitPeriod, - `${this.eventNameForErrorMessages} event fired ${this.count}, expected ${numberOfTimesFired}` - ); - } - public async assertFiredAtLeast(numberOfTimesFired: number, waitPeriod: number = 2_000): Promise { - await waitForCondition( - async () => this.count >= numberOfTimesFired, - waitPeriod, - `${this.eventNameForErrorMessages} event fired ${this.count}, expected at least ${numberOfTimesFired}.` - ); - } - public atIndex(index: number): T { - return this.handledEvents[index]; - } - - public dispose() { - this.handler.dispose(); - } - - private listener(e: T) { - this.handledEvents.push(e); - } -} - -export function createEventHandler( - obj: T, - eventName: K, - disposables: IDisposable[] = [] -): T[K] extends Event ? TestEventHandler : TestEventHandler { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return new TestEventHandler(obj[eventName] as any, eventName as string, disposables) as any; -} - /** * Captures screenshots (png format) & dumpts into root directory (only on CI). * If there's a failure, it will be logged (errors are swallowed). diff --git a/src/test/common.ts b/src/test/common.ts index 7d829232a1a..6ad2b82f2dd 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -3,7 +3,9 @@ // Licensed under the MIT License. 'use strict'; +import { Event } from 'vscode'; import { IExtensionApi } from '../platform/api'; +import { IDisposable } from '../platform/common/types'; import { IServiceContainer, IServiceManager } from '../platform/ioc/types'; export interface IExtensionTestApi extends IExtensionApi { @@ -80,3 +82,83 @@ export async function waitForCondition( pendingTimers.push(timeout); }); } + +/** + * Helper class to test events. + * + * Usage: Assume xyz.onDidSave is the event we want to test. + * const handler = new TestEventHandler(xyz.onDidSave); + * // Do something that would trigger the event. + * assert.ok(handler.fired) + * assert.equal(handler.first, 'Args Passed to first onDidSave') + * assert.equal(handler.count, 1)// Only one should have been fired. + */ +export class TestEventHandler implements IDisposable { + public get fired() { + return this.handledEvents.length > 0; + } + public get first(): T { + return this.handledEvents[0]; + } + public get second(): T { + return this.handledEvents[1]; + } + public get last(): T { + return this.handledEvents[this.handledEvents.length - 1]; + } + public get count(): number { + return this.handledEvents.length; + } + public get all(): T[] { + return this.handledEvents; + } + private readonly handler: IDisposable; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private readonly handledEvents: any[] = []; + constructor(event: Event, private readonly eventNameForErrorMessages: string, disposables: IDisposable[] = []) { + disposables.push(this); + this.handler = event(this.listener, this); + } + public reset() { + while (this.handledEvents.length) { + this.handledEvents.pop(); + } + } + public async assertFired(waitPeriod: number = 100): Promise { + await waitForCondition(async () => this.fired, waitPeriod, `${this.eventNameForErrorMessages} event not fired`); + } + public async assertFiredExactly(numberOfTimesFired: number, waitPeriod: number = 2_000): Promise { + await waitForCondition( + async () => this.count === numberOfTimesFired, + waitPeriod, + `${this.eventNameForErrorMessages} event fired ${this.count}, expected ${numberOfTimesFired}` + ); + } + public async assertFiredAtLeast(numberOfTimesFired: number, waitPeriod: number = 2_000): Promise { + await waitForCondition( + async () => this.count >= numberOfTimesFired, + waitPeriod, + `${this.eventNameForErrorMessages} event fired ${this.count}, expected at least ${numberOfTimesFired}.` + ); + } + public atIndex(index: number): T { + return this.handledEvents[index]; + } + + public dispose() { + this.handler.dispose(); + } + + private listener(e: T) { + this.handledEvents.push(e); + } +} + +export function createEventHandler( + obj: T, + eventName: K, + disposables: IDisposable[] = [] +): T[K] extends Event ? TestEventHandler : TestEventHandler { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return new TestEventHandler(obj[eventName] as any, eventName as string, disposables) as any; +} diff --git a/src/test/datascience/notebook/helper.ts b/src/test/datascience/notebook/helper.ts index b271585210f..07b1d915a7a 100644 --- a/src/test/datascience/notebook/helper.ts +++ b/src/test/datascience/notebook/helper.ts @@ -239,6 +239,7 @@ export async function createEmptyPythonNotebook( await waitForKernelToGetAutoSelected(); } await deleteAllCellsAndWait(); + return vscodeNotebook.activeNotebookEditor!.notebook; } async function shutdownAllNotebooks() { diff --git a/src/test/datascience/notebook/kernelEvents.vscode.common.ts b/src/test/datascience/notebook/kernelEvents.vscode.common.ts new file mode 100644 index 00000000000..9f343f4908d --- /dev/null +++ b/src/test/datascience/notebook/kernelEvents.vscode.common.ts @@ -0,0 +1,172 @@ +/* eslint-disable no-void */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ +import { assert } from 'chai'; +import * as sinon from 'sinon'; +import { Disposable, NotebookDocument } from 'vscode'; +import { traceInfo } from '../../../platform/logging'; +import { + IConfigurationService, + IDisposable, + IWatchableJupyterSettings, + ReadWrite +} from '../../../platform/common/types'; +import { IExtensionTestApi, waitForCondition } from '../../common'; +import { initialize } from '../../initialize'; +import { + runCell, + insertCodeCell, + waitForTextOutput, + closeNotebooksAndCleanUpAfterTests, + createEmptyPythonNotebook +} from './helper'; +import { createEventHandler } from '../../common'; +import { IKernelProvider } from '../../../kernels/types'; + +/* eslint-disable @typescript-eslint/no-explicit-any, no-invalid-this */ +export function sharedKernelEventTests( + this: Mocha.Suite, + options: { + startJupyterServer: (notebook?: NotebookDocument) => Promise; + } +) { + let api: IExtensionTestApi; + const disposables: IDisposable[] = []; + let configSettings: ReadWrite; + let kernelProvider: IKernelProvider; + let previousDisableJupyterAutoStartValue: boolean; + this.timeout(120_000); + suiteSetup(async function () { + traceInfo(`Suite Setup ${this.currentTest?.title}`); + this.timeout(120_000); + try { + api = await initialize(); + sinon.restore(); + kernelProvider = api.serviceContainer.get(IKernelProvider); + const configService = api.serviceContainer.get(IConfigurationService); + configSettings = configService.getSettings(undefined) as any; + previousDisableJupyterAutoStartValue = configSettings.disableJupyterAutoStart; + configSettings.disableJupyterAutoStart = true; + traceInfo('Suite Setup (completed)'); + } catch (e) { + traceInfo('Suite Setup (failed)'); + throw e; + } + }); + // Use same notebook without starting kernel in every single test (use one for whole suite). + setup(async function () { + try { + traceInfo(`Start Test ${this.currentTest?.title}`); + sinon.restore(); + const configService = api.serviceContainer.get(IConfigurationService); + configSettings = configService.getSettings(undefined) as any; + configSettings.disableJupyterAutoStart = true; + await options.startJupyterServer(); + traceInfo(`Start Test (completed) ${this.currentTest?.title}`); + } catch (e) { + throw e; + } + }); + teardown(async function () { + traceInfo(`Ended Test ${this.currentTest?.title}`); + await closeNotebooksAndCleanUpAfterTests(disposables); + configSettings.disableJupyterAutoStart = previousDisableJupyterAutoStartValue; + traceInfo(`Ended Test (completed) ${this.currentTest?.title}`); + }); + suiteTeardown(() => closeNotebooksAndCleanUpAfterTests(disposables)); + test('Kernel Events', async () => { + const kernelCreated = createEventHandler(kernelProvider, 'onDidCreateKernel', disposables); + const kernelStarted = createEventHandler(kernelProvider, 'onDidStartKernel', disposables); + const kernelDisposed = createEventHandler(kernelProvider, 'onDidDisposeKernel', disposables); + const kernelRestarted = createEventHandler(kernelProvider, 'onDidRestartKernel', disposables); + const kernelStatusChanged = createEventHandler(kernelProvider, 'onKernelStatusChanged', disposables); + + const nb = await createEmptyPythonNotebook(disposables); + await waitForCondition(async () => !!kernelProvider.get(nb.uri), 5_000, 'Kernel not created'); + const kernel = kernelProvider.get(nb.uri)!; + const startedEvent = createEventHandler(kernel, 'onStarted', disposables); + const onPreExecuteEvent = createEventHandler(kernel, 'onPreExecute', disposables); + const onStatusChangeEvent = createEventHandler(kernel, 'onStatusChanged', disposables); + const onDisposed = createEventHandler(kernel, 'onDisposed', disposables); + const restartEvent = createEventHandler(kernel, 'onRestarted', disposables); + + const cell = await insertCodeCell('print("cell1")', { index: 0 }); + await Promise.all([runCell(cell), waitForTextOutput(cell, 'cell1')]); + + assert.isTrue(kernelCreated.fired, 'IKernelProvider.onDidCreateKernel not fired'); + assert.isTrue(kernelStarted.fired, 'IKernelProvider.onDidStartKernel not fired'); + assert.isTrue(kernelStatusChanged.fired, 'IKernelProvider.onKernelStatusChanged not fired'); + assert.isFalse(kernelRestarted.fired, 'IKernelProvider.onDidRestartKernel should not have fired'); + assert.isFalse(kernelDisposed.fired, 'IKernelProvider.onDidDisposeKernel should not have fired'); + + assert.equal(onPreExecuteEvent.first, cell, 'Incorrect cell'); + assert.isTrue(startedEvent.fired, 'IKernel.onStarted not fired'); + assert.isTrue(onPreExecuteEvent.fired, 'IKernel.onPreExecute not fired'); + assert.isTrue(onStatusChangeEvent.fired, 'IKernel.onStatusChanged not fired'); + assert.isFalse(restartEvent.fired, 'IKernel.onRestarted event should not have fired'); + assert.isFalse(onDisposed.fired, 'IKernel.onDisposed event should not have fired'); + + await kernel.restart(); + assert.isTrue(restartEvent.fired, 'IKernel.onRestarted event not fired'); + assert.isFalse(onDisposed.fired, 'IKernel.onDisposed event should not have fired'); + assert.isTrue(kernelRestarted.fired, 'IKernelProvider.onDidRestartKernel not fired'); + assert.isFalse(kernelDisposed.fired, 'IKernelProvider.onDidDisposeKernel should not have fired'); + + await kernel.dispose(); + assert.isTrue(onDisposed.fired, 'Disposed event not fired'); + assert.isTrue(kernelDisposed.fired, 'IKernelProvider.onDidDisposeKernel not fired'); + }); + test('Kernel.IKernelConnection Events', async () => { + const nb = await createEmptyPythonNotebook(disposables); + await waitForCondition(async () => !!kernelProvider.get(nb.uri), 5_000, 'Kernel not created'); + const kernel = kernelProvider.get(nb.uri)!; + const onPreExecuteEvent = createEventHandler(kernel, 'onPreExecute', disposables); + + const cell = await insertCodeCell('print("cell1")', { index: 0 }); + await Promise.all([runCell(cell), waitForTextOutput(cell, 'cell1')]); + + const kernelConnection = kernelProvider.get(nb.uri)!.session!.kernel!; + assert.strictEqual(onPreExecuteEvent.count, 1, 'Pre-execute should be fired once'); + assert.equal(onPreExecuteEvent.first, cell, 'Incorrect cell'); + + let gotAnyMessage = false; + let gotIOPubMessage = false; + let statusChanged = false; + const onAnyMessage = () => (gotAnyMessage = true); + const onIOPubMessage = () => (gotIOPubMessage = true); + const onStatusChanged = () => (statusChanged = true); + kernelConnection.anyMessage.connect(onAnyMessage); + kernelConnection.iopubMessage.connect(onIOPubMessage); + kernelConnection.statusChanged.connect(onStatusChanged); + disposables.push(new Disposable(() => void kernelConnection.anyMessage.disconnect(onAnyMessage))); + disposables.push(new Disposable(() => void kernelConnection.iopubMessage.disconnect(onIOPubMessage))); + disposables.push(new Disposable(() => void kernelConnection.statusChanged.disconnect(onStatusChanged))); + + const cell2 = await insertCodeCell('print("cell2")', { index: 0 }); + await Promise.all([runCell(cell2), waitForTextOutput(cell2, 'cell2')]); + + assert.strictEqual(onPreExecuteEvent.count, 2, 'Pre-execute should be fired twice'); + assert.equal(onPreExecuteEvent.second, cell2, 'Incorrect cell'); + + assert.isTrue(gotAnyMessage, 'AnyMessage event not fired'); + assert.isTrue(gotIOPubMessage, 'IOPubMessage event fired'); + assert.isTrue(statusChanged, 'StatusChange event fired'); + + // Restart the kernel & verify we still get the events fired. + gotAnyMessage = false; + gotIOPubMessage = false; + statusChanged = false; + await kernel.restart(); + + const cell3 = await insertCodeCell('print("cell3")', { index: 0 }); + await Promise.all([runCell(cell3), waitForTextOutput(cell3, 'cell3')]); + + assert.isTrue(gotAnyMessage, 'AnyMessage event not fired after restarting the kernel'); + assert.isTrue(gotIOPubMessage, 'IOPubMessage event fired after restarting the kernel'); + assert.isTrue(statusChanged, 'StatusChange event fired after restarting the kernel'); + }); +} diff --git a/src/test/datascience/notebook/kernelEvents.vscode.test.ts b/src/test/datascience/notebook/kernelEvents.vscode.test.ts new file mode 100644 index 00000000000..40c5a4dfb1c --- /dev/null +++ b/src/test/datascience/notebook/kernelEvents.vscode.test.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { startJupyterServer } from './helper.node'; +import { sharedKernelEventTests } from './kernelEvents.vscode.common'; + +/* eslint-disable @typescript-eslint/no-explicit-any, no-invalid-this */ +suite('Kernel Events', function () { + sharedKernelEventTests.bind(this)({ startJupyterServer }); +}); diff --git a/src/test/datascience/notebook/kernelEvents.vscode.web.test.ts b/src/test/datascience/notebook/kernelEvents.vscode.web.test.ts new file mode 100644 index 00000000000..e727cd21b32 --- /dev/null +++ b/src/test/datascience/notebook/kernelEvents.vscode.web.test.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { startJupyterServer } from './helper.web'; +import { sharedKernelEventTests } from './kernelEvents.vscode.common'; + +/* eslint-disable @typescript-eslint/no-explicit-any, no-invalid-this */ +suite('Kernel Events', function () { + sharedKernelEventTests.bind(this)({ startJupyterServer }); +});