clean raw jupyter error stack traces

This commit is contained in:
Aaron Munger 2023-10-13 14:05:57 -07:00 committed by Aaron Munger
parent 9e36f4c03e
commit 8e8811a5c1
3 changed files with 57 additions and 1 deletions

View file

@ -7,6 +7,7 @@ import type { ActivationFunction, OutputItem, RendererContext } from 'vscode-not
import { createOutputContent, appendOutput, scrollableClass } from './textHelper';
import { HtmlRenderingHook, IDisposable, IRichRenderContext, JavaScriptRenderingHook, OutputWithAppend, RenderOptions } from './rendererTypes';
import { ttPolicy } from './htmlHelper';
import { cleanStackTrace } from './stackTraceHelper';
function clearContainer(container: HTMLElement) {
while (container.firstChild) {
@ -172,8 +173,10 @@ function renderError(
if (err.stack) {
outputElement.classList.add('traceback');
const stackTrace = cleanStackTrace(err.stack);
const outputScrolling = scrollingEnabled(outputInfo, ctx.settings);
const content = createOutputContent(outputInfo.id, err.stack ?? '', { linesLimit: ctx.settings.lineLimit, scrollable: outputScrolling, trustHtml });
const content = createOutputContent(outputInfo.id, stackTrace ?? '', { linesLimit: ctx.settings.lineLimit, scrollable: outputScrolling, trustHtml });
const contentParent = document.createElement('div');
contentParent.classList.toggle('word-wrap', ctx.settings.outputWordWrap);
disposableStore.push(ctx.onDidChangeSettings(e => {

View file

@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function cleanStackTrace(stack: string) {
let cleaned: string;
// Ansi colors are described here:
// https://en.wikipedia.org/wiki/ANSI_escape_code under the SGR section
// Remove background colors. The ones from IPython don't work well with
// themes 40-49 sets background color
cleaned = stack.replace(/\u001b\[4\dm/g, '');
// Also remove specific foreground colors (38 is the ascii code for picking one) (they don't translate either)
// Turn them into default foreground
cleaned = cleaned.replace(/\u001b\[38;.*?\d+m/g, '\u001b[39m');
// Turn all foreground colors after the --> to default foreground
cleaned = cleaned.replace(/(;32m[ ->]*?)(\d+)(.*)\n/g, (_s, prefix, num, suffix) => {
suffix = suffix.replace(/\u001b\[3\d+m/g, '\u001b[39m');
return `${prefix}${num}${suffix}\n`;
});
return cleaned;
}

View file

@ -451,5 +451,32 @@ suite('Notebook builtin output renderer', () => {
assert.equal(settingsChangedHandlers.length, handlerCount);
});
const rawIPythonError = {
name: "NameError",
message: "name 'x' is not defined",
stack: "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m" +
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)" +
"Cell \u001b[1;32mIn[2], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mmyfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n" +
"Cell \u001b[1;32mIn[1], line 2\u001b[0m, in \u001b[0;36mmyfunc\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mmyfunc\u001b[39m():\n\u001b[1;32m----> 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mx\u001b[49m)\n" +
"\u001b[1;31mNameError\u001b[0m: name 'x' is not defined"
};
test(`Should clean up raw IPython error stack traces`, async () => {
LinkDetector.injectedHtmlCreator = (value: string) => value;
const context = createContext({ outputWordWrap: true, outputScrolling: true });
const renderer = await activate(context);
assert.ok(renderer, 'Renderer not created');
const outputElement = new OutputHtml().getFirstOuputElement();
const outputItem = createOutputItem(JSON.stringify(rawIPythonError), errorMimeType);
await renderer!.renderOutputItem(outputItem, outputElement);
const inserted = outputElement.firstChild as HTMLElement;
assert.ok(inserted, `nothing appended to output element: ${outputElement.innerHTML}`);
//assert.ok(false, `TextContent:\n ${outputElement.textContent}`);
assert.ok(outputElement.innerHTML.indexOf('class="code-background-colored"') === -1, `inner HTML:\n ${outputElement.innerHTML}`);
});
});