Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement restart #11668

Merged
merged 13 commits into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -288,13 +288,6 @@
"icon": "$(debug-start)",
"enablement": "jupyter.development && notebookType == jupyter-notebook && isWorkspaceTrusted && jupyter.replayLogLoaded && !jupyter.webExtension"
},
{
"command": "jupyter.debugNotebook",
"title": "%jupyter.command.jupyter.debug.title%",
"icon": "$(bug)",
"category": "Jupyter",
"enablement": "notebookKernelCount > 0 && resource not in jupyter.notebookeditor.runByLineDocuments"
},
{
"command": "jupyter.filterKernels",
"title": "%jupyter.command.jupyter.filterKernels.title%",
Expand Down Expand Up @@ -1099,11 +1092,6 @@
"title": "%jupyter.commandPalette.jupyter.replayPylanceLog.title%",
"when": "jupyter.development && isWorkspaceTrusted"
},
{
"command": "jupyter.debugNotebook",
"title": "%jupyter.command.jupyter.debug.title%",
"when": "false"
},
{
"command": "jupyter.interactive.copyCell",
"when": "false"
Expand Down
3 changes: 1 addition & 2 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,10 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu
NotebookDocument | undefined
];
[DSCommands.SelectNativeJupyterUriFromToolBar]: [];
[DSCommands.DebugNotebook]: [];
[DSCommands.RunByLine]: [NotebookCell];
[DSCommands.RunAndDebugCell]: [NotebookCell];
[DSCommands.RunByLineNext]: [NotebookCell];
[DSCommands.RunByLineStop]: [];
[DSCommands.RunByLineStop]: [NotebookCell];
[DSCommands.ReplayPylanceLog]: [Uri];
[DSCommands.ReplayPylanceLogStep]: [];
[DSCommands.InstallPythonExtensionViaKernelPicker]: [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import { NotebookCell } from 'vscode';
import { DebugProtocol } from 'vscode-debugprotocol';
import { IKernel } from '../../../kernels/types';
import { DebuggingTelemetry } from '../../../notebooks/debugger/constants';
import { isJustMyCodeNotification } from '../../../notebooks/debugger/debugCellControllers';
import { isJustMyCodeNotification } from '../../../notebooks/debugger/controllers/debugCellController';
import { IDebuggingDelegate, IKernelDebugAdapter } from '../../../notebooks/debugger/debuggingTypes';
import { cellDebugSetup } from '../../../notebooks/debugger/helper';
import { createDeferred } from '../../../platform/common/utils/async';
import { traceVerbose } from '../../../platform/logging';
import { sendTelemetryEvent } from '../../../telemetry';
import { getInteractiveCellMetadata } from '../../helpers';
Expand All @@ -18,8 +17,6 @@ import { getInteractiveCellMetadata } from '../../helpers';
* Dumping a cell is how the IPython kernel determines the file path of a cell
*/
export class DebugCellController implements IDebuggingDelegate {
private readonly _ready = createDeferred<void>();
public readonly ready = this._ready.promise;
private cellDumpInvoked?: boolean;
constructor(
private readonly debugAdapter: IKernelDebugAdapter,
Expand All @@ -45,7 +42,7 @@ export class DebugCellController implements IDebuggingDelegate {
}

private debugCellDumped?: Promise<void>;
public async willSendRequest(request: DebugProtocol.Request): Promise<void> {
public async willSendRequest(request: DebugProtocol.Request): Promise<boolean> {
const metadata = getInteractiveCellMetadata(this.debugCell);
if (request.command === 'setBreakpoints' && metadata && metadata.generatedCode && !this.cellDumpInvoked) {
if (!this.debugCellDumped) {
Expand All @@ -58,7 +55,8 @@ export class DebugCellController implements IDebuggingDelegate {
this.debugCellDumped = cellDebugSetup(this.kernel, this.debugAdapter);
}
await this.debugCellDumped;
this._ready.resolve();
}

return false;
}
}
160 changes: 66 additions & 94 deletions src/interactive-window/debugger/jupyter/debuggingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,53 @@

import { inject, injectable } from 'inversify';
import {
NotebookDocument,
debug,
DebugAdapterDescriptor,
DebugAdapterInlineImplementation,
DebugSession,
NotebookCell,
DebugSessionOptions,
DebugAdapterDescriptor,
NotebookEditor,
debug
NotebookCell,
NotebookDocument,
NotebookEditor
} from 'vscode';
import { IKernelProvider } from '../../../kernels/types';
import { IControllerLoader, IControllerSelection } from '../../../notebooks/controllers/types';
import { pythonIWKernelDebugAdapter } from '../../../notebooks/debugger/constants';
import { Debugger } from '../../../notebooks/debugger/debugger';
import { DebuggingManagerBase } from '../../../notebooks/debugger/debuggingManagerBase';
import {
IDebuggingManager,
KernelDebugMode,
IKernelDebugAdapterConfig,
IDebugLocationTrackerFactory
IDebugLocationTrackerFactory,
IInteractiveWindowDebugConfig,
KernelDebugMode
} from '../../../notebooks/debugger/debuggingTypes';
import { IKernelProvider } from '../../../kernels/types';
import { IpykernelCheckResult, assertIsDebugConfig } from '../../../notebooks/debugger/helper';
import { KernelDebugAdapter } from './kernelDebugAdapter';
import { assertIsInteractiveWindowDebugConfig, IpykernelCheckResult } from '../../../notebooks/debugger/helper';
import { IExtensionSingleActivationService } from '../../../platform/activation/types';
import {
ICommandManager,
IApplicationShell,
IVSCodeNotebook,
IDebugService
ICommandManager,
IDebugService,
IVSCodeNotebook
} from '../../../platform/common/application/types';
import { IPlatformService } from '../../../platform/common/platform/types';
import { IConfigurationService } from '../../../platform/common/types';
import { DataScience } from '../../../platform/common/utils/localize';
import { traceInfoIfCI, traceInfo, traceError } from '../../../platform/logging';
import { noop } from '../../../platform/common/utils/misc';
import { IServiceContainer } from '../../../platform/ioc/types';
import { traceError, traceInfo, traceInfoIfCI } from '../../../platform/logging';
import * as path from '../../../platform/vscode-path/path';
import { DebugCellController } from './debugCellControllers';
import { DebuggingManagerBase } from '../../../notebooks/debugger/debuggingManagerBase';
import { IConfigurationService } from '../../../platform/common/types';
import { IFileGeneratedCodes } from '../../editor-integration/types';
import { buildSourceMap } from '../helper';
import { noop } from '../../../platform/common/utils/misc';
import { IInteractiveWindowDebuggingManager } from '../../types';
import { IControllerLoader, IControllerSelection } from '../../../notebooks/controllers/types';
import { IServiceContainer } from '../../../platform/ioc/types';
import { buildSourceMap } from '../helper';
import { DebugCellController } from './debugCellController';
import { KernelDebugAdapter } from './kernelDebugAdapter';

/**
* The DebuggingManager maintains the mapping between notebook documents and debug sessions.
*/
@injectable()
export class InteractiveWindowDebuggingManager
extends DebuggingManagerBase
implements IExtensionSingleActivationService, IDebuggingManager, IInteractiveWindowDebuggingManager
implements IExtensionSingleActivationService, IInteractiveWindowDebuggingManager
{
public constructor(
@inject(IKernelProvider) kernelProvider: IKernelProvider,
Expand Down Expand Up @@ -85,102 +85,78 @@ export class InteractiveWindowDebuggingManager
})
);
}

public getDebugMode(_notebook: NotebookDocument): KernelDebugMode | undefined {
return KernelDebugMode.InteractiveWindow;
}

public async start(editor: NotebookEditor, cell: NotebookCell) {
traceInfoIfCI(`Starting debugging IW`);

if (this.notebookInProgress.has(editor.notebook)) {
traceInfo(`Cannot start debugging. Already debugging this notebook`);
return;
}

if (this.isDebugging(editor.notebook)) {
traceInfo(`Cannot start debugging. Already debugging this notebook document. Toolbar should update`);
return;
}

const checkIpykernelAndStart = async (allowSelectKernel = true): Promise<void> => {
const ipykernelResult = await this.checkForIpykernel6(editor.notebook);
switch (ipykernelResult) {
case IpykernelCheckResult.NotInstalled:
// User would have been notified about this, nothing more to do.
return;
case IpykernelCheckResult.Outdated:
case IpykernelCheckResult.Unknown: {
this.promptInstallIpykernel6().then(noop, noop);
return;
}
case IpykernelCheckResult.Ok: {
await this.startDebuggingCell(editor.notebook, cell);
return;
}
case IpykernelCheckResult.ControllerNotSelected: {
if (allowSelectKernel) {
await this.commandManager.executeCommand('notebook.selectKernel', { notebookEditor: editor });
await checkIpykernelAndStart(false);
}
}
}
};

try {
this.notebookInProgress.add(editor.notebook);
await checkIpykernelAndStart();
} catch (e) {
traceInfo(`Error starting debugging: ${e}`);
} finally {
this.notebookInProgress.delete(editor.notebook);
const ipykernelResult = await this.checkIpykernelAndPrompt(cell);
if (ipykernelResult === IpykernelCheckResult.Ok) {
await this.startDebuggingCell(editor.notebook, cell);
}
}

private async startDebuggingCell(doc: NotebookDocument, cell: NotebookCell) {
const settings = this.configService.getSettings(doc.uri);
const config: IKernelDebugAdapterConfig = {
const config: IInteractiveWindowDebugConfig = {
type: pythonIWKernelDebugAdapter,
name: path.basename(doc.uri.toString()),
request: 'attach',
justMyCode: settings.debugJustMyCode,
__interactiveWindowNotebookUri: doc.uri.toString(),
__notebookUri: doc.uri.toString(),
// add a property to the config to know if the session is runByLine
__mode: KernelDebugMode.InteractiveWindow,
__cellIndex: cell.index
};
const opts: DebugSessionOptions = { suppressSaveBeforeStart: true };
return this.startDebuggingConfig(doc, config, opts);
return this.startDebuggingConfig(config, opts);
}

protected override async createDebugAdapterDescriptor(
session: DebugSession
): Promise<DebugAdapterDescriptor | undefined> {
const config = session.configuration;
assertIsDebugConfig(config);

const activeDoc = config.__interactiveWindowNotebookUri
? this.vscNotebook.notebookDocuments.find(
(doc) => doc.uri.toString() === config.__interactiveWindowNotebookUri
)
: this.vscNotebook.activeNotebookEditor?.notebook;
if (!activeDoc || typeof config.__cellIndex !== 'number') {
// This cannot happen.
protected async createDebugAdapterDescriptor(session: DebugSession): Promise<DebugAdapterDescriptor | undefined> {
const config = session.configuration as IInteractiveWindowDebugConfig;
assertIsInteractiveWindowDebugConfig(config);

const notebook = this.vscNotebook.notebookDocuments.find((doc) => doc.uri.toString() === config.__notebookUri);
if (!notebook || typeof config.__cellIndex !== 'number') {
traceError('Invalid debug session for debugging of IW using Jupyter Protocol');
return;
}

// TODO we apparently always have a kernel here, clean up typings
const kernel = await this.ensureKernelIsRunning(activeDoc);
const debug = this.getDebuggerByUri(activeDoc);
if (!debug) {
if (this.notebookInProgress.has(notebook)) {
traceInfo(`Cannot start debugging. Already debugging this notebook`);
return;
}

if (this.isDebugging(notebook)) {
traceInfo(`Cannot start debugging. Already debugging this notebook document. Toolbar should update`);
return;
}

this.notebookToDebugger.set(notebook, new Debugger(notebook, config, session));
try {
this.notebookInProgress.add(notebook);
return await this.doCreateDebugAdapterDescriptor(config, session, notebook);
} finally {
this.notebookInProgress.delete(notebook);
}
}

private async doCreateDebugAdapterDescriptor(
config: IInteractiveWindowDebugConfig,
session: DebugSession,
notebook: NotebookDocument
): Promise<DebugAdapterDescriptor | undefined> {
const kernel = await this.ensureKernelIsRunning(notebook);
if (!kernel?.session) {
this.appShell.showInformationMessage(DataScience.kernelWasNotStarted()).then(noop, noop);
return;
}
const adapter = new KernelDebugAdapter(
session,
debug.document,
notebook,
kernel.session,
kernel,
this.platform,
Expand All @@ -190,22 +166,18 @@ export class InteractiveWindowDebuggingManager

this.disposables.push(adapter.onDidEndSession(this.endSession.bind(this)));

// Wait till we're attached before resolving the session
const cell = activeDoc.cellAt(config.__cellIndex);
const cell = notebook.cellAt(config.__cellIndex);
const controller = new DebugCellController(adapter, cell, kernel!);
adapter.setDebuggingDelegate(controller);
controller.ready
.then(() => debug.resolve(session))
.catch((ex) => console.error('Failed waiting for controller to be ready', ex));
adapter.setDebuggingDelegates([controller]);

this.trackDebugAdapter(activeDoc, adapter);
this.trackDebugAdapter(notebook, adapter);
return new DebugAdapterInlineImplementation(adapter);
}

// TODO: This will likely be needed for mapping breakpoints and such
public async updateSourceMaps(notebookEditor: NotebookEditor, hashes: IFileGeneratedCodes[]): Promise<void> {
// Make sure that we have an active debugging session at this point
let debugSession = await this.getDebugSession(notebookEditor.notebook);
let debugSession = this.getDebugSession(notebookEditor.notebook);
if (debugSession) {
traceInfoIfCI(`Sending debug request for source map`);
await Promise.all(
Expand Down
Loading