-
Notifications
You must be signed in to change notification settings - Fork 338
Debugging (run by line) spike #6332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
-remove debugger UI code -remove logs -normalize paths
cast on the kernel debug adapter
Codecov Report
@@ Coverage Diff @@
## main #6332 +/- ##
======================================
- Coverage 69% 69% -1%
======================================
Files 407 410 +3
Lines 27918 28253 +335
Branches 4138 4186 +48
======================================
+ Hits 19487 19598 +111
- Misses 6774 6989 +215
- Partials 1657 1666 +9
|
- start the kernel when starting a debug session (if its not started already) -toggle the breapoint margin automatically - handle the case when we attach and the kernel is stopped
and dump them all when the debug session starts
import { ICell, IDebuggingCellMap, INotebookExecutionLogger } from '../../datascience/types'; | ||
|
||
@injectable() | ||
export class DebuggingCellMap implements IDebuggingCellMap, INotebookExecutionLogger { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a draft here, but this class could use some class comments about what it's responsible for.
src/client/datascience/types.ts
Outdated
@@ -272,6 +273,7 @@ export const INotebookExecutionLogger = Symbol('INotebookExecutionLogger'); | |||
export interface INotebookExecutionLogger extends IDisposable { | |||
preExecute(cell: ICell, silent: boolean): Promise<void>; | |||
postExecute(cell: ICell, silent: boolean, language: string, resource: Uri): Promise<void>; | |||
nativePostExecute(cell: NotebookCell): Promise<void>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this an optional method, this way you don't need to modify all other classes.
nativePostExecute(cell: NotebookCell): Promise<void>; | |
nativePostExecute?(cell: NotebookCell): Promise<void>; |
@@ -63,6 +63,12 @@ export abstract class BaseJupyterSession implements IJupyterSession { | |||
} | |||
return this.onStatusChangedEvent.event; | |||
} | |||
public get onIOPubMessageSignal(): Event<KernelMessage.IIOPubMessage> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to remove the "Signal" from the name here, it's only a Signal down in the Juypter code doesn't really make sense to have that on the name here.
) {} | ||
|
||
public async activate() { | ||
vscode.debug.breakpoints; // start to fetch breakpoints |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the API this looks like a non-async array, why are the results just ignored here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems really weird to do on extension activation? Fetch breakpoints?
.get<IPythonExecutionFactory>(IPythonExecutionFactory) | ||
.createActivatedEnvironment({ resource: uri, interpreter, allowEnvironmentFetchExceptions: true }); | ||
|
||
const output = pythonProcess.execObservable(['-m', 'pip', 'show', executableName], {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work for conda? Meaning will running pip show give you the correct information in a conda environment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use -m ipython --version
or the existing api as mentioned by ian
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a python expert (could consider asking the python folks). But an easier way if we can't use the python -m version check or python api then might be to activate the environment and do like this:
python -c "import ipykernel; print(ipykernel.version)"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try it, although it would be nice to not have to activate it
interpreter: PythonEnvironment, | ||
_token?: CancellationToken | ||
): Promise<boolean> { | ||
return this.installer.getVersion(Product.ipykernel, interpreter).then((version) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would async/await be more readable here? You forgot a 'catch' clause I think. Might be easier with try, await getVersion, parse versoin, catch - return false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup commit
although, I will check if we can accomplish this with the python api.
There's the issue that python -m ipykernel --version
returns the ipython version instead of the ipykernel version. Hopefully the python api deals with that.
} | ||
}), | ||
|
||
vscode.commands.registerCommand('jupyter.debugNotebook', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have commands in a 'commands.ts' file? So that this string is one spot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup commit
await this.debugInfo(); | ||
} | ||
|
||
// after disconnecting, hide the breakpoint margin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate a bit in this comment? I assume detaching is the same as hitting the 'debug' button on the toolbar. Can the user also cause this by just hitting the stop button during debugging?
Seems weird to me. Stopping during debugging should just interrrupt the cell I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
normally nothing needs to happen here. But since vscode has a command that shows and hides the breakpoint margin for notebooks, we need to hide it at some point. And the best time to do it is on the disconnect command.
const cell = this.fileToCell.get(source.path); | ||
if (cell) { | ||
source.name = path.basename(cell.document.uri.path); | ||
if (cell.index >= 0) { | ||
source.name += `, Cell ${cell.index + 1}`; | ||
} | ||
source.path = cell.document.uri.toString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is common with the code above? Meaning can you make a separate function or lambda?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup commit
const cell = this.notebookDocument.getCells().find((c) => c.document.uri.toString() === uri); | ||
if (cell) { | ||
try { | ||
const response = await this.session.customRequest('dumpCell', { code: cell.document.getText() }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this means the kernel has to support this request. I guess it's okay cause ipykernel is probably the only reasonable case for debugging, but maybe we should flag this somehow so we don't forget it when we try getting debugging working elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by flagging? With a comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah with a comment here and maybe an issue? ipykernel supports 'dumpcell' but does xeus?
move the debug context to its own function
@@ -126,6 +126,54 @@ export abstract class BaseInstaller { | |||
.install(product, resource, cancel, reInstallAndUpdate); | |||
} | |||
|
|||
public async getVersion(product: Product, resource?: InterpreterUri): Promise<string | undefined> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did using the pythonAPI function not work here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For modules (which ipykernel should be treated as), I believe the Python API uses
python -c 'import ipykernel; print(ipykernel.__version__)'
which would yield the right result
@@ -173,6 +180,31 @@ export class ActiveEditorContextService implements IExtensionSingleActivationSer | |||
} | |||
this.updateContextOfActiveNotebookKernel(activeEditor); | |||
} | |||
private async updateDebugContext(activeEditor?: INotebookEditor, interpreter?: PythonEnvironment) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is the second parameter case (interpreter) coming from? The only usage of this looks to just be passed an editor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That one used to come from kernel status change, but that didn't work right. I need to create an event for kernel change.
|
||
private getDebuggerByUri(document: vscode.NotebookDocument): Debugger | undefined { | ||
for (const [doc, dbg] of this.notebookToDebugger.entries()) { | ||
if (document.uri.toString() === doc.uri.toString()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per-discussion here: #6358 (comment) I believe we can just compare on documents.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup commit
version: '5.2', | ||
msg_type: 'debug_request', | ||
username: 'vscode', | ||
session: randomBytes(8).toString('hex') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might work for the spike, but for JMP messages session should be unique per session. It's not random per message. You can get the id of an existing session off the ISession on the IJupyterSession.
|
||
this.sendMessage.fire(msg.content); | ||
}; | ||
control.onStdin = (msg) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does anything actually come in on stdin?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, nothing, I'll remove it
package.json
Outdated
"debuggers": [ | ||
{ | ||
"type": "kernel", | ||
"label": "Kernel Debug" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
II think this should be called Jupyter Python
or Python Kernel
or similar, as it's specific to python not just any kernel.
This will matter in full debugging, user needs to select the right debug config (based on the label)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to Python Kernel on the cleanup commit
/** | ||
* Dump content of given cell into a tmp file and return path to file. | ||
*/ | ||
private async dumpCell(uri: string): Promise<void> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Who has ownership of the file here? Looks like ipykernel returns back a sourcePath for the file created in a success message. But I'm not seeing cleanup on the ipykernel side.
const response = await this.session.customRequest('debugInfo'); | ||
|
||
// If there's breakpoints at this point, delete them | ||
if (response.breakpoints.lenght > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks misspelled, "length." Sorry in a PR I can't see the type easily is it an any? Not sure why this would build.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup commit
I created types for these responses to avoid that
command: 'setBreakpoints', | ||
arguments: { | ||
source: { | ||
path: response.breakpoints[0].source |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will they all have the same source here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup commit
Small change, but instead of deleting the breakpoints on the kernel, I instead sent a message to VS Code to register them.
To do
|
if (activeEditor) { | ||
this.notebookProvider | ||
.getOrCreateNotebook({ identity: activeEditor.file, resource: activeEditor.file, getOnly: true }) | ||
.then(async (nb) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just an await here instead of chaining stuff?
const cell = this.notebookDocument.getCells().find((c) => c.document.uri.toString() === uri); | ||
if (cell) { | ||
try { | ||
const response = await this.session.customRequest('dumpCell', { code: cell.document.getText() }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah with a comment here and maybe an issue? ipykernel supports 'dumpcell' but does xeus?
@rchiodo for your second comment, I added this at the top of the file
I put it there because the whole debug adapter need support for those 2 special messages. |
-pr comment on chaining stuff vs await
For #6207
For #6215
For #6376
package-lock.json
has been regenerated by runningnpm install
(if dependencies have changed).