Skip to content

Include error in status and persist exec times #12134

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

Merged
merged 7 commits into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/client/datascience/notebook/executionHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,27 @@ export function updateCellOutput(notebookCellModel: ICell, outputs: nbformat.IOu
model.update(updateCell);
}

/**
* Store execution start and end times in ISO format for portability.
*/
export function updateCellExecutionTimes(
notebookCellModel: ICell,
model: INotebookModel,
startTime?: number,
duration?: number
) {
const startTimeISO = startTime ? new Date(startTime).toISOString() : undefined;
const endTimeISO = duration && startTime ? new Date(startTime + duration).toISOString() : undefined;
updateCellMetadata(
notebookCellModel,
{
end_execution_time: endTimeISO,
start_execution_time: startTimeISO
},
model
);
}

export function updateCellMetadata(
notebookCellModel: ICell,
metadata: Partial<IBaseCellVSCodeMetadata>,
Expand Down
16 changes: 16 additions & 0 deletions src/client/datascience/notebook/executionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ import {
handleUpdateDisplayDataMessage,
hasTransientOutputForAnotherCell,
updateCellExecutionCount,
updateCellExecutionTimes,
updateCellOutput,
updateCellWithErrorStatus
} from './executionHelpers';
import { getCellStatusMessageBasedOnFirstErrorOutput } from './helpers';
import { INotebookExecutionService } from './types';
// tslint:disable-next-line: no-var-requires no-require-imports
const vscodeNotebookEnums = require('vscode') as typeof import('vscode-proposed');
Expand Down Expand Up @@ -185,9 +187,23 @@ export class NotebookExecutionService implements INotebookExecutionService {
? vscodeNotebookEnums.NotebookCellRunState.Idle
: vscodeNotebookEnums.NotebookCellRunState.Success;
cell.metadata.statusMessage = '';

// Update metadata in our model.
const notebookCellModel = findMappedNotebookCellModel(cell, model.cells);
updateCellExecutionTimes(
notebookCellModel,
model,
cell.metadata.runStartTime,
cell.metadata.lastRunDuration
);

// If there are any errors in the cell, then change status to error.
if (cell.outputs.some((output) => output.outputKind === vscodeNotebookEnums.CellOutputKind.Error)) {
cell.metadata.runState = vscodeNotebookEnums.NotebookCellRunState.Error;
cell.metadata.statusMessage = getCellStatusMessageBasedOnFirstErrorOutput(
// tslint:disable-next-line: no-any
notebookCellModel.data.outputs as any
);
}
deferred.resolve();
}
Expand Down
72 changes: 66 additions & 6 deletions src/client/datascience/notebook/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {
// tslint:disable-next-line: no-var-requires no-require-imports
const vscodeNotebookEnums = require('vscode') as typeof import('vscode-proposed');
import * as uuid from 'uuid/v4';
import { NotebookCellRunState } from '../../../../typings/vscode-proposed';
import {
concatMultilineStringInput,
concatMultilineStringOutput,
Expand Down Expand Up @@ -90,24 +91,62 @@ export function cellToVSCNotebookCellData(cell: ICell): NotebookCellData | undef
return;
}

return {
// tslint:disable-next-line: no-any
const outputs = cellOutputsToVSCCellOutputs(cell.data.outputs as any);

// If we have an execution count & no errors, then success state.
// If we have an execution count & errors, then error state.
// Else idle state.
const hasErrors = outputs.some((output) => output.outputKind === vscodeNotebookEnums.CellOutputKind.Error);
const hasExecutionCount = typeof cell.data.execution_count === 'number' && cell.data.execution_count > 0;
let runState: NotebookCellRunState;
let statusMessage: string | undefined;
if (!hasExecutionCount) {
runState = vscodeNotebookEnums.NotebookCellRunState.Idle;
} else if (hasErrors) {
runState = vscodeNotebookEnums.NotebookCellRunState.Error;
// Error details are stripped from the output, get raw output.
// tslint:disable-next-line: no-any
statusMessage = getCellStatusMessageBasedOnFirstErrorOutput(cell.data.outputs as any);
} else {
runState = vscodeNotebookEnums.NotebookCellRunState.Success;
}

const notebookCellData: NotebookCellData = {
cellKind:
cell.data.cell_type === 'code' ? vscodeNotebookEnums.CellKind.Code : vscodeNotebookEnums.CellKind.Markdown,
language: cell.data.cell_type === 'code' ? PYTHON_LANGUAGE : MARKDOWN_LANGUAGE,
metadata: {
editable: true,
executionOrder: typeof cell.data.execution_count === 'number' ? cell.data.execution_count : undefined,
hasExecutionOrder: cell.data.cell_type === 'code',
runState: vscodeNotebookEnums.NotebookCellRunState.Idle,
runState,
runnable: cell.data.cell_type === 'code',
custom: {
cellId: cell.id
}
},
source: concatMultilineStringInput(cell.data.source),
// tslint:disable-next-line: no-any
outputs: cellOutputsToVSCCellOutputs(cell.data.outputs as any)
outputs
};

if (statusMessage) {
notebookCellData.metadata.statusMessage = statusMessage;
}

const startExecutionTime = cell.data.metadata.vscode?.start_execution_time
? new Date(Date.parse(cell.data.metadata.vscode.start_execution_time)).getTime()
: undefined;
const endExecutionTime = cell.data.metadata.vscode?.end_execution_time
? new Date(Date.parse(cell.data.metadata.vscode.end_execution_time)).getTime()
: undefined;

if (startExecutionTime && typeof endExecutionTime === 'number') {
notebookCellData.metadata.runStartTime = startExecutionTime;
notebookCellData.metadata.lastRunDuration = endExecutionTime - startExecutionTime;
}

return notebookCellData;
}

export function cellOutputsToVSCCellOutputs(outputs?: nbformat.IOutput[]): CellOutput[] {
Expand Down Expand Up @@ -205,11 +244,32 @@ function translateStreamOutput(output: nbformat.IStream): CellStreamOutput | Cel
}
};
}

/**
* We will display the error message in the status of the cell.
* We will display the error message in the status of the cell.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repeat comment

* The `ename` & `evalue` is displayed at the top of the output by VS Code.
* As we're displaying the error in the statusbar, we don't want this dup error in output.
* Hence remove this.
*/
export function translateErrorOutput(output: nbformat.IError): CellErrorOutput {
return {
ename: output.ename,
evalue: output.evalue,
ename: '',
evalue: '',
outputKind: vscodeNotebookEnums.CellOutputKind.Error,
traceback: output.traceback
};
}

export function getCellStatusMessageBasedOnFirstErrorOutput(outputs?: nbformat.IOutput[]): string {
if (!Array.isArray(outputs)) {
return '';
}
const errorOutput = (outputs.find((output) => output.output_type === 'error') as unknown) as
| nbformat.IError
| undefined;
if (!errorOutput) {
return '';
}
return `${errorOutput.ename}${errorOutput.evalue ? ': ' : ''}${errorOutput.evalue}`;
}
3 changes: 3 additions & 0 deletions src/test/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,9 @@ export class TestEventHandler<T extends void | any = any> implements IDisposable
public get count(): number {
return this.handledEvents.length;
}
public get all(): T[] {
return this.handledEvents;
}
private readonly handler: IDisposable;
// tslint:disable-next-line: no-any
private readonly handledEvents: any[] = [];
Expand Down
2 changes: 1 addition & 1 deletion src/test/datascience/notebook/contentProvider.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ suite('Data Science - NativeNotebook ContentProvider', () => {
editable: true,
executionOrder: 10,
hasExecutionOrder: true,
runState: (vscodeNotebookEnums as any).NotebookCellRunState.Idle,
runState: (vscodeNotebookEnums as any).NotebookCellRunState.Success,
runnable: true,
custom: {
cellId: 'MyCellId1'
Expand Down
18 changes: 17 additions & 1 deletion src/test/datascience/notebook/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { IDisposable } from '../../../client/common/types';
import { noop, swallowExceptions } from '../../../client/common/utils/misc';
import { NotebookContentProvider } from '../../../client/datascience/notebook/contentProvider';
import { ICell, INotebookEditorProvider, INotebookProvider } from '../../../client/datascience/types';
import { waitForCondition } from '../../common';
import { createEventHandler, waitForCondition } from '../../common';
import { EXTENSION_ROOT_DIR_FOR_TESTS } from '../../constants';
import { closeActiveWindows, initialize } from '../../initialize';
const vscodeNotebookEnums = require('vscode') as typeof import('vscode-proposed');
Expand Down Expand Up @@ -252,3 +252,19 @@ export function assertVSCCellHasErrors(cell: NotebookCell) {
assert.equal(cell.metadata.runState, vscodeNotebookEnums.NotebookCellRunState.Error);
return true;
}
export function assertVSCCellHasErrorOutput(cell: NotebookCell) {
assert.ok(
cell.outputs.filter((output) => output.outputKind === vscodeNotebookEnums.CellOutputKind.Error).length,
'No error output in cell'
);
return true;
}

export async function saveActiveNotebook(disposables: IDisposable[]) {
const api = await initialize();
const editorProvider = api.serviceContainer.get<INotebookEditorProvider>(INotebookEditorProvider);
const savedEvent = createEventHandler(editorProvider.activeEditor!.model!, 'changed', disposables);
await commands.executeCommand('workbench.action.files.saveAll');

await waitForCondition(async () => savedEvent.all.some((e) => e.kind === 'save'), 5_000, 'Not saved');
}
6 changes: 3 additions & 3 deletions src/test/datascience/notebook/helpers.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ suite('Data Science - NativeNotebook helpers', () => {
editable: true,
executionOrder: 10,
hasExecutionOrder: true,
runState: vscodeNotebookEnums.NotebookCellRunState.Idle,
runState: vscodeNotebookEnums.NotebookCellRunState.Success,
runnable: true,
custom: {
cellId: 'MyCellId1'
Expand Down Expand Up @@ -204,8 +204,8 @@ suite('Data Science - NativeNotebook helpers', () => {
[
{
outputKind: vscodeNotebookEnums.CellOutputKind.Error,
ename: 'Error Name',
evalue: 'Error Value',
ename: '',
evalue: '',
traceback: ['stack1', 'stack2', 'stack3']
}
]
Expand Down
Loading