diff --git a/extensions/notebook-renderers/src/stackTraceHelper.ts b/extensions/notebook-renderers/src/stackTraceHelper.ts
index 79441e8db35..3228628029d 100644
--- a/extensions/notebook-renderers/src/stackTraceHelper.ts
+++ b/extensions/notebook-renderers/src/stackTraceHelper.ts
@@ -22,5 +22,46 @@ export function formatStackTrace(stack: string) {
return `${prefix}${num}${suffix}\n`;
});
+ if (isIpythonStackTrace(stack)) {
+ return linkifyStack(stack);
+ }
+
return cleaned;
}
+
+function isIpythonStackTrace(stack: string) {
+ const cellIdentifier = /^Cell In\[\d+\], line \d+$/gm;
+ return cellIdentifier.test(stack);
+}
+
+const fileRegex = /^File\s+(.+):\d+/;
+const lineNumberRegex = /([ ->]*?)(\d+)(.*)/;
+
+function linkifyStack(stack: string) {
+ const lines = stack.split('\n');
+
+ let fileOrCell: string | undefined;
+
+ for (const i in lines) {
+
+ const original = lines[i];
+ console.log(`linkify ${original}`); // REMOVE
+ if (fileRegex.test(original)) {
+ const fileMatch = lines[i].match(fileRegex);
+ fileOrCell = fileMatch![1];
+ console.log(`matched file ${fileOrCell}`); // REMOVE
+ continue;
+ } else if (!fileOrCell || original.trim() === '') {
+ // we don't have a location, so don't linkify anything
+ fileOrCell = undefined;
+ continue;
+ } else if (lineNumberRegex.test(original)) {
+ console.log(`linkify line ${original}`); // REMOVE
+ lines[i] = original.replace(lineNumberRegex, (_s, prefix, num, suffix) => {
+ return `${prefix}${num}${suffix}`;
+ });
+ }
+ }
+
+ return lines.join('\n');
+}
diff --git a/extensions/notebook-renderers/src/test/stackTraceHelper.test.ts b/extensions/notebook-renderers/src/test/stackTraceHelper.test.ts
index 0fe1b861488..b40ec59e652 100644
--- a/extensions/notebook-renderers/src/test/stackTraceHelper.test.ts
+++ b/extensions/notebook-renderers/src/test/stackTraceHelper.test.ts
@@ -18,7 +18,7 @@ suite('StackTraceHelper', () => {
assert.equal(formatStackTrace(stack), stack);
});
- test('IPython cell references are linkified', () => {
+ test('IPython stack line numbers are linkified', () => {
const stack =
'---------------------------------------------------------------------------\n' +
'Exception Traceback(most recent call last)\n' +
@@ -31,7 +31,21 @@ suite('StackTraceHelper', () => {
'----> 2 raise Exception\n';
const formatted = formatStackTrace(stack);
- assert.ok(formatted.indexOf);
+ assert.ok(formatted.indexOf('2') > 0, formatted);
+ });
+
+ test('IPython stack trace lines without associated location are not linkified', () => {
+ const stack =
+ '---------------------------------------------------------------------------\n' +
+ 'Exception Traceback(most recent call last)\n' +
+ 'File C:\\venvs\\myLib.py:2, in throwEx()\n' +
+ '\n' +
+ 'unkown reference' +
+ ' 1 import myLib\n' + // trace lines without an associated file
+ '----> 2 myLib.throwEx()\n';
+
+ const formatted = formatStackTrace(stack);
+ assert.ok(formatted.indexOf('