diff --git a/src/client/datascience/jupyter/kernels/cellExecution.ts b/src/client/datascience/jupyter/kernels/cellExecution.ts index ac432a8fa981..926266776f3d 100644 --- a/src/client/datascience/jupyter/kernels/cellExecution.ts +++ b/src/client/datascience/jupyter/kernels/cellExecution.ts @@ -38,8 +38,6 @@ import { import { IKernel } from './types'; // tslint:disable-next-line: no-var-requires no-require-imports const vscodeNotebookEnums = require('vscode') as typeof import('vscode-proposed'); -// tslint:disable-next-line: no-require-imports -import escape = require('lodash/escape'); export class CellExecutionFactory { constructor( @@ -471,11 +469,6 @@ export class CellExecution { // See this for docs on the messages: // https://jupyter-client.readthedocs.io/en/latest/messaging.html#messaging-in-jupyter private async handleExecuteResult(msg: KernelMessage.IExecuteResultMsg, clearState: RefBool) { - // Escape text output - if (msg.content.data && msg.content.data.hasOwnProperty('text/plain')) { - msg.content.data['text/plain'] = escape(msg.content.data['text/plain'] as string); - } - await this.addToCellData( { output_type: 'execute_result', @@ -500,7 +493,7 @@ export class CellExecution { // Mark as stream output so the text is formatted because it likely has ansi codes in it. output_type: 'stream', // tslint:disable-next-line: no-any - text: escape((o.data as any)['text/plain'].toString()), + text: (o.data as any)['text/plain'].toString(), metadata: {}, execution_count: reply.execution_count }, @@ -544,7 +537,7 @@ export class CellExecution { ); edit.replaceCellOutput(this.cellIndex, [...exitingCellOutput]); // This is necessary to get VS code to update (for now) } else { - const originalText = formatStreamText(concatMultilineString(escape(msg.content.text))); + const originalText = formatStreamText(concatMultilineString(msg.content.text)); // Create a new stream entry const output: nbformat.IStream = { output_type: 'stream', @@ -557,11 +550,6 @@ export class CellExecution { } private async handleDisplayData(msg: KernelMessage.IDisplayDataMsg, clearState: RefBool) { - // Escape text output - if (msg.content.data && msg.content.data.hasOwnProperty('text/plain')) { - msg.content.data['text/plain'] = escape(msg.content.data['text/plain'] as string); - } - const output: nbformat.IDisplayData = { output_type: 'display_data', data: msg.content.data, diff --git a/src/test/datascience/notebook/executionService.ds.test.ts b/src/test/datascience/notebook/executionService.ds.test.ts index e19e25719f0e..12ddedd8026d 100644 --- a/src/test/datascience/notebook/executionService.ds.test.ts +++ b/src/test/datascience/notebook/executionService.ds.test.ts @@ -343,4 +343,52 @@ suite('DataScience - VSCode Notebook - (Execution) (slow)', function () { 'Incorrect output' ); }); + test('Verify escaping of output', async () => { + await insertPythonCellAndWait('1'); + await insertPythonCellAndWait(dedent` + a="" + a`); + await insertPythonCellAndWait(dedent` + a="" + print(a)`); + await insertPythonCellAndWait('raise Exception("")'); + const cells = vscodeNotebook.activeNotebookEditor?.document.cells!; + + await executeActiveDocument(); + + // Wait till execution count changes and status is error. + await waitForCondition( + async () => assertHasExecutionCompletedWithErrors(cells[3]), + 15_000, + 'Cell did not get executed' + ); + + for (const cell of cells) { + assert.lengthOf(cell.outputs, 1, 'Incorrect output'); + } + assert.equal( + cells[0].outputs[0].outputKind, + vscodeNotebookEnums.CellOutputKind.Rich, + 'Incorrect output for first cell' + ); + assert.equal( + cells[1].outputs[0].outputKind, + vscodeNotebookEnums.CellOutputKind.Rich, + 'Incorrect output for first cell' + ); + assert.equal( + cells[2].outputs[0].outputKind, + vscodeNotebookEnums.CellOutputKind.Rich, + 'Incorrect output for first cell' + ); + assertHasTextOutputInVSCode(cells[0], '1'); + assertHasTextOutputInVSCode(cells[1], '', 0, false); + assertHasTextOutputInVSCode(cells[2], '', 0, false); + const errorOutput = cells[3].outputs[0] as CellErrorOutput; + assert.equal(errorOutput.outputKind, vscodeNotebookEnums.CellOutputKind.Error, 'Incorrect output'); + assert.equal(errorOutput.ename, 'Exception', 'Incorrect ename'); // As status contains ename, we don't want this displayed again. + assert.equal(errorOutput.evalue, '', 'Incorrect evalue'); // As status contains ename, we don't want this displayed again. + assert.isNotEmpty(errorOutput.traceback, 'Incorrect traceback'); + assert.include(errorOutput.traceback.join(''), ''); + }); }); diff --git a/src/test/datascience/notebook/interrupRestart.ds.test.ts b/src/test/datascience/notebook/interrupRestart.ds.test.ts index 7deae6e8bfd5..cd4a12c0e863 100644 --- a/src/test/datascience/notebook/interrupRestart.ds.test.ts +++ b/src/test/datascience/notebook/interrupRestart.ds.test.ts @@ -81,7 +81,7 @@ suite('DataScience - VSCode Notebook - Restart/Interrupt/Cancel/Errors (slow)', await closeNotebooksAndCleanUpAfterTests(disposables.concat(suiteDisposables)); }); - test('Cancelling token will cancel cell executionxxx', async () => { + test('Cancelling token will cancel cell execution', async () => { await insertPythonCellAndWait('import time\nfor i in range(10000):\n print(i)\n time.sleep(0.1)', 0); const cell = vscEditor.document.cells[0]; const appShell = api.serviceContainer.get(IApplicationShell); @@ -115,7 +115,7 @@ suite('DataScience - VSCode Notebook - Restart/Interrupt/Cancel/Errors (slow)', assertVSCCellHasErrors(cell); } }); - test('Restarting kernel will cancel cell execution & we can re-run a cellxxx', async () => { + test('Restarting kernel will cancel cell execution & we can re-run a cell', async () => { await insertPythonCellAndWait('import time\nfor i in range(10000):\n print(i)\n time.sleep(0.1)', 0); const cell = vscEditor.document.cells[0];