diff --git a/extensions/notebook-renderers/src/stackTraceHelper.ts b/extensions/notebook-renderers/src/stackTraceHelper.ts index 9570ab8762d..63a46e3d356 100644 --- a/extensions/notebook-renderers/src/stackTraceHelper.ts +++ b/extensions/notebook-renderers/src/stackTraceHelper.ts @@ -32,8 +32,10 @@ export function formatStackTrace(stack: string): { formattedStack: string; error const formatSequence = /\u001b\[.+?m/g; const fileRegex = /File\s+(?:\u001b\[.+?m)?(.+):(\d+)/; -const lineNumberRegex = /^((?:\u001b\[.+?m)?[ \->]+?)(\d+)(?:\u001b\[0m)?( .*)/; -const cellRegex = /(?Cell\s+(?:\u001b\[.+?m)?In\s*\[(?\d+)\],\s*)(?line (?\d+)).*/; +// look for the "--->" before a line number +const lineNumberRegex = /^((?:\u001b\[.+?m|\s)*[-]+>(?:\u001b\[.+?m|\s)*)(\d+)(?:\u001b\[.+?m)*( .*)/; +// just capturing parts of "Cell In[3], line 2" with lots of formatting in between +const cellRegex = /(?(?:\u001b\[.+?m)*Cell(?:\u001b\[.+?m|\s)*In(?:\u001b\[.+?m|\s)*\[(?\d+)\](?:\u001b\[.+?m|\s|,)+)(?line (?\d+)).*/; // older versions of IPython ~8.3.0 const inputRegex = /(?Input\s+?(?:\u001b\[.+?m)(?In\s*\[(?\d+)\]))(?.*)/; diff --git a/extensions/notebook-renderers/src/test/stackTraceHelper.test.ts b/extensions/notebook-renderers/src/test/stackTraceHelper.test.ts index faae56894f8..c2893e97365 100644 --- a/extensions/notebook-renderers/src/test/stackTraceHelper.test.ts +++ b/extensions/notebook-renderers/src/test/stackTraceHelper.test.ts @@ -24,7 +24,7 @@ suite('StackTraceHelper', () => { return text.replace(formatSequence, ''); } - test('IPython stack line numbers are linkified', () => { + test('IPython stack line numbers are linkified for IPython 8.3.6', () => { const stack = '\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n' + '\u001b[1;31mException\u001b[0m Traceback (most recent call last)\n' + @@ -45,6 +45,24 @@ suite('StackTraceHelper', () => { assert.equal(errorLocation, 'line 2'); }); + test('IPython stack line numbers are linkified for IPython 9.0.0', () => { + const stack = + '\u001b[31m---------------------------------------------------------------------------\u001b[39m\n' + + '\u001b[31mTypeError\u001b[39m Traceback (most recent call last)\n' + + '\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 2\u001b[39m\n' + + '\u001b[32m 1\u001b[39m x = firstItem((\u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m, \u001b[32m3\u001b[39m, \u001b[32m5\u001b[39m))\n' + + '\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m y = \u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[43m+\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m1\u001b[39;49m\n' + + '\u001b[32m 3\u001b[39m \u001b[38;5;28mprint\u001b[39m(y)\n' + + '\n' + + '\u001b[31mTypeError\u001b[39m: unsupported operand type(s) for +: "NoneType" and "int"\n'; + + const { formattedStack, errorLocation } = formatStackTrace(stack); + const cleanStack = stripAsciiFormatting(formattedStack); + assert.ok(cleanStack.indexOf('Cell In[3], line 2') > 0, 'Missing line link in ' + cleanStack); + assert.ok(cleanStack.indexOf('2') > 0, 'Missing frame link in ' + cleanStack); + assert.equal(errorLocation, 'line 2'); + }); + test('IPython stack line numbers are linkified for IPython 8.3', () => { // stack frames within functions do not list the line number, i.e. // 'Input In [1], in myfunc()' vs