Skip to content

Commit 64e9d72

Browse files
committed
add support for exception notes printing + new tests
1 parent f4ce4ba commit 64e9d72

3 files changed

Lines changed: 102 additions & 26 deletions

File tree

graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646

4747

4848
class ExceptStarPrintTest(unittest.TestCase):
49-
@skipIfBytecodeDSL("a")
5049
def test_01_eg_simple(self):
5150
script = textwrap.dedent("""
5251
raise ExceptionGroup("eg", [
@@ -65,7 +64,6 @@ def test_01_eg_simple(self):
6564
b' +------------------------------------']
6665
self.assertEqual(p.stderr.splitlines(), expected)
6766

68-
@skipIfBytecodeDSL("a")
6967
def test_02_eg_nested(self):
7068
script = textwrap.dedent("""
7169
raise ExceptionGroup("EG", [
@@ -170,7 +168,6 @@ def test_02_eg_nested(self):
170168
self.maxDiff = None
171169
self.assertEqual(p.stderr.splitlines(), expected)
172170

173-
@skipIfBytecodeDSL("a")
174171
def test_03_eg_nested_truncated(self):
175172
script = textwrap.dedent("""
176173
raise ExceptionGroup("EG", [
@@ -385,7 +382,6 @@ def test_03_eg_nested_truncated(self):
385382
self.maxDiff = None
386383
self.assertEqual(p.stderr.splitlines(), expected)
387384

388-
@skipIfBytecodeDSL("a")
389385
def test_04_eg_cause(self):
390386
script = textwrap.dedent("""
391387
EG = ExceptionGroup
@@ -420,7 +416,6 @@ def test_04_eg_cause(self):
420416
self.maxDiff = None
421417
self.assertEqual(p.stderr.splitlines(), expected)
422418

423-
@skipIfBytecodeDSL("a")
424419
def test_05_eg_context_with_context(self):
425420
script = textwrap.dedent("""
426421
EG = ExceptionGroup
@@ -464,7 +459,6 @@ def test_05_eg_context_with_context(self):
464459
self.maxDiff = None
465460
self.assertEqual(p.stderr.splitlines(), expected)
466461

467-
@skipIfBytecodeDSL("a")
468462
def test_06_eg_nested_with_context(self):
469463
script = textwrap.dedent("""
470464
EG = ExceptionGroup
@@ -556,5 +550,57 @@ def test_07_eg_with_notes(self):
556550
b' | the terrible value',
557551
b' +------------------------------------',]
558552

553+
self.maxDiff = None
554+
self.assertEqual(p.stderr.splitlines(), expected)
555+
556+
def test_08_eg_with_multiple_notes(self):
557+
script = textwrap.dedent("""
558+
try:
559+
excs = []
560+
for msg in ['bad value', 'terrible value']:
561+
try:
562+
raise ValueError(msg)
563+
except ValueError as e:
564+
e.add_note(f'the {msg}')
565+
e.add_note(f'Goodbye {msg}')
566+
excs.append(e)
567+
raise ExceptionGroup("nested", excs)
568+
except ExceptionGroup as e:
569+
e.add_note(('>> Multi line note\\n'
570+
'>> Because I am such\\n'
571+
'>> an important exception.\\n'
572+
'>> empty lines work too\\n'
573+
'\\n'
574+
'(that was an empty line)'))
575+
e.add_note('Goodbye!')
576+
raise
577+
""")
578+
579+
p = subprocess.run([sys.executable, "-c", script], capture_output=True)
580+
581+
expected = [b' + Exception Group Traceback (most recent call last):',
582+
b' | File "<string>", line 11, in <module>',
583+
b' | ExceptionGroup: nested (2 sub-exceptions)',
584+
b' | >> Multi line note',
585+
b' | >> Because I am such',
586+
b' | >> an important exception.',
587+
b' | >> empty lines work too',
588+
b' | ',
589+
b' | (that was an empty line)',
590+
b' | Goodbye!',
591+
b' +-+---------------- 1 ----------------',
592+
b' | Traceback (most recent call last):',
593+
b' | File "<string>", line 6, in <module>',
594+
b' | ValueError: bad value',
595+
b' | the bad value',
596+
b' | Goodbye bad value',
597+
b' +---------------- 2 ----------------',
598+
b' | Traceback (most recent call last):',
599+
b' | File "<string>", line 6, in <module>',
600+
b' | ValueError: terrible value',
601+
b' | the terrible value',
602+
b' | Goodbye terrible value',
603+
b' +------------------------------------',]
604+
559605
self.maxDiff = None
560606
self.assertEqual(p.stderr.splitlines(), expected)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
import static com.oracle.graal.python.nodes.BuiltinNames.T___DISPLAYHOOK__;
102102
import static com.oracle.graal.python.nodes.BuiltinNames.T___EXCEPTHOOK__;
103103
import static com.oracle.graal.python.nodes.BuiltinNames.T___GRAALPYTHON__;
104+
import static com.oracle.graal.python.nodes.BuiltinNames.T___NOTES__;
104105
import static com.oracle.graal.python.nodes.BuiltinNames.T___STDERR__;
105106
import static com.oracle.graal.python.nodes.BuiltinNames.T___STDIN__;
106107
import static com.oracle.graal.python.nodes.BuiltinNames.T___STDOUT__;
@@ -115,6 +116,7 @@
115116
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___;
116117
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__;
117118
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SIZEOF__;
119+
import static com.oracle.graal.python.nodes.StringLiterals.J_NEWLINE;
118120
import static com.oracle.graal.python.nodes.StringLiterals.T_BACKSLASHREPLACE;
119121
import static com.oracle.graal.python.nodes.StringLiterals.T_BASE_PREFIX;
120122
import static com.oracle.graal.python.nodes.StringLiterals.T_BIG;
@@ -180,6 +182,7 @@
180182
import com.oracle.graal.python.builtins.objects.function.PArguments;
181183
import com.oracle.graal.python.builtins.objects.function.PKeyword;
182184
import com.oracle.graal.python.builtins.objects.ints.PInt;
185+
import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes;
183186
import com.oracle.graal.python.builtins.objects.list.PList;
184187
import com.oracle.graal.python.builtins.objects.module.PythonModule;
185188
import com.oracle.graal.python.builtins.objects.namespace.PSimpleNamespace;
@@ -1518,13 +1521,13 @@ private static void printErrorText(Object out, SyntaxErrData syntaxErrData) {
15181521

15191522
@TruffleBoundary
15201523
void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode,
1521-
PythonModule sys, Object out, Object value, Set<Object> seen) {
1522-
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, new ExceptionPrintContext());
1524+
PythonModule sys, Object out, Object value, Set<Object> seen, IteratorNodes.ToArrayNode toArrayNode) {
1525+
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, new ExceptionPrintContext(), toArrayNode);
15231526
}
15241527

15251528
@TruffleBoundary
15261529
void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode,
1527-
PythonModule sys, Object out, Object value, Set<Object> seen, ExceptionPrintContext ctx) {
1530+
PythonModule sys, Object out, Object value, Set<Object> seen, ExceptionPrintContext ctx, IteratorNodes.ToArrayNode toArrayNode) {
15281531
if (seen != null) {
15291532
// Exception chaining
15301533
add(seen, value);
@@ -1535,22 +1538,22 @@ void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTraceback
15351538
boolean needsToEnd = ctx.needsToEnd;
15361539
if (cause != PNone.NONE) {
15371540
if (notSeen(seen, cause)) {
1538-
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, cause, seen, ctx);
1541+
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, cause, seen, ctx, toArrayNode);
15391542
fileWriteString(out, T_CAUSE_MESSAGE);
15401543
}
15411544
} else if (context != PNone.NONE && !ExceptionNodes.GetSuppressContextNode.executeUncached(value)) {
15421545
if (notSeen(seen, context)) {
1543-
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, context, seen, ctx);
1546+
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, context, seen, ctx, toArrayNode);
15441547
fileWriteString(out, T_CONTEXT_MESSAGE);
15451548
}
15461549
}
15471550
ctx.needsToEnd = needsToEnd;
15481551
}
15491552
}
15501553
if (value instanceof PBaseExceptionGroup) {
1551-
printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, ctx);
1554+
printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, ctx, toArrayNode);
15521555
} else {
1553-
printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, ctx);
1556+
printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, ctx, toArrayNode);
15541557
}
15551558
}
15561559

@@ -1568,7 +1571,7 @@ protected static void fileWriteIndentedString(Object file, TruffleString string,
15681571
}
15691572

15701573
protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode,
1571-
PythonModule sys, Object out, Object excValue, ExceptionPrintContext ctx) {
1574+
PythonModule sys, Object out, Object excValue, ExceptionPrintContext ctx, IteratorNodes.ToArrayNode toArrayNode) {
15721575
Object value = excValue;
15731576
final Object type = getObjectClass(value);
15741577
if (!PyExceptionInstanceCheckNode.executeUncached(value)) {
@@ -1644,10 +1647,35 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac
16441647
}
16451648

16461649
fileWriteString(out, T_NEWLINE);
1650+
1651+
if (objectHasAttr(value, T___NOTES__)) {
1652+
// print notes
1653+
Object notes = objectLookupAttr(value, T___NOTES__);
1654+
if (notes instanceof PList noteList) {
1655+
Object[] arr = toArrayNode.execute(null, noteList);
1656+
for (Object oStr : arr) {
1657+
if (oStr instanceof TruffleString note) {
1658+
String n = note.toString();
1659+
if (n.contains(J_NEWLINE)) {
1660+
String[] lines = n.split(J_NEWLINE);
1661+
for (String line : lines) {
1662+
fileWriteIndentedString(out, ctx.getMargin(), ctx.getIndent());
1663+
fileWriteString(out, line);
1664+
fileWriteString(out, T_NEWLINE);
1665+
}
1666+
} else {
1667+
fileWriteIndentedString(out, ctx.getMargin(), ctx.getIndent());
1668+
fileWriteString(out, note);
1669+
fileWriteString(out, T_NEWLINE);
1670+
}
1671+
}
1672+
}
1673+
}
1674+
}
16471675
}
16481676

16491677
protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode,
1650-
PythonModule sys, Object out, Object excValue, Set<Object> seen, ExceptionPrintContext ctx) {
1678+
PythonModule sys, Object out, Object excValue, Set<Object> seen, ExceptionPrintContext ctx, IteratorNodes.ToArrayNode toArrayNode) {
16511679
Object value = excValue;
16521680
final Object type = getObjectClass(value);
16531681
if (!PyExceptionGroupInstanceCheckNode.executeUncached(value)) {
@@ -1669,7 +1697,7 @@ protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTra
16691697
ctx.increaseDepth();
16701698
}
16711699

1672-
printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excValue, ctx);
1700+
printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excValue, ctx, toArrayNode);
16731701

16741702
PBaseExceptionGroup exceptionGroup = (PBaseExceptionGroup) excValue;
16751703
int counter = 1;
@@ -1688,7 +1716,7 @@ protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTra
16881716
fileWriteString(out, String.format("+---------------- %d ----------------", counter));
16891717
fileWriteString(out, T_NEWLINE);
16901718
ctx.increaseDepth();
1691-
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, exception, seen, ctx);
1719+
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, exception, seen, ctx, toArrayNode);
16921720
ctx.decreaseDepth();
16931721
} else {
16941722
fileWriteString(out, "+---------------- ... ----------------");
@@ -1757,10 +1785,11 @@ Object doHookWithTb(VirtualFrame frame, PythonModule sys, @SuppressWarnings("unu
17571785
@Bind Node inliningTarget,
17581786
@Shared @Cached TracebackBuiltins.GetTracebackFrameNode getTbFrameNode,
17591787
@Shared @Cached TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode,
1760-
@Shared @Cached("createFor($node)") BoundaryCallData boundaryCallData) {
1788+
@Shared @Cached("createFor($node)") BoundaryCallData boundaryCallData,
1789+
@Shared @Cached IteratorNodes.ToArrayNode toArrayNode) {
17611790
Object saved = BoundaryCallContext.enter(frame, boundaryCallData);
17621791
try {
1763-
doHookWithTbImpl(inliningTarget, getTbFrameNode, materializeStNode, sys, value, traceBack);
1792+
doHookWithTbImpl(inliningTarget, getTbFrameNode, materializeStNode, sys, value, traceBack, toArrayNode);
17641793
} finally {
17651794
BoundaryCallContext.exit(frame, boundaryCallData, saved);
17661795
}
@@ -1769,10 +1798,10 @@ Object doHookWithTb(VirtualFrame frame, PythonModule sys, @SuppressWarnings("unu
17691798

17701799
@TruffleBoundary
17711800
private void doHookWithTbImpl(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode,
1772-
PythonModule sys, Object value, PTraceback traceBack) {
1801+
PythonModule sys, Object value, PTraceback traceBack, IteratorNodes.ToArrayNode toArrayNode) {
17731802
setExceptionTraceback(value, traceBack);
17741803
Object stdErr = objectLookupAttr(sys, T_STDERR);
1775-
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, stdErr, value, createSet());
1804+
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, stdErr, value, createSet(), toArrayNode);
17761805
fileFlush(stdErr);
17771806
}
17781807

@@ -1781,10 +1810,11 @@ Object doHookWithoutTb(VirtualFrame frame, PythonModule sys, @SuppressWarnings("
17811810
@Bind Node inliningTarget,
17821811
@Shared @Cached TracebackBuiltins.GetTracebackFrameNode getTbFrameNode,
17831812
@Shared @Cached TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode,
1784-
@Shared @Cached("createFor($node)") BoundaryCallData boundaryCallData) {
1813+
@Shared @Cached("createFor($node)") BoundaryCallData boundaryCallData,
1814+
@Shared @Cached IteratorNodes.ToArrayNode toArrayNode) {
17851815
Object saved = BoundaryCallContext.enter(frame, boundaryCallData);
17861816
try {
1787-
doHookWithoutTbImpl(inliningTarget, getTbFrameNode, materializeStNode, sys, value);
1817+
doHookWithoutTbImpl(inliningTarget, getTbFrameNode, materializeStNode, sys, value, toArrayNode);
17881818
} finally {
17891819
BoundaryCallContext.exit(frame, boundaryCallData, saved);
17901820
}
@@ -1793,9 +1823,9 @@ Object doHookWithoutTb(VirtualFrame frame, PythonModule sys, @SuppressWarnings("
17931823

17941824
@TruffleBoundary
17951825
private void doHookWithoutTbImpl(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode,
1796-
PythonModule sys, Object value) {
1826+
PythonModule sys, Object value, IteratorNodes.ToArrayNode toArrayNode) {
17971827
Object stdErr = objectLookupAttr(sys, T_STDERR);
1798-
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, stdErr, value, createSet());
1828+
printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, stdErr, value, createSet(), toArrayNode);
17991829
fileFlush(stdErr);
18001830
}
18011831
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ private static void printInternal(Node inliningTarget, TracebackBuiltins.GetTrac
382382
}
383383

384384
public static void print(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, PythonModule sys,
385-
Object out, Object tbObj, boolean isExceptionGroup, boolean printMarginControl, int indent, TruffleString margin) {
385+
Object out, Object tbObj, boolean isExceptionGroup, boolean printMarginControl, int indent, TruffleString margin) {
386386
// Although we should be behind TB, we need cached nodes, because they may do stack walking
387387
// and for that they must be connected to the currently executing root. In practice, it's
388388
// not strictly necessary, because they will never request the current frame, but in order

0 commit comments

Comments
 (0)