From 92f3172ea3ce4ae22ce7328cd302e1a524f8bd14 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Wed, 26 Jun 2019 08:19:56 -0700 Subject: [PATCH 01/14] first pass of debugging --- package.json | 17 ++++ package.nls.json | 1 + src/client/common/application/commands.ts | 1 + src/client/common/types.ts | 1 + src/client/datascience/constants.ts | 1 + src/client/datascience/datascience.ts | 17 ++++ .../editor-integration/codewatcher.ts | 45 ++++++++++ .../interactive-window/interactiveWindow.ts | 90 +++++++++++++++++++ .../datascience/jupyter/jupyterExecution.ts | 4 +- .../datascience/jupyter/jupyterServer.ts | 32 +++++++ src/client/datascience/types.ts | 12 +++ 11 files changed, 220 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index be68ab4f7aec..6b012ce5e827 100644 --- a/package.json +++ b/package.json @@ -328,6 +328,11 @@ "title": "%python.command.python.datascience.runallcellsabove.palette.title%", "category": "Python" }, + { + "command": "python.datascience.debugcurrentcell.palette", + "title": "%python.command.python.datascience.debugcurrentcell.palette.title%", + "category": "Python" + }, { "command": "python.datascience.execSelectionInteractive", "title": "%python.command.python.datascience.execSelectionInteractive.title%", @@ -616,6 +621,12 @@ "category": "Python", "when": "python.datascience.hascodecells && python.datascience.featureenabled" }, + { + "command": "python.datascience.debugcurrentcell.palette", + "title": "%python.command.python.datascience.debugcurrentcell.palette.title%", + "category": "Python", + "when": "python.datascience.hascodecells && python.datascience.featureenabled" + }, { "command": "python.datascience.showhistorypane", "title": "%python.command.python.datascience.showhistorypane.title%", @@ -1327,6 +1338,12 @@ "description": "Allow for connecting the Python Interactive window to a https Jupyter server that does not have valid certificates. This can be a security risk, so only use for known and trusted servers.", "scope": "resource" }, + "python.dataScience.enableDebugging": { + "type": "boolean", + "default": false, + "description": "When starting a Python Interactive session import and enable the ptvsd debugger in the kernel.", + "scope": "resource" + }, "python.disableInstallationCheck": { "type": "boolean", "default": false, diff --git a/package.nls.json b/package.nls.json index 2bd9ebdb872c..383c6cfdae2d 100644 --- a/package.nls.json +++ b/package.nls.json @@ -33,6 +33,7 @@ "python.command.python.datascience.runcellandallbelow.title": "Run Below", "python.command.python.datascience.runallcellsabove.palette.title": "Run Cells Above Current Cell", "python.command.python.datascience.runcurrentcellandallbelow.palette.title": "Run Current Cell and Below", + "python.command.python.datascience.debugcurrentcell.palette.title": "Debug Current Cell", "python.command.python.datascience.runtoline.title": "Run To Line in Python Interactive window", "python.command.python.datascience.runfromline.title": "Run From Line in Python Interactive window", "python.command.python.datascience.runcurrentcell.title": "Run Current Cell", diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index c8d7c534a70d..e9bcbae42c70 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -100,6 +100,7 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu [DSCommands.RunCellAndAllBelow]: [string, number, number]; [DSCommands.RunAllCellsAbovePalette]: []; [DSCommands.RunCellAndAllBelowPalette]: []; + [DSCommands.DebugCurrentCellPalette]: []; [DSCommands.RunToLine]: [string, number, number]; [DSCommands.RunFromLine]: [string, number, number]; [DSCommands.ImportNotebook]: [undefined | Uri, undefined | CommandSource]; diff --git a/src/client/common/types.ts b/src/client/common/types.ts index 8d35997b6670..307c6d41dd25 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -319,6 +319,7 @@ export interface IDataScienceSettings { autoPreviewNotebooksInInteractivePane?: boolean; allowUnauthorizedRemoteConnection?: boolean; askForKernelRestart?: boolean; + enableDebugging?: boolean; } export const IConfigurationService = Symbol('IConfigurationService'); diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 7cb7eaad4ff0..ad4b9236a364 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -33,6 +33,7 @@ export namespace Commands { export const ExecSelectionInInteractiveWindow = 'python.datascience.execSelectionInteractive'; export const RunFileInInteractiveWindows = 'python.datascience.runFileInteractive'; export const AddCellBelow = 'python.datascience.addcellbelow'; + export const DebugCurrentCellPalette = 'python.datascience.debugcurrentcell.palette'; } export namespace EditorContexts { diff --git a/src/client/datascience/datascience.ts b/src/client/datascience/datascience.ts index 445f6ed6ad15..2f3aaeb7adf6 100644 --- a/src/client/datascience/datascience.ts +++ b/src/client/datascience/datascience.ts @@ -331,6 +331,21 @@ export class DataScience implements IDataScience { return this.dataScienceCodeLensProvider.getCodeWatcher(activeEditor.document); } + private async debugCurrentCell(): Promise { + this.dataScienceSurveyBanner.showBanner().ignoreErrors(); + + const currentCodeLens = this.getCurrentCodeLens(); + if (currentCodeLens) { + const activeCodeWatcher = this.getCurrentCodeWatcher(); + if (activeCodeWatcher) { + //return activeCodeWatcher.runCellAndAllBelow(currentCodeLens.range.start.line, currentCodeLens.range.start.character); + return activeCodeWatcher.debugCurrentCell(); + } + } else { + return Promise.resolve(); + } + } + private registerCommands(): void { let disposable = this.commandManager.registerCommand(Commands.RunAllCells, this.runAllCells, this); this.disposableRegistry.push(disposable); @@ -360,6 +375,8 @@ export class DataScience implements IDataScience { this.disposableRegistry.push(disposable); disposable = this.commandManager.registerCommand(Commands.AddCellBelow, this.addCellBelow, this); this.disposableRegistry.push(disposable); + disposable = this.commandManager.registerCommand(Commands.DebugCurrentCellPalette, this.debugCurrentCell, this); + this.disposableRegistry.push(disposable); this.commandListeners.forEach((listener: IDataScienceCommandListener) => { listener.register(this.commandManager); }); diff --git a/src/client/datascience/editor-integration/codewatcher.ts b/src/client/datascience/editor-integration/codewatcher.ts index 32586ec7b540..03344ae2e331 100644 --- a/src/client/datascience/editor-integration/codewatcher.ts +++ b/src/client/datascience/editor-integration/codewatcher.ts @@ -93,6 +93,15 @@ export class CodeWatcher implements ICodeWatcher { return this.codeLenses; } + public async debugCurrentCell() { + if (!this.documentManager.activeTextEditor || !this.documentManager.activeTextEditor.document) { + return Promise.resolve(); + } + + // Run the cell that matches the current cursor position. + return this.debugMatchingCell(this.documentManager.activeTextEditor.selection, false); + } + @captureTelemetry(Telemetry.RunAllCells) public async runAllCells() { // Run all of our code lenses, they should always be ordered in the file so we can just @@ -250,6 +259,42 @@ export class CodeWatcher implements ICodeWatcher { } } + // IANHU: Duplicating too much with run commands, perhaps debug is a parameter on run? + private async debugMatchingCell(range: Range, advance?: boolean) { + const currentRunCellLens = this.getCurrentCellLens(range.start); + const nextRunCellLens = this.getNextCellLens(range.start); + + if (currentRunCellLens) { + // Move the next cell if allowed. + if (advance) { + // Either use the next cell that we found, or add a new one into the document + let nextRange: Range; + if (!nextRunCellLens) { + nextRange = this.createNewCell(currentRunCellLens.range); + } else { + nextRange = nextRunCellLens.range; + } + + if (nextRange) { + this.advanceToRange(nextRange); + } + } + + // Run the cell after moving the selection + if (this.document) { + // Use that to get our code. + const code = this.document.getText(currentRunCellLens.range); + + try { + const activeInteractiveWindow = await this.interactiveWindowProvider.getOrCreateActive(); + await activeInteractiveWindow.debugCode(code, this.getFileName(), range.start.line, this.documentManager.activeTextEditor); + } catch (err) { + this.handleError(err); + } + } + } + } + private async runMatchingCell(range: Range, advance?: boolean) { const currentRunCellLens = this.getCurrentCellLens(range.start); const nextRunCellLens = this.getNextCellLens(range.start); diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index 6e099aa5b8f3..9b25641dc4ed 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -183,6 +183,11 @@ export class InteractiveWindow extends WebViewHost im return this.submitCode(code, file, line, undefined, editor); } + public debugCode(code: string, file: string, line: number, editor?: TextEditor) : Promise { + // Call the internal method. + return this.debugCodeInternal(code, file, line, undefined, editor); + } + // tslint:disable-next-line: no-any no-empty cyclomatic-complexity max-func-body-length public onMessage(message: string, payload: any) { switch (message) { @@ -776,6 +781,91 @@ export class InteractiveWindow extends WebViewHost im } } + private async debugCodeInternal(code: string, file: string, line: number, id?: string, _editor?: TextEditor) : Promise { + this.logger.logInformation(`Submitting code for ${this.id}`); + + // Start a status item + const status = this.setStatus(localize.DataScience.executingCode()); + + // Transmit this submission to all other listeners (in a live share session) + if (!id) { + id = uuid(); + this.shareMessage(InteractiveWindowMessages.RemoteAddCode, {code, file, line, id, originator: this.id}); + } + + // Create a deferred object that will wait until the status is disposed + const finishedAddingCode = createDeferred(); + const actualDispose = status.dispose.bind(status); + status.dispose = () => { + finishedAddingCode.resolve(); + actualDispose(); + }; + + try { + + // Make sure we're loaded first. + try { + this.logger.logInformation('Waiting for jupyter server and web panel ...'); + await this.loadPromise; + } catch (exc) { + // We should dispose ourselves if the load fails. Othewise the user + // updates their install and we just fail again because the load promise is the same. + this.dispose(); + + throw exc; + } + + // Then show our webpanel + await this.show(); + + // Add our sys info if necessary + if (file !== Identifiers.EmptyFileName) { + await this.addSysInfo(SysInfoReason.Start); + } + + if (this.jupyterServer) { + // Before we try to execute code make sure that we have an initial directory set + // Normally set via the workspace, but we might not have one here if loading a single loose file + if (file !== Identifiers.EmptyFileName) { + await this.jupyterServer.setInitialDirectory(path.dirname(file)); + } + + // Check if we have a debugger port set up + + // Attempt to evaluate this cell in the jupyter notebook + const observable = this.jupyterServer.executeObservable(code, file, line, id, false); + + // Indicate we executed some code + this.executeEvent.fire(code); + + // Sign up for cell changes + observable.subscribe( + (cells: ICell[]) => { + this.onAddCodeEvent(cells, undefined); + }, + (error) => { + status.dispose(); + if (!(error instanceof CancellationError)) { + this.applicationShell.showErrorMessage(error.toString()); + } + }, + () => { + // Indicate executing until this cell is done. + status.dispose(); + }); + + // Wait for the cell to finish + await finishedAddingCode.promise; + traceInfo(`Finished execution for ${id}`); + } + } catch (err) { + status.dispose(); + + const message = localize.DataScience.executingCodeFailure().format(err); + this.applicationShell.showErrorMessage(message); + } + } + private setStatus = (message: string): Disposable => { const result = this.statusProvider.set(message); this.potentiallyUnfinishedStatus.push(result); diff --git a/src/client/datascience/jupyter/jupyterExecution.ts b/src/client/datascience/jupyter/jupyterExecution.ts index 38ff5b87b4c0..d4d65ab4aea1 100644 --- a/src/client/datascience/jupyter/jupyterExecution.ts +++ b/src/client/datascience/jupyter/jupyterExecution.ts @@ -139,6 +139,7 @@ export class JupyterExecutionBase implements IJupyterExecution { // Try to connect to our jupyter process. Check our setting for the number of tries let tryCount = 0; const maxTries = this.configuration.getSettings().datascience.jupyterLaunchRetries; + const enableDebugging = this.configuration.getSettings().datascience.enableDebugging ? this.configuration.getSettings().datascience.enableDebugging : false; while (tryCount < maxTries) { try { // Start or connect to the process @@ -154,7 +155,8 @@ export class JupyterExecutionBase implements IJupyterExecution { kernelSpec: startInfo.kernelSpec, workingDir: options ? options.workingDir : undefined, uri: options ? options.uri : undefined, - purpose: options ? options.purpose : uuid() + purpose: options ? options.purpose : uuid(), + enableDebugging: enableDebugging }; traceInfo(`Connecting to process for ${options ? options.purpose : 'unknown type of'} server`); diff --git a/src/client/datascience/jupyter/jupyterServer.ts b/src/client/datascience/jupyter/jupyterServer.ts index e5cc6d58a16f..e5de3c2f54d6 100644 --- a/src/client/datascience/jupyter/jupyterServer.ts +++ b/src/client/datascience/jupyter/jupyterServer.ts @@ -28,6 +28,7 @@ import { ICell, IConnection, IDataScience, + IDebuggerConnectInfo, IJupyterSession, IJupyterSessionManager, INotebookCompletion, @@ -132,6 +133,7 @@ export class JupyterServerBase implements INotebookServer { private connectPromise: Deferred = createDeferred(); private connectionInfoDisconnectHandler: Disposable | undefined; private serverExitCode: number | undefined; + private debuggerConnectInfo: IDebuggerConnectInfo | undefined; constructor( _liveShare: ILiveShareApi, @@ -553,11 +555,41 @@ export class JupyterServerBase implements INotebookServer { CodeSnippits.MatplotLibInit, cancelToken ); + + // If our setting for this is turned on, then import the debugger and enable it to attach + if (this.launchInfo && this.launchInfo.enableDebugging) { + this.debuggerConnectInfo = await this.enableDebugging(); + traceInfo(this.debuggerConnectInfo.hostName); + } } catch (e) { traceWarning(e); } } + private async enableDebugging(): Promise { + // tslint:disable-next-line:no-multiline-string + const enableDebuggerResults = await this.executeSilently(`import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + + const enableAttachString = enableDebuggerResults.length > 0 ? this.extractStreamOutput(enableDebuggerResults[0]).trimQuotes() : ''; + traceInfo(enableAttachString); + + const debugInfoRegEx = /\('(.*?)', ([0-9]*)\)/; + + const debugInfoMatch = debugInfoRegEx.exec(enableAttachString); + if (debugInfoMatch) { + return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; + } + //const urlMatch = nameAndPortRegEx.exec(output); + //if (urlMatch && !urlMatch[4]) { + //return `${urlMatch[1]}://${urlMatch[2]}:${urlMatch[3]}/`; + //} else if (urlMatch && urlMatch.length === 5) { + //return `${urlMatch[1]}://${urlMatch[2]}:${urlMatch[3]}/?token=${urlMatch[4]}`; + //} + + // undefined here? + return { hostName: 'localhost', port: 5678 }; + } + private combineObservables = (...args: Observable[]): Observable => { return new Observable(subscriber => { // When all complete, we have our results diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index 6dc5a2ce706a..a4d41fd08f47 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -49,6 +49,14 @@ export enum InterruptResult { Restarted = 2 } +// Information needed to attach our debugger instance +// IANHU Make this a part of launch info? +export interface IDebuggerConnectInfo +{ + hostName: string; + port: number; +} + // Information used to launch a notebook server export interface INotebookServerLaunchInfo { @@ -58,6 +66,7 @@ export interface INotebookServerLaunchInfo kernelSpec: IJupyterKernelSpec | undefined; workingDir: string | undefined; purpose: string | undefined; // Purpose this server is for + enableDebugging: boolean | undefined; // If we should enable debugging for this server } export interface INotebookCompletion { @@ -167,6 +176,8 @@ export interface IInteractiveWindow extends Disposable { onExecutedCode: Event; show() : Promise; addCode(code: string, file: string, line: number, editor?: TextEditor) : Promise; + // IANHU combine with add code + debugCode(code: string, file: string, line: number, editor?: TextEditor): Promise; // tslint:disable-next-line:no-any startProgress(): void; stopProgress(): void; @@ -235,6 +246,7 @@ export interface ICodeWatcher { runCellAndAllBelow(startLine: number, startCharacter: number): Promise; runFileInteractive(): Promise; addEmptyCellToBottom(): Promise; + debugCurrentCell(): Promise; } export enum CellState { From 33acc09437e6285ccea905198147bac72e5ebd34 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Wed, 26 Jun 2019 14:01:01 -0700 Subject: [PATCH 02/14] bp hit --- .../interactive-window/interactiveWindow.ts | 55 +++++++++++++++++-- .../datascience/jupyter/jupyterServer.ts | 14 +++-- .../jupyter/jupyterServerFactory.ts | 11 ++++ .../jupyter/liveshare/guestJupyterServer.ts | 10 ++++ src/client/datascience/types.ts | 3 + src/test/datascience/execution.unit.test.ts | 9 +++ 6 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index 9b25641dc4ed..2a4434d6a70c 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -8,13 +8,14 @@ import * as fs from 'fs-extra'; import { inject, injectable, multiInject } from 'inversify'; import * as path from 'path'; import * as uuid from 'uuid/v4'; -import { ConfigurationTarget, Event, EventEmitter, Position, Range, Selection, TextEditor, Uri, ViewColumn } from 'vscode'; +import { ConfigurationTarget, DebugConfiguration, Event, EventEmitter, Position, Range, Selection, TextEditor, Uri, ViewColumn } from 'vscode'; import { Disposable } from 'vscode-jsonrpc'; import * as vsls from 'vsls/vscode'; import { IApplicationShell, ICommandManager, + IDebugService, IDocumentManager, ILiveShareApi, IWebPanelProvider, @@ -26,7 +27,7 @@ import { ContextKey } from '../../common/contextKey'; import { traceInfo, traceWarning } from '../../common/logger'; import { IFileSystem } from '../../common/platform/types'; import { IConfigurationService, IDisposableRegistry, ILogger } from '../../common/types'; -import { createDeferred, Deferred } from '../../common/utils/async'; +import { createDeferred, Deferred, sleep } from '../../common/utils/async'; import * as localize from '../../common/utils/localize'; import { IInterpreterService, PythonInterpreter } from '../../interpreter/contracts'; import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; @@ -115,7 +116,8 @@ export class InteractiveWindow extends WebViewHost im @inject(IInteractiveWindowProvider) private interactiveWindowProvider: IInteractiveWindowProvider, @inject(IDataViewerProvider) private dataExplorerProvider: IDataViewerProvider, @inject(IJupyterVariables) private jupyterVariables: IJupyterVariables, - @inject(INotebookImporter) private jupyterImporter: INotebookImporter + @inject(INotebookImporter) private jupyterImporter: INotebookImporter, + @inject(IDebugService) private debugService: IDebugService ) { super( configuration, @@ -781,6 +783,47 @@ export class InteractiveWindow extends WebViewHost im } } + private async debuggerAttach(): Promise { + if (this.jupyterServer) { + // Get our connection info + const debugConnectInfo = await this.jupyterServer.getDebuggerInfo(); + + // tslint:disable-next-line:no-multiline-string + this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); + + // IANHU: Not right. Just giving time to see if we have actually executed + await sleep(4000); + + if (debugConnectInfo) { + // First connect the VSCode UI + const config: DebugConfiguration = { + name: 'IPython', + request: 'attach', + type: 'python', + port: debugConnectInfo.port, + host: debugConnectInfo.hostName + }; + + await this.debugService.startDebugging(undefined, config); + } + + // Then enable tracing + await this.jupyterServer.setDebugTracing(true); + } + } + + private async debuggerDetach(): Promise { + // Disable tracing + if (this.jupyterServer) { + await this.jupyterServer.setDebugTracing(false); + } + + // Stop our debugging UI session + //if (this.debugService.activeDebugSession) { + //this.debugService.activeDebugSession. + //} + } + private async debugCodeInternal(code: string, file: string, line: number, id?: string, _editor?: TextEditor) : Promise { this.logger.logInformation(`Submitting code for ${this.id}`); @@ -830,7 +873,8 @@ export class InteractiveWindow extends WebViewHost im await this.jupyterServer.setInitialDirectory(path.dirname(file)); } - // Check if we have a debugger port set up + // Attach our debugger + await this.debuggerAttach(); // Attempt to evaluate this cell in the jupyter notebook const observable = this.jupyterServer.executeObservable(code, file, line, id, false); @@ -857,6 +901,9 @@ export class InteractiveWindow extends WebViewHost im // Wait for the cell to finish await finishedAddingCode.promise; traceInfo(`Finished execution for ${id}`); + + // Detach our debugger + this.debuggerDetach(); } } catch (err) { status.dispose(); diff --git a/src/client/datascience/jupyter/jupyterServer.ts b/src/client/datascience/jupyter/jupyterServer.ts index e5de3c2f54d6..8ebef340fe72 100644 --- a/src/client/datascience/jupyter/jupyterServer.ts +++ b/src/client/datascience/jupyter/jupyterServer.ts @@ -423,6 +423,14 @@ export class JupyterServerBase implements INotebookServer { throw new Error(localize.DataScience.sessionDisposed()); } + public async setDebugTracing(tracingOn: boolean): Promise { + const silentResults = await this.executeSilently(`from ptvsd import tracing\r\ntracing(${tracingOn ? 'True' : 'False'})`); + } + + public getDebuggerInfo(): Promise { + return Promise.resolve(this.debuggerConnectInfo); + } + private finishUncompletedCells() { const copyPending = [...this.pendingCellSubscriptions]; copyPending.forEach(c => c.cancel()); @@ -579,12 +587,6 @@ export class JupyterServerBase implements INotebookServer { if (debugInfoMatch) { return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; } - //const urlMatch = nameAndPortRegEx.exec(output); - //if (urlMatch && !urlMatch[4]) { - //return `${urlMatch[1]}://${urlMatch[2]}:${urlMatch[3]}/`; - //} else if (urlMatch && urlMatch.length === 5) { - //return `${urlMatch[1]}://${urlMatch[2]}:${urlMatch[3]}/?token=${urlMatch[4]}`; - //} // undefined here? return { hostName: 'localhost', port: 5678 }; diff --git a/src/client/datascience/jupyter/jupyterServerFactory.ts b/src/client/datascience/jupyter/jupyterServerFactory.ts index 642910941ebb..e22910a9bda2 100644 --- a/src/client/datascience/jupyter/jupyterServerFactory.ts +++ b/src/client/datascience/jupyter/jupyterServerFactory.ts @@ -13,6 +13,7 @@ import { ICell, IConnection, IDataScience, + IDebuggerConnectInfo, IJupyterSessionManager, INotebookCompletion, INotebookServer, @@ -157,4 +158,14 @@ export class JupyterServerFactory implements INotebookServer { const server = await this.serverFactory.get(); return server.getCompletion(cellCode, offsetInCode, cancelToken); } + + public async setDebugTracing(tracingOn: boolean): Promise { + const server = await this.serverFactory.get(); + return server.setDebugTracing(tracingOn); + } + + public async getDebuggerInfo(): Promise { + const server = await this.serverFactory.get(); + return server.getDebuggerInfo(); + } } diff --git a/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts b/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts index a1c4350501d7..470fa069a0f4 100644 --- a/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts +++ b/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts @@ -16,6 +16,7 @@ import { ICell, IConnection, IDataScience, + IDebuggerConnectInfo, IJupyterSessionManager, INotebookCompletion, INotebookServer, @@ -94,6 +95,15 @@ export class GuestJupyterServer return deferred.promise; } + public setDebugTracing(_tracingOn: boolean): Promise { + // Ignore on guest side for now + return Promise.resolve(); + } + + public async getDebuggerInfo(): Promise { + return Promise.resolve(undefined); + } + public setInitialDirectory(_directory: string): Promise { // Ignore this command on this side return Promise.resolve(); diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index a4d41fd08f47..559262d7d2b6 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -94,6 +94,9 @@ export interface INotebookServer extends IAsyncDisposable { getConnectionInfo(): IConnection | undefined; getSysInfo() : Promise; setMatplotLibStyle(useDark: boolean) : Promise; + setDebugTracing(tracingOn: boolean): Promise; + // IANHU: Combine with something else + getDebuggerInfo(): Promise; } export interface INotebookServerOptions { diff --git a/src/test/datascience/execution.unit.test.ts b/src/test/datascience/execution.unit.test.ts index 9fa58c18d8ae..5519a739825e 100644 --- a/src/test/datascience/execution.unit.test.ts +++ b/src/test/datascience/execution.unit.test.ts @@ -39,6 +39,7 @@ import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyte import { ICell, IConnection, + IDebuggerConnectInfo, IJupyterKernelSpec, INotebookCompletion, INotebookServer, @@ -117,6 +118,14 @@ class MockJupyterServer implements INotebookServer { return Promise.resolve(undefined); } + public setDebugTracing(_tracingOn: boolean): Promise { + return Promise.resolve(); + } + + public async getDebuggerInfo(): Promise { + return Promise.resolve(undefined); + } + public interruptKernel(_timeout: number) : Promise { throw new Error('Method not implemented'); } From fd074ef3ba58a8b5e559d784397004cd5857bf67 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Wed, 26 Jun 2019 16:15:23 -0700 Subject: [PATCH 03/14] working E2E --- src/client/common/application/commands.ts | 1 + .../editor-integration/codewatcher.ts | 77 +++++------ .../interactive-window/interactiveWindow.ts | 126 +++--------------- .../datascience/jupyter/jupyterServer.ts | 2 +- src/client/datascience/types.ts | 4 +- 5 files changed, 62 insertions(+), 148 deletions(-) diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index e9bcbae42c70..36676f7bdb6c 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -22,6 +22,7 @@ interface ICommandNameWithoutArgumentTypeMapping { [Commands.Set_ShebangInterpreter]: []; [Commands.Run_Linter]: []; [Commands.Enable_Linter]: []; + ['workbench.action.debug.stop']: []; ['workbench.action.reloadWindow']: []; ['editor.action.formatDocument']: []; ['editor.action.rename']: []; diff --git a/src/client/datascience/editor-integration/codewatcher.ts b/src/client/datascience/editor-integration/codewatcher.ts index 03344ae2e331..69c9859c09dd 100644 --- a/src/client/datascience/editor-integration/codewatcher.ts +++ b/src/client/datascience/editor-integration/codewatcher.ts @@ -93,13 +93,14 @@ export class CodeWatcher implements ICodeWatcher { return this.codeLenses; } + // IANHU: Add Telemetry public async debugCurrentCell() { if (!this.documentManager.activeTextEditor || !this.documentManager.activeTextEditor.document) { return Promise.resolve(); } // Run the cell that matches the current cursor position. - return this.debugMatchingCell(this.documentManager.activeTextEditor.selection, false); + return this.runMatchingCell(this.documentManager.activeTextEditor.selection, false, true); } @captureTelemetry(Telemetry.RunAllCells) @@ -260,7 +261,42 @@ export class CodeWatcher implements ICodeWatcher { } // IANHU: Duplicating too much with run commands, perhaps debug is a parameter on run? - private async debugMatchingCell(range: Range, advance?: boolean) { + //private async debugMatchingCell(range: Range, advance?: boolean) { + //const currentRunCellLens = this.getCurrentCellLens(range.start); + //const nextRunCellLens = this.getNextCellLens(range.start); + + //if (currentRunCellLens) { + //// Move the next cell if allowed. + //if (advance) { + //// Either use the next cell that we found, or add a new one into the document + //let nextRange: Range; + //if (!nextRunCellLens) { + //nextRange = this.createNewCell(currentRunCellLens.range); + //} else { + //nextRange = nextRunCellLens.range; + //} + + //if (nextRange) { + //this.advanceToRange(nextRange); + //} + //} + + //// Run the cell after moving the selection + //if (this.document) { + //// Use that to get our code. + //const code = this.document.getText(currentRunCellLens.range); + + //try { + //const activeInteractiveWindow = await this.interactiveWindowProvider.getOrCreateActive(); + //await activeInteractiveWindow.addCode(code, this.getFileName(), range.start.line, this.documentManager.activeTextEditor, true); + //} catch (err) { + //this.handleError(err); + //} + //} + //} + //} + + private async runMatchingCell(range: Range, advance?: boolean, debug?: boolean) { const currentRunCellLens = this.getCurrentCellLens(range.start); const nextRunCellLens = this.getNextCellLens(range.start); @@ -287,42 +323,7 @@ export class CodeWatcher implements ICodeWatcher { try { const activeInteractiveWindow = await this.interactiveWindowProvider.getOrCreateActive(); - await activeInteractiveWindow.debugCode(code, this.getFileName(), range.start.line, this.documentManager.activeTextEditor); - } catch (err) { - this.handleError(err); - } - } - } - } - - private async runMatchingCell(range: Range, advance?: boolean) { - const currentRunCellLens = this.getCurrentCellLens(range.start); - const nextRunCellLens = this.getNextCellLens(range.start); - - if (currentRunCellLens) { - // Move the next cell if allowed. - if (advance) { - // Either use the next cell that we found, or add a new one into the document - let nextRange: Range; - if (!nextRunCellLens) { - nextRange = this.createNewCell(currentRunCellLens.range); - } else { - nextRange = nextRunCellLens.range; - } - - if (nextRange) { - this.advanceToRange(nextRange); - } - } - - // Run the cell after moving the selection - if (this.document) { - // Use that to get our code. - const code = this.document.getText(currentRunCellLens.range); - - try { - const activeInteractiveWindow = await this.interactiveWindowProvider.getOrCreateActive(); - await activeInteractiveWindow.addCode(code, this.getFileName(), range.start.line, this.documentManager.activeTextEditor); + await activeInteractiveWindow.addCode(code, this.getFileName(), range.start.line, this.documentManager.activeTextEditor, debug); } catch (err) { this.handleError(err); } diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index 2a4434d6a70c..cf143e722649 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -27,7 +27,7 @@ import { ContextKey } from '../../common/contextKey'; import { traceInfo, traceWarning } from '../../common/logger'; import { IFileSystem } from '../../common/platform/types'; import { IConfigurationService, IDisposableRegistry, ILogger } from '../../common/types'; -import { createDeferred, Deferred, sleep } from '../../common/utils/async'; +import { createDeferred, Deferred } from '../../common/utils/async'; import * as localize from '../../common/utils/localize'; import { IInterpreterService, PythonInterpreter } from '../../interpreter/contracts'; import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; @@ -180,14 +180,9 @@ export class InteractiveWindow extends WebViewHost im return this.executeEvent.event; } - public addCode(code: string, file: string, line: number, editor?: TextEditor) : Promise { + public addCode(code: string, file: string, line: number, editor?: TextEditor, debug?: boolean) : Promise { // Call the internal method. - return this.submitCode(code, file, line, undefined, editor); - } - - public debugCode(code: string, file: string, line: number, editor?: TextEditor) : Promise { - // Call the internal method. - return this.debugCodeInternal(code, file, line, undefined, editor); + return this.submitCode(code, file, line, undefined, editor, debug); } // tslint:disable-next-line: no-any no-empty cyclomatic-complexity max-func-body-length @@ -700,7 +695,7 @@ export class InteractiveWindow extends WebViewHost im } } - private async submitCode(code: string, file: string, line: number, id?: string, _editor?: TextEditor) : Promise { + private async submitCode(code: string, file: string, line: number, id?: string, _editor?: TextEditor, debug?: boolean) : Promise { this.logger.logInformation(`Submitting code for ${this.id}`); // Start a status item @@ -749,6 +744,11 @@ export class InteractiveWindow extends WebViewHost im await this.jupyterServer.setInitialDirectory(path.dirname(file)); } + if (debug) { + // Attach our debugger + await this.debuggerAttach(); + } + // Attempt to evaluate this cell in the jupyter notebook const observable = this.jupyterServer.executeObservable(code, file, line, id, false); @@ -774,6 +774,11 @@ export class InteractiveWindow extends WebViewHost im // Wait for the cell to finish await finishedAddingCode.promise; traceInfo(`Finished execution for ${id}`); + + // IANHU: Right spot here? + if (debug) { + this.debuggerDetach(); + } } } catch (err) { status.dispose(); @@ -788,12 +793,6 @@ export class InteractiveWindow extends WebViewHost im // Get our connection info const debugConnectInfo = await this.jupyterServer.getDebuggerInfo(); - // tslint:disable-next-line:no-multiline-string - this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); - - // IANHU: Not right. Just giving time to see if we have actually executed - await sleep(4000); - if (debugConnectInfo) { // First connect the VSCode UI const config: DebugConfiguration = { @@ -807,6 +806,9 @@ export class InteractiveWindow extends WebViewHost im await this.debugService.startDebugging(undefined, config); } + // tslint:disable-next-line:no-multiline-string + await this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); + // Then enable tracing await this.jupyterServer.setDebugTracing(true); } @@ -819,98 +821,8 @@ export class InteractiveWindow extends WebViewHost im } // Stop our debugging UI session - //if (this.debugService.activeDebugSession) { - //this.debugService.activeDebugSession. - //} - } - - private async debugCodeInternal(code: string, file: string, line: number, id?: string, _editor?: TextEditor) : Promise { - this.logger.logInformation(`Submitting code for ${this.id}`); - - // Start a status item - const status = this.setStatus(localize.DataScience.executingCode()); - - // Transmit this submission to all other listeners (in a live share session) - if (!id) { - id = uuid(); - this.shareMessage(InteractiveWindowMessages.RemoteAddCode, {code, file, line, id, originator: this.id}); - } - - // Create a deferred object that will wait until the status is disposed - const finishedAddingCode = createDeferred(); - const actualDispose = status.dispose.bind(status); - status.dispose = () => { - finishedAddingCode.resolve(); - actualDispose(); - }; - - try { - - // Make sure we're loaded first. - try { - this.logger.logInformation('Waiting for jupyter server and web panel ...'); - await this.loadPromise; - } catch (exc) { - // We should dispose ourselves if the load fails. Othewise the user - // updates their install and we just fail again because the load promise is the same. - this.dispose(); - - throw exc; - } - - // Then show our webpanel - await this.show(); - - // Add our sys info if necessary - if (file !== Identifiers.EmptyFileName) { - await this.addSysInfo(SysInfoReason.Start); - } - - if (this.jupyterServer) { - // Before we try to execute code make sure that we have an initial directory set - // Normally set via the workspace, but we might not have one here if loading a single loose file - if (file !== Identifiers.EmptyFileName) { - await this.jupyterServer.setInitialDirectory(path.dirname(file)); - } - - // Attach our debugger - await this.debuggerAttach(); - - // Attempt to evaluate this cell in the jupyter notebook - const observable = this.jupyterServer.executeObservable(code, file, line, id, false); - - // Indicate we executed some code - this.executeEvent.fire(code); - - // Sign up for cell changes - observable.subscribe( - (cells: ICell[]) => { - this.onAddCodeEvent(cells, undefined); - }, - (error) => { - status.dispose(); - if (!(error instanceof CancellationError)) { - this.applicationShell.showErrorMessage(error.toString()); - } - }, - () => { - // Indicate executing until this cell is done. - status.dispose(); - }); - - // Wait for the cell to finish - await finishedAddingCode.promise; - traceInfo(`Finished execution for ${id}`); - - // Detach our debugger - this.debuggerDetach(); - } - } catch (err) { - status.dispose(); - - const message = localize.DataScience.executingCodeFailure().format(err); - this.applicationShell.showErrorMessage(message); - } + // IANHU: await not needed here? + await this.commandManager.executeCommand('workbench.action.debug.stop'); } private setStatus = (message: string): Disposable => { diff --git a/src/client/datascience/jupyter/jupyterServer.ts b/src/client/datascience/jupyter/jupyterServer.ts index 8ebef340fe72..354feb9a3db2 100644 --- a/src/client/datascience/jupyter/jupyterServer.ts +++ b/src/client/datascience/jupyter/jupyterServer.ts @@ -424,7 +424,7 @@ export class JupyterServerBase implements INotebookServer { } public async setDebugTracing(tracingOn: boolean): Promise { - const silentResults = await this.executeSilently(`from ptvsd import tracing\r\ntracing(${tracingOn ? 'True' : 'False'})`); + await this.executeSilently(`from ptvsd import tracing\r\ntracing(${tracingOn ? 'True' : 'False'})`); } public getDebuggerInfo(): Promise { diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index 559262d7d2b6..4a2dbe5fcd93 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -178,9 +178,9 @@ export interface IInteractiveWindow extends Disposable { ready: Promise; onExecutedCode: Event; show() : Promise; - addCode(code: string, file: string, line: number, editor?: TextEditor) : Promise; + addCode(code: string, file: string, line: number, editor?: TextEditor, debug?: boolean) : Promise; // IANHU combine with add code - debugCode(code: string, file: string, line: number, editor?: TextEditor): Promise; + //debugCode(code: string, file: string, line: number, editor?: TextEditor): Promise; // tslint:disable-next-line:no-any startProgress(): void; stopProgress(): void; From 986050452b44f7983d42c0a071db4227d55de68d Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Wed, 26 Jun 2019 16:46:48 -0700 Subject: [PATCH 04/14] review cleanup --- src/client/datascience/constants.ts | 3 +- src/client/datascience/datascience.ts | 29 +++++++------- .../editor-integration/codewatcher.ts | 38 +------------------ .../interactive-window/interactiveWindow.ts | 24 ++++++------ .../datascience/jupyter/jupyterServer.ts | 7 ++-- src/client/datascience/types.ts | 11 +----- src/client/telemetry/index.ts | 1 + 7 files changed, 34 insertions(+), 79 deletions(-) diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index ad4b9236a364..2494a0bf3ee5 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -125,7 +125,8 @@ export enum Telemetry { GetPasswordAttempt = 'DATASCIENCE.GET_PASSWORD_ATTEMPT', GetPasswordFailure = 'DATASCIENCE.GET_PASSWORD_FAILURE', GetPasswordSuccess = 'DATASCIENCE.GET_PASSWORD_SUCCESS', - OpenPlotViewer = 'DATASCIENCE.OPEN_PLOT_VIEWER' + OpenPlotViewer = 'DATASCIENCE.OPEN_PLOT_VIEWER', + DebugCurrentCell = 'DATASCIENCE.DEBUG_CURRENT_CELL' } export namespace HelpLinks { diff --git a/src/client/datascience/datascience.ts b/src/client/datascience/datascience.ts index 2f3aaeb7adf6..92bf112df586 100644 --- a/src/client/datascience/datascience.ts +++ b/src/client/datascience/datascience.ts @@ -287,6 +287,20 @@ export class DataScience implements IDataScience { } } + private async debugCurrentCell(): Promise { + this.dataScienceSurveyBanner.showBanner().ignoreErrors(); + + const currentCodeLens = this.getCurrentCodeLens(); + if (currentCodeLens) { + const activeCodeWatcher = this.getCurrentCodeWatcher(); + if (activeCodeWatcher) { + return activeCodeWatcher.debugCurrentCell(); + } + } else { + return Promise.resolve(); + } + } + private validateURI = (testURI: string): string | undefined | null => { try { // tslint:disable-next-line:no-unused-expression @@ -331,21 +345,6 @@ export class DataScience implements IDataScience { return this.dataScienceCodeLensProvider.getCodeWatcher(activeEditor.document); } - private async debugCurrentCell(): Promise { - this.dataScienceSurveyBanner.showBanner().ignoreErrors(); - - const currentCodeLens = this.getCurrentCodeLens(); - if (currentCodeLens) { - const activeCodeWatcher = this.getCurrentCodeWatcher(); - if (activeCodeWatcher) { - //return activeCodeWatcher.runCellAndAllBelow(currentCodeLens.range.start.line, currentCodeLens.range.start.character); - return activeCodeWatcher.debugCurrentCell(); - } - } else { - return Promise.resolve(); - } - } - private registerCommands(): void { let disposable = this.commandManager.registerCommand(Commands.RunAllCells, this.runAllCells, this); this.disposableRegistry.push(disposable); diff --git a/src/client/datascience/editor-integration/codewatcher.ts b/src/client/datascience/editor-integration/codewatcher.ts index 69c9859c09dd..47bf2e65019e 100644 --- a/src/client/datascience/editor-integration/codewatcher.ts +++ b/src/client/datascience/editor-integration/codewatcher.ts @@ -93,7 +93,7 @@ export class CodeWatcher implements ICodeWatcher { return this.codeLenses; } - // IANHU: Add Telemetry + @captureTelemetry(Telemetry.DebugCurrentCell) public async debugCurrentCell() { if (!this.documentManager.activeTextEditor || !this.documentManager.activeTextEditor.document) { return Promise.resolve(); @@ -260,42 +260,6 @@ export class CodeWatcher implements ICodeWatcher { } } - // IANHU: Duplicating too much with run commands, perhaps debug is a parameter on run? - //private async debugMatchingCell(range: Range, advance?: boolean) { - //const currentRunCellLens = this.getCurrentCellLens(range.start); - //const nextRunCellLens = this.getNextCellLens(range.start); - - //if (currentRunCellLens) { - //// Move the next cell if allowed. - //if (advance) { - //// Either use the next cell that we found, or add a new one into the document - //let nextRange: Range; - //if (!nextRunCellLens) { - //nextRange = this.createNewCell(currentRunCellLens.range); - //} else { - //nextRange = nextRunCellLens.range; - //} - - //if (nextRange) { - //this.advanceToRange(nextRange); - //} - //} - - //// Run the cell after moving the selection - //if (this.document) { - //// Use that to get our code. - //const code = this.document.getText(currentRunCellLens.range); - - //try { - //const activeInteractiveWindow = await this.interactiveWindowProvider.getOrCreateActive(); - //await activeInteractiveWindow.addCode(code, this.getFileName(), range.start.line, this.documentManager.activeTextEditor, true); - //} catch (err) { - //this.handleError(err); - //} - //} - //} - //} - private async runMatchingCell(range: Range, advance?: boolean, debug?: boolean) { const currentRunCellLens = this.getCurrentCellLens(range.start); const nextRunCellLens = this.getNextCellLens(range.start); diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index cf143e722649..328758686a89 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -774,17 +774,16 @@ export class InteractiveWindow extends WebViewHost im // Wait for the cell to finish await finishedAddingCode.promise; traceInfo(`Finished execution for ${id}`); - - // IANHU: Right spot here? - if (debug) { - this.debuggerDetach(); - } } } catch (err) { status.dispose(); const message = localize.DataScience.executingCodeFailure().format(err); this.applicationShell.showErrorMessage(message); + } finally { + if (debug) { + this.debuggerDetach(); + } } } @@ -804,13 +803,13 @@ export class InteractiveWindow extends WebViewHost im }; await this.debugService.startDebugging(undefined, config); - } - // tslint:disable-next-line:no-multiline-string - await this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); + // tslint:disable-next-line:no-multiline-string + await this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); - // Then enable tracing - await this.jupyterServer.setDebugTracing(true); + // Then enable tracing + await this.jupyterServer.setDebugTracing(true); + } } } @@ -820,9 +819,8 @@ export class InteractiveWindow extends WebViewHost im await this.jupyterServer.setDebugTracing(false); } - // Stop our debugging UI session - // IANHU: await not needed here? - await this.commandManager.executeCommand('workbench.action.debug.stop'); + // Stop our debugging UI session, no await as we just want it stopped + this.commandManager.executeCommand('workbench.action.debug.stop'); } private setStatus = (message: string): Disposable => { diff --git a/src/client/datascience/jupyter/jupyterServer.ts b/src/client/datascience/jupyter/jupyterServer.ts index 354feb9a3db2..05c7fa6a761a 100644 --- a/src/client/datascience/jupyter/jupyterServer.ts +++ b/src/client/datascience/jupyter/jupyterServer.ts @@ -567,16 +567,16 @@ export class JupyterServerBase implements INotebookServer { // If our setting for this is turned on, then import the debugger and enable it to attach if (this.launchInfo && this.launchInfo.enableDebugging) { this.debuggerConnectInfo = await this.enableDebugging(); - traceInfo(this.debuggerConnectInfo.hostName); } } catch (e) { traceWarning(e); } } - private async enableDebugging(): Promise { + private async enableDebugging(): Promise { // tslint:disable-next-line:no-multiline-string const enableDebuggerResults = await this.executeSilently(`import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + //const enableDebuggerResults = await this.executeSilently(`import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); const enableAttachString = enableDebuggerResults.length > 0 ? this.extractStreamOutput(enableDebuggerResults[0]).trimQuotes() : ''; traceInfo(enableAttachString); @@ -588,8 +588,7 @@ export class JupyterServerBase implements INotebookServer { return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; } - // undefined here? - return { hostName: 'localhost', port: 5678 }; + return undefined; } private combineObservables = (...args: Observable[]): Observable => { diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index 4a2dbe5fcd93..63f92be77218 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -50,16 +50,13 @@ export enum InterruptResult { } // Information needed to attach our debugger instance -// IANHU Make this a part of launch info? -export interface IDebuggerConnectInfo -{ +export interface IDebuggerConnectInfo { hostName: string; port: number; } // Information used to launch a notebook server -export interface INotebookServerLaunchInfo -{ +export interface INotebookServerLaunchInfo { connectionInfo: IConnection; currentInterpreter: PythonInterpreter | undefined; uri: string | undefined; // Different from the connectionInfo as this is the setting used, not the result @@ -95,7 +92,6 @@ export interface INotebookServer extends IAsyncDisposable { getSysInfo() : Promise; setMatplotLibStyle(useDark: boolean) : Promise; setDebugTracing(tracingOn: boolean): Promise; - // IANHU: Combine with something else getDebuggerInfo(): Promise; } @@ -179,9 +175,6 @@ export interface IInteractiveWindow extends Disposable { onExecutedCode: Event; show() : Promise; addCode(code: string, file: string, line: number, editor?: TextEditor, debug?: boolean) : Promise; - // IANHU combine with add code - //debugCode(code: string, file: string, line: number, editor?: TextEditor): Promise; - // tslint:disable-next-line:no-any startProgress(): void; stopProgress(): void; undoCells(): void; diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 9dc9eea80386..182d3e049e29 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -338,6 +338,7 @@ export interface IEventNamePropertyMapping { [Telemetry.ConnectRemoteSelfCertFailedJupyter]: never | undefined; [Telemetry.CopySourceCode]: never | undefined; [Telemetry.DataScienceSettings]: JSONObject; + [Telemetry.DebugCurrentCell]: never | undefined; [Telemetry.DeleteAllCells]: never | undefined; [Telemetry.DeleteCell]: never | undefined; [Telemetry.DisableInteractiveShiftEnter]: never | undefined; From 8d7ada0bc9e5290092d7db6e5b52180fc23d1331 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 09:50:54 -0700 Subject: [PATCH 05/14] debugCode top level function --- src/client/datascience/editor-integration/codewatcher.ts | 6 +++++- .../datascience/interactive-window/interactiveWindow.ts | 9 +++++++-- src/client/datascience/types.ts | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/client/datascience/editor-integration/codewatcher.ts b/src/client/datascience/editor-integration/codewatcher.ts index 47bf2e65019e..82399199cedd 100644 --- a/src/client/datascience/editor-integration/codewatcher.ts +++ b/src/client/datascience/editor-integration/codewatcher.ts @@ -287,7 +287,11 @@ export class CodeWatcher implements ICodeWatcher { try { const activeInteractiveWindow = await this.interactiveWindowProvider.getOrCreateActive(); - await activeInteractiveWindow.addCode(code, this.getFileName(), range.start.line, this.documentManager.activeTextEditor, debug); + if (debug) { + await activeInteractiveWindow.debugCode(code, this.getFileName(), range.start.line, this.documentManager.activeTextEditor); + } else { + await activeInteractiveWindow.addCode(code, this.getFileName(), range.start.line, this.documentManager.activeTextEditor); + } } catch (err) { this.handleError(err); } diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index 328758686a89..128ae86f595d 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -180,9 +180,14 @@ export class InteractiveWindow extends WebViewHost im return this.executeEvent.event; } - public addCode(code: string, file: string, line: number, editor?: TextEditor, debug?: boolean) : Promise { + public addCode(code: string, file: string, line: number, editor?: TextEditor) : Promise { // Call the internal method. - return this.submitCode(code, file, line, undefined, editor, debug); + return this.submitCode(code, file, line, undefined, editor, false); + } + + public debugCode(code: string, file: string, line: number, editor?: TextEditor) : Promise { + // Call the internal method. + return this.submitCode(code, file, line, undefined, editor, true); } // tslint:disable-next-line: no-any no-empty cyclomatic-complexity max-func-body-length diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index 63f92be77218..3fc40e65a773 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -175,6 +175,7 @@ export interface IInteractiveWindow extends Disposable { onExecutedCode: Event; show() : Promise; addCode(code: string, file: string, line: number, editor?: TextEditor, debug?: boolean) : Promise; + debugCode(code: string, file: string, line: number, editor?: TextEditor, debug?: boolean) : Promise; startProgress(): void; stopProgress(): void; undoCells(): void; From e9c92e6afae62192bc1240f54ed9e3ab80920a10 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 10:11:50 -0700 Subject: [PATCH 06/14] put enable debugging on server options --- .../interactive-window/interactiveWindowProvider.ts | 2 ++ src/client/datascience/jupyter/jupyterExecution.ts | 3 +-- src/client/datascience/jupyter/liveshare/serverCache.ts | 4 +++- src/client/datascience/types.ts | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/client/datascience/interactive-window/interactiveWindowProvider.ts b/src/client/datascience/interactive-window/interactiveWindowProvider.ts index c8a63b023524..92933debf8ae 100644 --- a/src/client/datascience/interactive-window/interactiveWindowProvider.ts +++ b/src/client/datascience/interactive-window/interactiveWindowProvider.ts @@ -82,6 +82,7 @@ export class InteractiveWindowProvider implements IInteractiveWindowProvider, IA const settings = this.configService.getSettings(); let serverURI: string | undefined = settings.datascience.jupyterServerURI; const useDefaultConfig: boolean | undefined = settings.datascience.useDefaultConfigForJupyter; + const enableDebugging: boolean | undefined = settings.datascience.enableDebugging; // For the local case pass in our URI as undefined, that way connect doesn't have to check the setting if (serverURI === Settings.JupyterServerLocalLaunch) { @@ -89,6 +90,7 @@ export class InteractiveWindowProvider implements IInteractiveWindowProvider, IA } return { + enableDebugging: enableDebugging, uri: serverURI, useDefaultConfig, purpose: Identifiers.HistoryPurpose diff --git a/src/client/datascience/jupyter/jupyterExecution.ts b/src/client/datascience/jupyter/jupyterExecution.ts index d4d65ab4aea1..0bc172c551dd 100644 --- a/src/client/datascience/jupyter/jupyterExecution.ts +++ b/src/client/datascience/jupyter/jupyterExecution.ts @@ -139,7 +139,6 @@ export class JupyterExecutionBase implements IJupyterExecution { // Try to connect to our jupyter process. Check our setting for the number of tries let tryCount = 0; const maxTries = this.configuration.getSettings().datascience.jupyterLaunchRetries; - const enableDebugging = this.configuration.getSettings().datascience.enableDebugging ? this.configuration.getSettings().datascience.enableDebugging : false; while (tryCount < maxTries) { try { // Start or connect to the process @@ -156,7 +155,7 @@ export class JupyterExecutionBase implements IJupyterExecution { workingDir: options ? options.workingDir : undefined, uri: options ? options.uri : undefined, purpose: options ? options.purpose : uuid(), - enableDebugging: enableDebugging + enableDebugging: options ? options.enableDebugging : false }; traceInfo(`Connecting to process for ${options ? options.purpose : 'unknown type of'} server`); diff --git a/src/client/datascience/jupyter/liveshare/serverCache.ts b/src/client/datascience/jupyter/liveshare/serverCache.ts index 3640483d7d33..a3b05f880f0f 100644 --- a/src/client/datascience/jupyter/liveshare/serverCache.ts +++ b/src/client/datascience/jupyter/liveshare/serverCache.ts @@ -61,6 +61,7 @@ export class ServerCache implements IAsyncDisposable { public async generateDefaultOptions(options?: INotebookServerOptions): Promise { return { + enableDebugging: options ? options.enableDebugging : false, uri: options ? options.uri : undefined, useDefaultConfig: options ? options.useDefaultConfig : true, // Default for this is true. usingDarkTheme: options ? options.usingDarkTheme : undefined, @@ -76,9 +77,10 @@ export class ServerCache implements IAsyncDisposable { // combine all the values together to make a unique key const uri = options.uri ? options.uri : ''; const useFlag = options.useDefaultConfig ? 'true' : 'false'; + const debug = options.enableDebugging ? 'true' : 'false'; // tslint:disable-next-line:no-suspicious-comment // TODO: Should there be some separator in the key? - return `${options.purpose}${uri}${useFlag}${options.workingDir}`; + return `${options.purpose}${uri}${useFlag}${options.workingDir}${debug}`; } } diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index 3fc40e65a773..4c0bf16e21b8 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -96,6 +96,7 @@ export interface INotebookServer extends IAsyncDisposable { } export interface INotebookServerOptions { + enableDebugging?: boolean; uri?: string; usingDarkTheme?: boolean; useDefaultConfig?: boolean; From 5dbdb9685247e9f3b6c443b3a50eb44232137029 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 14:04:12 -0700 Subject: [PATCH 07/14] refactor working --- .../interactive-window/interactiveWindow.ts | 83 +++++++----- .../datascience/jupyter/jupyterDebugger.ts | 125 ++++++++++++++++++ .../datascience/jupyter/jupyterServer.ts | 38 +++--- .../jupyter/jupyterServerFactory.ts | 18 +-- src/client/datascience/serviceRegistry.ts | 3 + src/client/datascience/types.ts | 11 +- 6 files changed, 212 insertions(+), 66 deletions(-) create mode 100644 src/client/datascience/jupyter/jupyterDebugger.ts diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index 128ae86f595d..d6ca71fa9a15 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -48,6 +48,7 @@ import { IInteractiveWindowInfo, IInteractiveWindowListener, IInteractiveWindowProvider, + IJupyterDebugger, IJupyterExecution, IJupyterVariable, IJupyterVariables, @@ -117,7 +118,8 @@ export class InteractiveWindow extends WebViewHost im @inject(IDataViewerProvider) private dataExplorerProvider: IDataViewerProvider, @inject(IJupyterVariables) private jupyterVariables: IJupyterVariables, @inject(INotebookImporter) private jupyterImporter: INotebookImporter, - @inject(IDebugService) private debugService: IDebugService + @inject(IDebugService) private debugService: IDebugService, + @inject(IJupyterDebugger) private jupyterDebugger: IJupyterDebugger ) { super( configuration, @@ -751,7 +753,8 @@ export class InteractiveWindow extends WebViewHost im if (debug) { // Attach our debugger - await this.debuggerAttach(); + //await this.debuggerAttach(); + await this.jupyterDebugger.startDebugging(this.jupyterServer); } // Attempt to evaluate this cell in the jupyter notebook @@ -787,46 +790,49 @@ export class InteractiveWindow extends WebViewHost im this.applicationShell.showErrorMessage(message); } finally { if (debug) { - this.debuggerDetach(); + //this.debuggerDetach(); + if (this.jupyterServer) { + await this.jupyterDebugger.stopDebugging(this.jupyterServer); + } } } } - private async debuggerAttach(): Promise { - if (this.jupyterServer) { - // Get our connection info - const debugConnectInfo = await this.jupyterServer.getDebuggerInfo(); - - if (debugConnectInfo) { - // First connect the VSCode UI - const config: DebugConfiguration = { - name: 'IPython', - request: 'attach', - type: 'python', - port: debugConnectInfo.port, - host: debugConnectInfo.hostName - }; - - await this.debugService.startDebugging(undefined, config); - - // tslint:disable-next-line:no-multiline-string - await this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); - - // Then enable tracing - await this.jupyterServer.setDebugTracing(true); - } - } - } + //private async debuggerAttach(): Promise { + //if (this.jupyterServer) { + //// Get our connection info + //const debugConnectInfo = await this.jupyterServer.getDebuggerInfo(); - private async debuggerDetach(): Promise { - // Disable tracing - if (this.jupyterServer) { - await this.jupyterServer.setDebugTracing(false); - } + //if (debugConnectInfo) { + //// First connect the VSCode UI + //const config: DebugConfiguration = { + //name: 'IPython', + //request: 'attach', + //type: 'python', + //port: debugConnectInfo.port, + //host: debugConnectInfo.hostName + //}; - // Stop our debugging UI session, no await as we just want it stopped - this.commandManager.executeCommand('workbench.action.debug.stop'); - } + //await this.debugService.startDebugging(undefined, config); + + //// tslint:disable-next-line:no-multiline-string + //await this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); + + //// Then enable tracing + //await this.jupyterServer.setDebugTracing(true); + //} + //} + //} + + //private async debuggerDetach(): Promise { + //// Disable tracing + //if (this.jupyterServer) { + //await this.jupyterServer.setDebugTracing(false); + //} + + //// Stop our debugging UI session, no await as we just want it stopped + //this.commandManager.executeCommand('workbench.action.debug.stop'); + //} private setStatus = (message: string): Disposable => { const result = this.statusProvider.set(message); @@ -1053,6 +1059,11 @@ export class InteractiveWindow extends WebViewHost im // Now try to create a notebook server this.jupyterServer = await this.jupyterExecution.connectToNotebookServer(options); + // Enable debugging support if set + if (options.enableDebugging && this.jupyterServer) { + await this.jupyterDebugger.enableAttach(this.jupyterServer); + } + // Before we run any cells, update the dark setting if (this.jupyterServer) { await this.jupyterServer.setMatplotLibStyle(knownDark); diff --git a/src/client/datascience/jupyter/jupyterDebugger.ts b/src/client/datascience/jupyter/jupyterDebugger.ts new file mode 100644 index 000000000000..cbfe809ceb8a --- /dev/null +++ b/src/client/datascience/jupyter/jupyterDebugger.ts @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +import { nbformat } from '@jupyterlab/coreutils'; +import { inject, injectable } from 'inversify'; +import * as uuid from 'uuid/v4'; +import { DebugConfiguration } from 'vscode'; + +import { ICommandManager, IDebugService } from '../../common/application/types'; +import { traceInfo } from '../../common/logger'; +import { Identifiers } from '../constants'; +import { CellState, ICell, IDebuggerConnectInfo, IJupyterDebugger, INotebookServer } from '../types'; + +@injectable() +export class JupyterDebugger implements IJupyterDebugger { + private connectInfo: IDebuggerConnectInfo | undefined; + + constructor( + @inject(ICommandManager) private commandManager: ICommandManager, + @inject(IDebugService) private debugService: IDebugService + ) {} + + public async enableAttach(server: INotebookServer): Promise { + //await this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); + //// tslint:disable-next-line:no-multiline-string + //const enableDebuggerResults = await this.executeSilently(`import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + ////const enableDebuggerResults = await this.executeSilently(`import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + + //const enableAttachString = enableDebuggerResults.length > 0 ? this.extractStreamOutput(enableDebuggerResults[0]).trimQuotes() : ''; + //traceInfo(enableAttachString); + + //const debugInfoRegEx = /\('(.*?)', ([0-9]*)\)/; + + //const debugInfoMatch = debugInfoRegEx.exec(enableAttachString); + //if (debugInfoMatch) { + //return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; + //} + + //return undefined; + traceInfo('enable debugger attach'); + + // IANHU this current import is only for local testing with specific bits. The main ptvsd code is below + // tslint:disable-next-line:no-multiline-string + const enableDebuggerResults = await this.executeSilently(server, `import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + //const enableDebuggerResults = await this.executeSilently(`import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + this.connectInfo = this.parseConnectInfo(enableDebuggerResults); + // IANHU: We need to clear this out when disconnected from the server + } + + public async startDebugging(server: INotebookServer): Promise { + traceInfo('start debugging'); + // IANHU: no connect info = throw exception? Log it? + if (this.connectInfo) { + // First connect the VSCode UI + const config: DebugConfiguration = { + name: 'IPython', + request: 'attach', + type: 'python', + port: this.connectInfo.port, + host: this.connectInfo.hostName + }; + + await this.debugService.startDebugging(undefined, config); + + // Wait for attach before we turn on tracing and allow the code to run, if the IDE is already attached this is just a no-op + // tslint:disable-next-line:no-multiline-string + await this.executeSilently(server, `import ptvsd\r\nptvsd.wait_for_attach()`); + + // Then enable tracing + // tslint:disable-next-line:no-multiline-string + await this.executeSilently(server, `from ptvsd import tracing\r\ntracing(True)`); + } + } + + public async stopDebugging(server: INotebookServer): Promise { + traceInfo('stop debugging'); + // Disable tracing + // tslint:disable-next-line:no-multiline-string + await this.executeSilently(server, `from ptvsd import tracing\r\ntracing(False)`); + + // Stop our debugging UI session, no await as we just want it stopped + this.commandManager.executeCommand('workbench.action.debug.stop'); + } + + private executeSilently(server: INotebookServer, code: string): Promise { + // IANHU add to notebook server interface? + return server.execute(code, Identifiers.EmptyFileName, 0, uuid(), undefined, true); + } + + private parseConnectInfo(cells: ICell[]): IDebuggerConnectInfo | undefined { + const enableAttachString = cells.length > 0 ? this.extractStreamOutput(cells[0]).trimQuotes() : ''; + + const debugInfoRegEx = /\('(.*?)', ([0-9]*)\)/; + + const debugInfoMatch = debugInfoRegEx.exec(enableAttachString); + if (debugInfoMatch) { + return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; + } + + return undefined; + } + + // IANHU Copied from INotebookServer, combine? + private extractStreamOutput(cell: ICell): string { + let result = ''; + if (cell.state === CellState.error || cell.state === CellState.finished) { + const outputs = cell.data.outputs as nbformat.IOutput[]; + if (outputs) { + outputs.forEach(o => { + if (o.output_type === 'stream') { + const stream = o as nbformat.IStream; + result = result.concat(stream.text.toString()); + } else { + const data = o.data; + if (data && data.hasOwnProperty('text/plain')) { + // tslint:disable-next-line:no-any + result = result.concat((data as any)['text/plain']); + } + } + }); + } + } + return result; + } +} diff --git a/src/client/datascience/jupyter/jupyterServer.ts b/src/client/datascience/jupyter/jupyterServer.ts index 05c7fa6a761a..fb3230250bb1 100644 --- a/src/client/datascience/jupyter/jupyterServer.ts +++ b/src/client/datascience/jupyter/jupyterServer.ts @@ -423,9 +423,9 @@ export class JupyterServerBase implements INotebookServer { throw new Error(localize.DataScience.sessionDisposed()); } - public async setDebugTracing(tracingOn: boolean): Promise { - await this.executeSilently(`from ptvsd import tracing\r\ntracing(${tracingOn ? 'True' : 'False'})`); - } + //public async setDebugTracing(tracingOn: boolean): Promise { + //await this.executeSilently(`from ptvsd import tracing\r\ntracing(${tracingOn ? 'True' : 'False'})`); + //} public getDebuggerInfo(): Promise { return Promise.resolve(this.debuggerConnectInfo); @@ -565,31 +565,31 @@ export class JupyterServerBase implements INotebookServer { ); // If our setting for this is turned on, then import the debugger and enable it to attach - if (this.launchInfo && this.launchInfo.enableDebugging) { - this.debuggerConnectInfo = await this.enableDebugging(); - } + //if (this.launchInfo && this.launchInfo.enableDebugging) { + //this.debuggerConnectInfo = await this.enableDebugging(); + //} } catch (e) { traceWarning(e); } } - private async enableDebugging(): Promise { - // tslint:disable-next-line:no-multiline-string - const enableDebuggerResults = await this.executeSilently(`import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); - //const enableDebuggerResults = await this.executeSilently(`import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + //private async enableDebugging(): Promise { + //// tslint:disable-next-line:no-multiline-string + //const enableDebuggerResults = await this.executeSilently(`import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + ////const enableDebuggerResults = await this.executeSilently(`import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); - const enableAttachString = enableDebuggerResults.length > 0 ? this.extractStreamOutput(enableDebuggerResults[0]).trimQuotes() : ''; - traceInfo(enableAttachString); + //const enableAttachString = enableDebuggerResults.length > 0 ? this.extractStreamOutput(enableDebuggerResults[0]).trimQuotes() : ''; + //traceInfo(enableAttachString); - const debugInfoRegEx = /\('(.*?)', ([0-9]*)\)/; + //const debugInfoRegEx = /\('(.*?)', ([0-9]*)\)/; - const debugInfoMatch = debugInfoRegEx.exec(enableAttachString); - if (debugInfoMatch) { - return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; - } + //const debugInfoMatch = debugInfoRegEx.exec(enableAttachString); + //if (debugInfoMatch) { + //return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; + //} - return undefined; - } + //return undefined; + //} private combineObservables = (...args: Observable[]): Observable => { return new Observable(subscriber => { diff --git a/src/client/datascience/jupyter/jupyterServerFactory.ts b/src/client/datascience/jupyter/jupyterServerFactory.ts index e22910a9bda2..8a10819129dd 100644 --- a/src/client/datascience/jupyter/jupyterServerFactory.ts +++ b/src/client/datascience/jupyter/jupyterServerFactory.ts @@ -159,13 +159,13 @@ export class JupyterServerFactory implements INotebookServer { return server.getCompletion(cellCode, offsetInCode, cancelToken); } - public async setDebugTracing(tracingOn: boolean): Promise { - const server = await this.serverFactory.get(); - return server.setDebugTracing(tracingOn); - } - - public async getDebuggerInfo(): Promise { - const server = await this.serverFactory.get(); - return server.getDebuggerInfo(); - } + //public async setDebugTracing(tracingOn: boolean): Promise { + //const server = await this.serverFactory.get(); + //return server.setDebugTracing(tracingOn); + //} + + //public async getDebuggerInfo(): Promise { + //const server = await this.serverFactory.get(); + //return server.getDebuggerInfo(); + //} } diff --git a/src/client/datascience/serviceRegistry.ts b/src/client/datascience/serviceRegistry.ts index 1aea95f9062d..a7ff8f22780f 100644 --- a/src/client/datascience/serviceRegistry.ts +++ b/src/client/datascience/serviceRegistry.ts @@ -18,6 +18,7 @@ import { InteractiveWindowProvider } from './interactive-window/interactiveWindo import { LinkProvider } from './interactive-window/linkProvider'; import { ShowPlotListener } from './interactive-window/showPlotListener'; import { JupyterCommandFactory } from './jupyter/jupyterCommand'; +import { JupyterDebugger } from './jupyter/jupyterDebugger'; import { JupyterExecutionFactory } from './jupyter/jupyterExecutionFactory'; import { JupyterExporter } from './jupyter/jupyterExporter'; import { JupyterImporter } from './jupyter/jupyterImporter'; @@ -41,6 +42,7 @@ import { IInteractiveWindowListener, IInteractiveWindowProvider, IJupyterCommandFactory, + IJupyterDebugger, IJupyterExecution, IJupyterPasswordConnect, IJupyterSessionManager, @@ -81,4 +83,5 @@ export function registerTypes(serviceManager: IServiceManager) { serviceManager.add(IInteractiveWindowListener, ShowPlotListener); serviceManager.addSingleton(IPlotViewerProvider, PlotViewerProvider); serviceManager.add(IPlotViewer, PlotViewer); + serviceManager.addSingleton(IJupyterDebugger, JupyterDebugger); } diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index 4c0bf16e21b8..4bd18b4e6a22 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -91,8 +91,8 @@ export interface INotebookServer extends IAsyncDisposable { getConnectionInfo(): IConnection | undefined; getSysInfo() : Promise; setMatplotLibStyle(useDark: boolean) : Promise; - setDebugTracing(tracingOn: boolean): Promise; - getDebuggerInfo(): Promise; + //setDebugTracing(tracingOn: boolean): Promise; + //getDebuggerInfo(): Promise; } export interface INotebookServerOptions { @@ -119,6 +119,13 @@ export interface IJupyterExecution extends IAsyncDisposable { getServer(options?: INotebookServerOptions) : Promise; } +export const IJupyterDebugger = Symbol('IJupyterDebugger'); +export interface IJupyterDebugger { + enableAttach(server: INotebookServer): Promise; + startDebugging(server: INotebookServer): Promise; + stopDebugging(server: INotebookServer): Promise; +} + export interface IJupyterPasswordConnectInfo { xsrfCookie: string; sessionCookieName: string; From ac759a0a7d491ece248a1d3c49786a246ac37397 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 14:12:41 -0700 Subject: [PATCH 08/14] remove old comments --- .../interactive-window/interactiveWindow.ts | 4 +--- src/client/datascience/jupyter/jupyterServer.ts | 10 ---------- .../datascience/jupyter/jupyterServerFactory.ts | 11 ----------- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index d6ca71fa9a15..3adc08666ab0 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -8,14 +8,13 @@ import * as fs from 'fs-extra'; import { inject, injectable, multiInject } from 'inversify'; import * as path from 'path'; import * as uuid from 'uuid/v4'; -import { ConfigurationTarget, DebugConfiguration, Event, EventEmitter, Position, Range, Selection, TextEditor, Uri, ViewColumn } from 'vscode'; +import { ConfigurationTarget, Event, EventEmitter, Position, Range, Selection, TextEditor, Uri, ViewColumn } from 'vscode'; import { Disposable } from 'vscode-jsonrpc'; import * as vsls from 'vsls/vscode'; import { IApplicationShell, ICommandManager, - IDebugService, IDocumentManager, ILiveShareApi, IWebPanelProvider, @@ -118,7 +117,6 @@ export class InteractiveWindow extends WebViewHost im @inject(IDataViewerProvider) private dataExplorerProvider: IDataViewerProvider, @inject(IJupyterVariables) private jupyterVariables: IJupyterVariables, @inject(INotebookImporter) private jupyterImporter: INotebookImporter, - @inject(IDebugService) private debugService: IDebugService, @inject(IJupyterDebugger) private jupyterDebugger: IJupyterDebugger ) { super( diff --git a/src/client/datascience/jupyter/jupyterServer.ts b/src/client/datascience/jupyter/jupyterServer.ts index fb3230250bb1..9c0059a5e05e 100644 --- a/src/client/datascience/jupyter/jupyterServer.ts +++ b/src/client/datascience/jupyter/jupyterServer.ts @@ -28,7 +28,6 @@ import { ICell, IConnection, IDataScience, - IDebuggerConnectInfo, IJupyterSession, IJupyterSessionManager, INotebookCompletion, @@ -133,7 +132,6 @@ export class JupyterServerBase implements INotebookServer { private connectPromise: Deferred = createDeferred(); private connectionInfoDisconnectHandler: Disposable | undefined; private serverExitCode: number | undefined; - private debuggerConnectInfo: IDebuggerConnectInfo | undefined; constructor( _liveShare: ILiveShareApi, @@ -423,14 +421,6 @@ export class JupyterServerBase implements INotebookServer { throw new Error(localize.DataScience.sessionDisposed()); } - //public async setDebugTracing(tracingOn: boolean): Promise { - //await this.executeSilently(`from ptvsd import tracing\r\ntracing(${tracingOn ? 'True' : 'False'})`); - //} - - public getDebuggerInfo(): Promise { - return Promise.resolve(this.debuggerConnectInfo); - } - private finishUncompletedCells() { const copyPending = [...this.pendingCellSubscriptions]; copyPending.forEach(c => c.cancel()); diff --git a/src/client/datascience/jupyter/jupyterServerFactory.ts b/src/client/datascience/jupyter/jupyterServerFactory.ts index 8a10819129dd..642910941ebb 100644 --- a/src/client/datascience/jupyter/jupyterServerFactory.ts +++ b/src/client/datascience/jupyter/jupyterServerFactory.ts @@ -13,7 +13,6 @@ import { ICell, IConnection, IDataScience, - IDebuggerConnectInfo, IJupyterSessionManager, INotebookCompletion, INotebookServer, @@ -158,14 +157,4 @@ export class JupyterServerFactory implements INotebookServer { const server = await this.serverFactory.get(); return server.getCompletion(cellCode, offsetInCode, cancelToken); } - - //public async setDebugTracing(tracingOn: boolean): Promise { - //const server = await this.serverFactory.get(); - //return server.setDebugTracing(tracingOn); - //} - - //public async getDebuggerInfo(): Promise { - //const server = await this.serverFactory.get(); - //return server.getDebuggerInfo(); - //} } From 1d0c254746d767300997642b5d547e6eed247ce9 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 14:36:18 -0700 Subject: [PATCH 09/14] review changes --- .../datascience/jupyter/jupyterDebugger.ts | 63 +++++++------------ 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/src/client/datascience/jupyter/jupyterDebugger.ts b/src/client/datascience/jupyter/jupyterDebugger.ts index cbfe809ceb8a..cecdad02b979 100644 --- a/src/client/datascience/jupyter/jupyterDebugger.ts +++ b/src/client/datascience/jupyter/jupyterDebugger.ts @@ -21,35 +21,19 @@ export class JupyterDebugger implements IJupyterDebugger { ) {} public async enableAttach(server: INotebookServer): Promise { - //await this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); - //// tslint:disable-next-line:no-multiline-string - //const enableDebuggerResults = await this.executeSilently(`import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); - ////const enableDebuggerResults = await this.executeSilently(`import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); - - //const enableAttachString = enableDebuggerResults.length > 0 ? this.extractStreamOutput(enableDebuggerResults[0]).trimQuotes() : ''; - //traceInfo(enableAttachString); - - //const debugInfoRegEx = /\('(.*?)', ([0-9]*)\)/; - - //const debugInfoMatch = debugInfoRegEx.exec(enableAttachString); - //if (debugInfoMatch) { - //return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; - //} - - //return undefined; traceInfo('enable debugger attach'); // IANHU this current import is only for local testing with specific bits. The main ptvsd code is below // tslint:disable-next-line:no-multiline-string const enableDebuggerResults = await this.executeSilently(server, `import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); //const enableDebuggerResults = await this.executeSilently(`import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + + // Save our connection info to this server this.connectInfo = this.parseConnectInfo(enableDebuggerResults); - // IANHU: We need to clear this out when disconnected from the server } public async startDebugging(server: INotebookServer): Promise { traceInfo('start debugging'); - // IANHU: no connect info = throw exception? Log it? if (this.connectInfo) { // First connect the VSCode UI const config: DebugConfiguration = { @@ -83,43 +67,38 @@ export class JupyterDebugger implements IJupyterDebugger { } private executeSilently(server: INotebookServer, code: string): Promise { - // IANHU add to notebook server interface? return server.execute(code, Identifiers.EmptyFileName, 0, uuid(), undefined, true); } + // Pull our connection info out from the cells returned by enable_attach private parseConnectInfo(cells: ICell[]): IDebuggerConnectInfo | undefined { - const enableAttachString = cells.length > 0 ? this.extractStreamOutput(cells[0]).trimQuotes() : ''; + if (cells.length > 0) { + let enableAttachString = this.extractOutput(cells[0]); + if (enableAttachString) { + enableAttachString = enableAttachString.trimQuotes(); - const debugInfoRegEx = /\('(.*?)', ([0-9]*)\)/; + const debugInfoRegEx = /\('(.*?)', ([0-9]*)\)/; - const debugInfoMatch = debugInfoRegEx.exec(enableAttachString); - if (debugInfoMatch) { - return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; + const debugInfoMatch = debugInfoRegEx.exec(enableAttachString); + if (debugInfoMatch) { + return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; + } + } } - return undefined; } - // IANHU Copied from INotebookServer, combine? - private extractStreamOutput(cell: ICell): string { - let result = ''; + private extractOutput(cell: ICell): string | undefined { if (cell.state === CellState.error || cell.state === CellState.finished) { const outputs = cell.data.outputs as nbformat.IOutput[]; - if (outputs) { - outputs.forEach(o => { - if (o.output_type === 'stream') { - const stream = o as nbformat.IStream; - result = result.concat(stream.text.toString()); - } else { - const data = o.data; - if (data && data.hasOwnProperty('text/plain')) { - // tslint:disable-next-line:no-any - result = result.concat((data as any)['text/plain']); - } - } - }); + if (outputs.length > 0) { + const data = outputs[0].data; + if (data && data.hasOwnProperty('text/plain')) { + // tslint:disable-next-line:no-any + return ((data as any)['text/plain']); + } } } - return result; + return undefined; } } From de4e435d1f7b4feee909f31cf04c603f08a794a1 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 14:40:33 -0700 Subject: [PATCH 10/14] final cleanup --- .../interactive-window/interactiveWindow.ts | 38 ------------------- .../datascience/jupyter/jupyterServer.ts | 23 ----------- .../jupyter/liveshare/guestJupyterServer.ts | 9 ----- src/client/datascience/types.ts | 2 - src/test/datascience/execution.unit.test.ts | 8 ---- 5 files changed, 80 deletions(-) diff --git a/src/client/datascience/interactive-window/interactiveWindow.ts b/src/client/datascience/interactive-window/interactiveWindow.ts index 3adc08666ab0..b1c40c2e4956 100644 --- a/src/client/datascience/interactive-window/interactiveWindow.ts +++ b/src/client/datascience/interactive-window/interactiveWindow.ts @@ -751,7 +751,6 @@ export class InteractiveWindow extends WebViewHost im if (debug) { // Attach our debugger - //await this.debuggerAttach(); await this.jupyterDebugger.startDebugging(this.jupyterServer); } @@ -788,7 +787,6 @@ export class InteractiveWindow extends WebViewHost im this.applicationShell.showErrorMessage(message); } finally { if (debug) { - //this.debuggerDetach(); if (this.jupyterServer) { await this.jupyterDebugger.stopDebugging(this.jupyterServer); } @@ -796,42 +794,6 @@ export class InteractiveWindow extends WebViewHost im } } - //private async debuggerAttach(): Promise { - //if (this.jupyterServer) { - //// Get our connection info - //const debugConnectInfo = await this.jupyterServer.getDebuggerInfo(); - - //if (debugConnectInfo) { - //// First connect the VSCode UI - //const config: DebugConfiguration = { - //name: 'IPython', - //request: 'attach', - //type: 'python', - //port: debugConnectInfo.port, - //host: debugConnectInfo.hostName - //}; - - //await this.debugService.startDebugging(undefined, config); - - //// tslint:disable-next-line:no-multiline-string - //await this.jupyterServer.execute(`import ptvsd\r\nptvsd.wait_for_attach()`, Identifiers.EmptyFileName, 0, uuid(), undefined, true); - - //// Then enable tracing - //await this.jupyterServer.setDebugTracing(true); - //} - //} - //} - - //private async debuggerDetach(): Promise { - //// Disable tracing - //if (this.jupyterServer) { - //await this.jupyterServer.setDebugTracing(false); - //} - - //// Stop our debugging UI session, no await as we just want it stopped - //this.commandManager.executeCommand('workbench.action.debug.stop'); - //} - private setStatus = (message: string): Disposable => { const result = this.statusProvider.set(message); this.potentiallyUnfinishedStatus.push(result); diff --git a/src/client/datascience/jupyter/jupyterServer.ts b/src/client/datascience/jupyter/jupyterServer.ts index 9c0059a5e05e..e5cc6d58a16f 100644 --- a/src/client/datascience/jupyter/jupyterServer.ts +++ b/src/client/datascience/jupyter/jupyterServer.ts @@ -553,34 +553,11 @@ export class JupyterServerBase implements INotebookServer { CodeSnippits.MatplotLibInit, cancelToken ); - - // If our setting for this is turned on, then import the debugger and enable it to attach - //if (this.launchInfo && this.launchInfo.enableDebugging) { - //this.debuggerConnectInfo = await this.enableDebugging(); - //} } catch (e) { traceWarning(e); } } - //private async enableDebugging(): Promise { - //// tslint:disable-next-line:no-multiline-string - //const enableDebuggerResults = await this.executeSilently(`import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); - ////const enableDebuggerResults = await this.executeSilently(`import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); - - //const enableAttachString = enableDebuggerResults.length > 0 ? this.extractStreamOutput(enableDebuggerResults[0]).trimQuotes() : ''; - //traceInfo(enableAttachString); - - //const debugInfoRegEx = /\('(.*?)', ([0-9]*)\)/; - - //const debugInfoMatch = debugInfoRegEx.exec(enableAttachString); - //if (debugInfoMatch) { - //return { hostName: debugInfoMatch[1], port: parseInt(debugInfoMatch[2], 10) }; - //} - - //return undefined; - //} - private combineObservables = (...args: Observable[]): Observable => { return new Observable(subscriber => { // When all complete, we have our results diff --git a/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts b/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts index 470fa069a0f4..cd2345bd4d7c 100644 --- a/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts +++ b/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts @@ -95,15 +95,6 @@ export class GuestJupyterServer return deferred.promise; } - public setDebugTracing(_tracingOn: boolean): Promise { - // Ignore on guest side for now - return Promise.resolve(); - } - - public async getDebuggerInfo(): Promise { - return Promise.resolve(undefined); - } - public setInitialDirectory(_directory: string): Promise { // Ignore this command on this side return Promise.resolve(); diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index 4bd18b4e6a22..de3cedbf6a34 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -91,8 +91,6 @@ export interface INotebookServer extends IAsyncDisposable { getConnectionInfo(): IConnection | undefined; getSysInfo() : Promise; setMatplotLibStyle(useDark: boolean) : Promise; - //setDebugTracing(tracingOn: boolean): Promise; - //getDebuggerInfo(): Promise; } export interface INotebookServerOptions { diff --git a/src/test/datascience/execution.unit.test.ts b/src/test/datascience/execution.unit.test.ts index 5519a739825e..6908219b4963 100644 --- a/src/test/datascience/execution.unit.test.ts +++ b/src/test/datascience/execution.unit.test.ts @@ -118,14 +118,6 @@ class MockJupyterServer implements INotebookServer { return Promise.resolve(undefined); } - public setDebugTracing(_tracingOn: boolean): Promise { - return Promise.resolve(); - } - - public async getDebuggerInfo(): Promise { - return Promise.resolve(undefined); - } - public interruptKernel(_timeout: number) : Promise { throw new Error('Method not implemented'); } From 296266b39fc070231839b928182e74fffd38f2f7 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 14:41:59 -0700 Subject: [PATCH 11/14] add news --- news/1 Enhancements/5900.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/1 Enhancements/5900.md diff --git a/news/1 Enhancements/5900.md b/news/1 Enhancements/5900.md new file mode 100644 index 000000000000..5ac3fe2eec00 --- /dev/null +++ b/news/1 Enhancements/5900.md @@ -0,0 +1 @@ +Hook up ptvsd debugger to jupyter UI \ No newline at end of file From 1e5b759bfce2c4d6fee6b00a2b6106e4eb7e43f4 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 15:13:42 -0700 Subject: [PATCH 12/14] remove hard coded path --- src/client/datascience/jupyter/jupyterDebugger.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/client/datascience/jupyter/jupyterDebugger.ts b/src/client/datascience/jupyter/jupyterDebugger.ts index cecdad02b979..7d50dde82fc5 100644 --- a/src/client/datascience/jupyter/jupyterDebugger.ts +++ b/src/client/datascience/jupyter/jupyterDebugger.ts @@ -23,10 +23,11 @@ export class JupyterDebugger implements IJupyterDebugger { public async enableAttach(server: INotebookServer): Promise { traceInfo('enable debugger attach'); - // IANHU this current import is only for local testing with specific bits. The main ptvsd code is below + // Current version of ptvsd doesn't support returning the value that we need so you need to install the correct version or use my hardcoded line above // tslint:disable-next-line:no-multiline-string - const enableDebuggerResults = await this.executeSilently(server, `import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); - //const enableDebuggerResults = await this.executeSilently(`import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + //const enableDebuggerResults = await this.executeSilently(server, `import sys\r\nsys.path.append('d:/ptvsd-drop/kdrop/src')\r\nimport os\r\nos.environ["PTVSD_LOG_DIR"] = "d:/note_dbg/logs"\r\nimport ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); + // tslint:disable-next-line:no-multiline-string + const enableDebuggerResults = await this.executeSilently(server, `import ptvsd\r\nptvsd.enable_attach(('localhost', 0))`); // Save our connection info to this server this.connectInfo = this.parseConnectInfo(enableDebuggerResults); From b86d32db9e7f10fc46b04440a1e77fa6f86d8396 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 15:25:09 -0700 Subject: [PATCH 13/14] remove unused vars --- src/client/datascience/jupyter/liveshare/guestJupyterServer.ts | 1 - src/test/datascience/execution.unit.test.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts b/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts index cd2345bd4d7c..a1c4350501d7 100644 --- a/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts +++ b/src/client/datascience/jupyter/liveshare/guestJupyterServer.ts @@ -16,7 +16,6 @@ import { ICell, IConnection, IDataScience, - IDebuggerConnectInfo, IJupyterSessionManager, INotebookCompletion, INotebookServer, diff --git a/src/test/datascience/execution.unit.test.ts b/src/test/datascience/execution.unit.test.ts index 6908219b4963..9fa58c18d8ae 100644 --- a/src/test/datascience/execution.unit.test.ts +++ b/src/test/datascience/execution.unit.test.ts @@ -39,7 +39,6 @@ import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyte import { ICell, IConnection, - IDebuggerConnectInfo, IJupyterKernelSpec, INotebookCompletion, INotebookServer, From 6be7ac159ba165f05b393d93f0abdaa3d23137a9 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Thu, 27 Jun 2019 16:14:37 -0700 Subject: [PATCH 14/14] fix functional tests --- src/test/datascience/dataScienceIocContainer.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/datascience/dataScienceIocContainer.ts b/src/test/datascience/dataScienceIocContainer.ts index bb7fe800b175..68b4a1b44d0e 100644 --- a/src/test/datascience/dataScienceIocContainer.ts +++ b/src/test/datascience/dataScienceIocContainer.ts @@ -22,10 +22,12 @@ import { import * as vsls from 'vsls/vscode'; import { ILanguageServer, ILanguageServerAnalysisOptions } from '../../client/activation/types'; +import { DebugService } from '../../client/common/application/debugService'; import { TerminalManager } from '../../client/common/application/terminalManager'; import { IApplicationShell, ICommandManager, + IDebugService, IDocumentManager, ILiveShareApi, ILiveShareTestingApi, @@ -92,6 +94,7 @@ import { InteractiveWindow } from '../../client/datascience/interactive-window/i import { InteractiveWindowCommandListener } from '../../client/datascience/interactive-window/interactiveWindowCommandListener'; import { InteractiveWindowProvider } from '../../client/datascience/interactive-window/interactiveWindowProvider'; import { JupyterCommandFactory } from '../../client/datascience/jupyter/jupyterCommand'; +import { JupyterDebugger } from '../../client/datascience/jupyter/jupyterDebugger'; import { JupyterExecutionFactory } from '../../client/datascience/jupyter/jupyterExecutionFactory'; import { JupyterExporter } from '../../client/datascience/jupyter/jupyterExporter'; import { JupyterImporter } from '../../client/datascience/jupyter/jupyterImporter'; @@ -114,6 +117,7 @@ import { IInteractiveWindowListener, IInteractiveWindowProvider, IJupyterCommandFactory, + IJupyterDebugger, IJupyterExecution, IJupyterPasswordConnect, IJupyterSessionManager, @@ -314,6 +318,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer { this.serviceManager.add(ICodeExecutionHelper, CodeExecutionHelper); this.serviceManager.add(IDataScienceCommandListener, InteractiveWindowCommandListener); this.serviceManager.addSingleton(IJupyterVariables, JupyterVariables); + this.serviceManager.addSingleton(IJupyterDebugger, JupyterDebugger); this.serviceManager.addSingleton(ITerminalHelper, TerminalHelper); this.serviceManager.addSingleton( @@ -331,6 +336,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer { this.serviceManager.addSingleton(ILanguageServer, MockLanguageServer); this.serviceManager.addSingleton(ILanguageServerAnalysisOptions, MockLanguageServerAnalysisOptions); this.serviceManager.add(IInteractiveWindowListener, DotNetIntellisenseProvider); + this.serviceManager.addSingleton(IDebugService, DebugService); // Setup our command list this.commandManager.registerCommand('setContext', (name: string, value: boolean) => {