diff --git a/src/client/debugger/jupyter/debugControllers.ts b/src/client/debugger/jupyter/debugControllers.ts index d4745232adf..aa5e6740915 100644 --- a/src/client/debugger/jupyter/debugControllers.ts +++ b/src/client/debugger/jupyter/debugControllers.ts @@ -57,6 +57,7 @@ export class RunByLineController implements IDebuggingDelegate { public continue(): void { if (typeof this.lastPausedThreadId !== 'number') { traceVerbose(`No paused thread, can't do RBL`); + this.stop(); return; } @@ -64,6 +65,8 @@ export class RunByLineController implements IDebuggingDelegate { } public stop(): void { + // When debugpy gets stuck, running a cell fixes it and allows us to start another debugging session + void this.kernel.executeHidden('pass'); this.debugAdapter.disconnect(); } @@ -173,10 +176,8 @@ async function cellDebugSetup( ): Promise { // remove this if when https://github.com/microsoft/debugpy/issues/706 is fixed and ipykernel ships it // executing this code restarts debugpy and fixes https://github.com/microsoft/vscode-jupyter/issues/7251 - if (kernel) { - const code = 'import debugpy\ndebugpy.debug_this_thread()'; - await kernel.executeHidden(code); - } + const code = 'import debugpy\ndebugpy.debug_this_thread()'; + await kernel.executeHidden(code); await debugAdapter.dumpCell(debugCell.index); } diff --git a/src/client/debugger/jupyter/debuggingManager.ts b/src/client/debugger/jupyter/debuggingManager.ts index 3b7cd4d1783..911c9052718 100644 --- a/src/client/debugger/jupyter/debuggingManager.ts +++ b/src/client/debugger/jupyter/debuggingManager.ts @@ -15,7 +15,8 @@ import { ProgressLocation, DebugAdapterDescriptor, Event, - EventEmitter + EventEmitter, + NotebookEditor } from 'vscode'; import * as path from 'path'; import { IKernel, IKernelProvider } from '../../datascience/jupyter/kernels/types'; @@ -51,6 +52,7 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb private notebookToDebugger = new Map(); private notebookToDebugAdapter = new Map(); private notebookToRunByLineController = new Map(); + private notebookInProgress = new Set(); private cache = new Map(); private readonly disposables: IDisposable[] = []; private _doneDebugging = new EventEmitter(); @@ -81,9 +83,9 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb workspace.onDidCloseNotebookDocument(async (document) => { const dbg = this.notebookToDebugger.get(document); if (dbg) { + await dbg.stop(); this.updateToolbar(false); this.updateCellToolbar(false); - await dbg.stop(); } }), @@ -94,16 +96,7 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb this.commandManager.registerCommand(DSCommands.DebugNotebook, async () => { const editor = this.vscNotebook.activeNotebookEditor; - if (editor) { - if (await this.checkForIpykernel6(editor.document)) { - this.updateToolbar(true); - void this.startDebugging(editor.document); - } else { - void this.installIpykernel6(); - } - } else { - void this.appShell.showErrorMessage(DataScience.noNotebookToDebug()); - } + await this.tryToStartDebugging(KernelDebugMode.Everything, editor); }), this.commandManager.registerCommand(DSCommands.RunByLine, async (cell: NotebookCell | undefined) => { @@ -120,17 +113,7 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb return; } - if (editor) { - if (await this.checkForIpykernel6(editor.document, DataScience.startingRunByLine())) { - this.updateToolbar(true); - this.updateCellToolbar(true); - await this.startDebuggingCell(editor.document, KernelDebugMode.RunByLine, cell); - } else { - void this.installIpykernel6(); - } - } else { - void this.appShell.showErrorMessage(DataScience.noNotebookToDebug()); - } + await this.tryToStartDebugging(KernelDebugMode.RunByLine, editor, cell); }), this.commandManager.registerCommand(DSCommands.RunByLineNext, (cell: NotebookCell | undefined) => { @@ -146,6 +129,10 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb return; } + if (this.notebookInProgress.has(cell.notebook)) { + return; + } + const controller = this.notebookToRunByLineController.get(cell.notebook); if (controller && controller.debugCell.document.uri.toString() === cell.document.uri.toString()) { controller.continue(); @@ -157,7 +144,9 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb if (editor) { const controller = this.notebookToRunByLineController.get(editor.document); if (controller) { - sendTelemetryEvent(DebuggingTelemetry.endedSession, undefined, { reason: 'withKeybinding' }); + sendTelemetryEvent(DebuggingTelemetry.endedSession, undefined, { + reason: 'withKeybinding' + }); controller.stop(); } } @@ -177,16 +166,7 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb return; } - if (editor) { - if (await this.checkForIpykernel6(editor.document)) { - this.updateToolbar(true); - void this.startDebuggingCell(editor.document, KernelDebugMode.Cell, cell); - } else { - void this.installIpykernel6(); - } - } else { - void this.appShell.showErrorMessage(DataScience.noNotebookToDebug()); - } + await this.tryToStartDebugging(KernelDebugMode.Cell, editor, cell); }) ); } @@ -232,6 +212,59 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb this.runByLineInProgress.set(runningByLine).ignoreErrors(); } + private async tryToStartDebugging(mode: KernelDebugMode, editor?: NotebookEditor, cell?: NotebookCell) { + if (!editor) { + void this.appShell.showErrorMessage(DataScience.noNotebookToDebug()); + return; + } + + if (this.notebookInProgress.has(editor.document)) { + return; + } + + if (this.isDebugging(editor.document)) { + this.updateToolbar(true); + if (mode === KernelDebugMode.RunByLine) { + this.updateCellToolbar(true); + } + return; + } + + try { + this.notebookInProgress.add(editor.document); + if ( + await this.checkForIpykernel6( + editor.document, + mode === KernelDebugMode.RunByLine ? DataScience.startingRunByLine() : undefined + ) + ) { + switch (mode) { + case KernelDebugMode.Everything: + await this.startDebugging(editor.document); + this.updateToolbar(true); + break; + case KernelDebugMode.Cell: + if (cell) { + await this.startDebuggingCell(editor.document, KernelDebugMode.Cell, cell); + this.updateToolbar(true); + } + break; + case KernelDebugMode.RunByLine: + if (cell) { + await this.startDebuggingCell(editor.document, KernelDebugMode.RunByLine, cell); + this.updateToolbar(true); + this.updateCellToolbar(true); + } + break; + } + } else { + void this.installIpykernel6(); + } + } finally { + this.notebookInProgress.delete(editor.document); + } + } + private async startDebuggingCell( doc: NotebookDocument, mode: KernelDebugMode.Cell | KernelDebugMode.RunByLine, @@ -286,13 +319,13 @@ export class DebuggingManager implements IExtensionSingleActivationService, IDeb } private async endSession(session: DebugSession) { - void this.updateToolbar(false); - void this.updateCellToolbar(false); this._doneDebugging.fire(); for (const [doc, dbg] of this.notebookToDebugger.entries()) { if (dbg && session.id === (await dbg.session).id) { this.notebookToDebugger.delete(doc); this.notebookToDebugAdapter.delete(doc); + this.updateToolbar(false); + this.updateCellToolbar(false); break; } }