From 9dfb15aa169a1900983d9a0087c53c03dc1dce3c Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Thu, 15 Jan 2026 13:21:04 +0100 Subject: [PATCH 01/13] add exception group instance check node --- .../PyExceptionGroupInstanceCheckNode.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyExceptionGroupInstanceCheckNode.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyExceptionGroupInstanceCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyExceptionGroupInstanceCheckNode.java new file mode 100644 index 0000000000..77544e1459 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyExceptionGroupInstanceCheckNode.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.lib; + +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.builtins.objects.exception.PBaseException; +import com.oracle.graal.python.builtins.objects.exception.PBaseExceptionGroup; +import com.oracle.graal.python.nodes.classes.IsSubtypeNode; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.GenerateCached; +import com.oracle.truffle.api.dsl.GenerateInline; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.exception.AbstractTruffleException; +import com.oracle.truffle.api.nodes.Node; + +@GenerateInline +@GenerateCached(false) +@GenerateUncached +public abstract class PyExceptionGroupInstanceCheckNode extends Node { + public abstract boolean execute(Node inliningTarget, Object object); + + public static boolean executeUncached(Object object) { + return PyExceptionInstanceCheckNodeGen.getUncached().execute(null, object); + } + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PBaseExceptionGroup exception) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject object, + @Cached GetClassNode getClassNode, + @Cached(inline = false) IsSubtypeNode isSubtypeNode) { + // May be native or interop + return isSubtypeNode.execute(getClassNode.execute(inliningTarget, object), PythonBuiltinClassType.PBaseExceptionGroup); + } + + @Specialization + static boolean doInterop(@SuppressWarnings("unused") AbstractTruffleException exception) { + return false; + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object exception) { + return false; + } +} From e4eb8ab02a92f6c679018568fe53b2dbf7247499 Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Thu, 15 Jan 2026 13:21:39 +0100 Subject: [PATCH 02/13] add margin and indentation to traceback printing --- .../graal/python/lib/PyTraceBackPrint.java | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java index 368b2778ee..8bea087a8b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java @@ -48,6 +48,7 @@ import static com.oracle.graal.python.nodes.StringLiterals.T_SPACE; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.io.BufferedReader; import java.io.IOException; @@ -238,16 +239,20 @@ private static void printLineRepeated(Object out, int count) { fileWriteString(out, sbToString(sb)); } - private static void displayLine(Object out, TruffleString fileName, int lineNo, TruffleString name) { + private static void displayLine(Object out, TruffleString fileName, int lineNo, TruffleString name, int indent, TruffleString margin) { if (fileName == null || name == null) { return; } - final StringBuilder sb = newStringBuilder(" File \""); + boolean withIndentOrMargin = indent > 0 || !margin.isEmpty(); + final StringBuilder sb = withIndentOrMargin ? new StringBuilder() : newStringBuilder(" File \""); + if (withIndentOrMargin) { + append(sb, getIndent(indent), margin, " File \""); + } append(sb, fileName, "\", line ", lineNo, ", in ", name, J_NEWLINE); fileWriteString(out, sbToString(sb)); // ignore errors since we can't report them, can we? - displaySourceLine(out, fileName, lineNo, 4); + displaySourceLine(out, fileName, lineNo, 4, indent, margin); } protected static TruffleString getIndent(int indent) { @@ -287,9 +292,11 @@ protected static CharSequence getSourceLine(TruffleString fileName, int lineNo) return line; } - private static void displaySourceLine(Object out, TruffleString fileName, int lineNo, int indent) { + private static void displaySourceLine(Object out, TruffleString fileName, int lineNo, int indent, int marginIndent, TruffleString margin) { final CharSequence line = getSourceLine(fileName, lineNo); if (line != null) { + fileWriteString(out, getIndent(marginIndent)); + fileWriteString(out, margin); fileWriteString(out, getIndent(indent)); fileWriteString(out, trimLeft(line)); fileWriteString(out, J_NEWLINE); @@ -324,8 +331,14 @@ private static String trimLeft(CharSequence sequence) { return (st > 0 ? sequence.subSequence(st, len) : sequence).toString(); } + private static void printIndentedHeader(Object out, String header, int indent, String margin) { + String sb = " ".repeat(indent) + margin + header + "\n"; + fileWriteString(out, sb); + } + private static void printInternal(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, - TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, Object out, PTraceback traceback, long limit) { + TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, Object out, PTraceback traceback, long limit, + int indent, TruffleString margin) { int depth = 0; TruffleString lastFile = null; int lastLine = -1; @@ -359,7 +372,7 @@ private static void printInternal(Node inliningTarget, TracebackBuiltins.GetTrac } cnt++; if (cnt <= TB_RECURSIVE_CUTOFF) { - displayLine(out, code.getFilename(), tb.getLineno(), code.getName()); + displayLine(out, code.getFilename(), tb.getLineno(), code.getName(), indent, margin); } tb = getNextTb(inliningTarget, materializeStNode, tb); } @@ -369,7 +382,7 @@ private static void printInternal(Node inliningTarget, TracebackBuiltins.GetTrac } public static void print(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, PythonModule sys, - Object out, Object tbObj) { + Object out, Object tbObj, boolean isExceptionGroup, int indent, TruffleString margin) { // Although we should be behind TB, we need cached nodes, because they may do stack walking // and for that they must be connected to the currently executing root. In practice, it's // not strictly necessary, because they will never request the current frame, but in order @@ -378,6 +391,10 @@ public static void print(Node inliningTarget, TracebackBuiltins.GetTracebackFram assert inliningTarget != null && inliningTarget.isAdoptable(); assert getTbFrameNode.isAdoptable(); + if (margin == null) { + margin = tsLiteral(""); + } + if (tbObj instanceof PTraceback tb) { long limit = TRACEBACK_LIMIT; final Object limitv = ReadAttributeFromObjectNode.getUncached().execute(sys, BuiltinNames.T_TRACEBACKLIMIT); @@ -387,8 +404,12 @@ public static void print(Node inliningTarget, TracebackBuiltins.GetTracebackFram return; } } - fileWriteString(out, "Traceback (most recent call last):\n"); - printInternal(inliningTarget, getTbFrameNode, materializeStNode, out, tb, limit); + if (isExceptionGroup) { + printIndentedHeader(out, "Exception Group Traceback (most recent call last) (Java traceback):", indent, "+ "); + } else { + printIndentedHeader(out, "Traceback (most recent call last) (Java traceback):", indent, ""); + } + printInternal(inliningTarget, getTbFrameNode, materializeStNode, out, tb, limit, indent, margin); } else { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.SystemError, BAD_ARG_TO_INTERNAL_FUNC); } From 4b1c397dfa4120e016b4ae66688b67babe84cc3e Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Thu, 15 Jan 2026 13:22:03 +0100 Subject: [PATCH 03/13] exception group pretty printing with max width support --- .../builtins/modules/SysModuleBuiltins.java | 93 +++++++++++++++++-- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index 3185a01e7d..d5b7f04002 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -126,12 +126,14 @@ import static com.oracle.graal.python.nodes.StringLiterals.T_LITTLE; import static com.oracle.graal.python.nodes.StringLiterals.T_NEWLINE; import static com.oracle.graal.python.nodes.StringLiterals.T_PREFIX; +import static com.oracle.graal.python.nodes.StringLiterals.T_SPACE; import static com.oracle.graal.python.nodes.StringLiterals.T_STRICT; import static com.oracle.graal.python.nodes.StringLiterals.T_STRING_SOURCE; import static com.oracle.graal.python.nodes.StringLiterals.T_SURROGATEESCAPE; import static com.oracle.graal.python.nodes.StringLiterals.T_VALUE_UNKNOWN; import static com.oracle.graal.python.nodes.StringLiterals.T_VERSION; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.toIntError; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static com.oracle.graal.python.util.PythonUtils.tsInternedLiteral; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; @@ -174,6 +176,8 @@ import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; import com.oracle.graal.python.builtins.objects.exception.GetEscapedExceptionNode; +import com.oracle.graal.python.builtins.objects.exception.PBaseException; +import com.oracle.graal.python.builtins.objects.exception.PBaseExceptionGroup; import com.oracle.graal.python.builtins.objects.frame.PFrame; import com.oracle.graal.python.builtins.objects.function.PArguments; import com.oracle.graal.python.builtins.objects.function.PKeyword; @@ -192,6 +196,7 @@ import com.oracle.graal.python.builtins.objects.tuple.StructSequence; import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins; import com.oracle.graal.python.lib.OsEnvironGetNode; +import com.oracle.graal.python.lib.PyExceptionGroupInstanceCheckNode; import com.oracle.graal.python.lib.PyExceptionInstanceCheckNode; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckExactNode; @@ -1250,7 +1255,7 @@ private void writeUnraisableExc(Node inliningTarget, TracebackBuiltins.GetTraceb } if (excTb != PNone.NONE) { - PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excTb); + PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excTb, false, 0, null); } if (excType == PNone.NONE) { @@ -1344,6 +1349,9 @@ abstract static class ExceptHookNode extends PythonBuiltinNode { static final TruffleString T_ATTR_OFFSET = tsInternedLiteral("offset"); static final TruffleString T_ATTR_TEXT = tsInternedLiteral("text"); + protected static final int INT_MAX_GROUP_WIDTH = 15; + protected static final int INT_MAX_GROUP_DEPTH = 10; + @ValueType static final class SyntaxErrData { final Object message; @@ -1474,7 +1482,13 @@ private static void printErrorText(Object out, SyntaxErrData syntaxErrData) { @TruffleBoundary void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object value, Set seen) { + PythonModule sys, Object out, Object value, Set seen) { + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, 0, tsLiteral("")); + } + + @TruffleBoundary + void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, + PythonModule sys, Object out, Object value, Set seen, int indent, TruffleString margin) { if (seen != null) { // Exception chaining add(seen, value); @@ -1484,22 +1498,40 @@ void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTraceback if (cause != PNone.NONE) { if (notSeen(seen, cause)) { - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, cause, seen); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, cause, seen, indent, margin); fileWriteString(out, T_CAUSE_MESSAGE); } } else if (context != PNone.NONE && !ExceptionNodes.GetSuppressContextNode.executeUncached(value)) { if (notSeen(seen, context)) { - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, context, seen); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, context, seen, indent, margin); fileWriteString(out, T_CONTEXT_MESSAGE); } } } } - printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value); + if (value instanceof PBaseExceptionGroup) { + printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, indent, tsLiteral("| ")); + } + else { + printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, indent, margin); + } + } + + protected static TruffleString getIndent(int indent) { + return T_SPACE.repeatUncached(indent, TS_ENCODING); + } + + protected static void fileWriteIndentedString(Object file, String string, int indent) { + fileWriteIndentedString(file, tsLiteral(string), indent); + } + + protected static void fileWriteIndentedString(Object file, TruffleString string, int indent) { + fileWriteString(file, getIndent(indent)); + fileWriteString(file, string); } protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object excValue) { + PythonModule sys, Object out, Object excValue, int indent, TruffleString margin) { Object value = excValue; final Object type = getObjectClass(value); if (!PyExceptionInstanceCheckNode.executeUncached(value)) { @@ -1511,7 +1543,7 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac final Object tb = getExceptionTraceback(value); if (tb instanceof PTraceback) { - PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, tb); + PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, tb, (value instanceof PBaseExceptionGroup), indent, margin); } if (objectHasAttr(value, T_ATTR_PRINT_FILE_AND_LINE)) { @@ -1530,6 +1562,8 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac } } + fileWriteIndentedString(out, margin, indent); + TruffleString className; try { className = getTypeName(type); @@ -1571,6 +1605,51 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac fileWriteString(out, T_NEWLINE); } + protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, + PythonModule sys, Object out, Object excValue, Set seen, int indent, TruffleString margin) { + Object value = excValue; + final Object type = getObjectClass(value); + if (!PyExceptionGroupInstanceCheckNode.executeUncached(value)) { + PyTraceBackPrint.fileWriteString(out, "TypeError: print_exception_group(): Exception group expected for value, "); + fileWriteString(out, getTypeName(type)); + PyTraceBackPrint.fileWriteString(out, " found\n"); + return; + } + + if (indent == 0) { + indent = 2; + } + + printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excValue, indent, margin); + + PBaseExceptionGroup exceptionGroup = (PBaseExceptionGroup) excValue; + int counter = 1; + for (Object exception : exceptionGroup.getExceptions()) { + if (counter == 1) { + fileWriteIndentedString(out, "+-", indent); + } else { + fileWriteString(out, getIndent(indent + 2)); + } + if (counter <= INT_MAX_GROUP_WIDTH) { + fileWriteString(out, String.format("+---------------- %d ----------------", counter)); + fileWriteString(out, T_NEWLINE); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, exception, seen, indent + 2, margin); + } else { + fileWriteString(out, String.format("+---------------- ... ----------------", counter)); + fileWriteString(out, T_NEWLINE); + fileWriteIndentedString(out, margin, indent + 2); + int exceptionsRemaining = exceptionGroup.getExceptions().length - INT_MAX_GROUP_WIDTH; + fileWriteString(out, String.format("and %d more exception%s", exceptionsRemaining, exceptionsRemaining > 1 ? "s" : "")); + fileWriteString(out, T_NEWLINE); + break; + } + counter++; + } + fileWriteString(out, getIndent(indent + 2)); + fileWriteString(out, "+------------------------------------"); + fileWriteString(out, T_NEWLINE); + } + @TruffleBoundary(allowInlining = true) private static StringBuilder newStringBuilder(String str) { return new StringBuilder(str); From 9cf92354ad7cdf979b577694499a3323ff188365 Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Thu, 15 Jan 2026 21:40:34 +0100 Subject: [PATCH 04/13] More compact ExceptionGroup print context. --- .../builtins/modules/SysModuleBuiltins.java | 103 ++++++++++++++---- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index d5b7f04002..a4f95015ec 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -1349,8 +1349,8 @@ abstract static class ExceptHookNode extends PythonBuiltinNode { static final TruffleString T_ATTR_OFFSET = tsInternedLiteral("offset"); static final TruffleString T_ATTR_TEXT = tsInternedLiteral("text"); - protected static final int INT_MAX_GROUP_WIDTH = 15; - protected static final int INT_MAX_GROUP_DEPTH = 10; + protected static final int INT_MAX_GROUP_WIDTH = 5; + protected static final int INT_MAX_GROUP_DEPTH = 3; @ValueType static final class SyntaxErrData { @@ -1371,6 +1371,54 @@ static final class SyntaxErrData { } } + static class ExceptionPrintContext { + public final int indent; + public final TruffleString margin; + public final int depthMax; + public final int depthCurrent; + public final int widthMax; + + ExceptionPrintContext(int indent, TruffleString margin) { + this.indent = indent; + this.margin = margin; + this.depthMax = INT_MAX_GROUP_DEPTH; + this.depthCurrent = 0; + this.widthMax = INT_MAX_GROUP_WIDTH; + } + + ExceptionPrintContext(int indent, TruffleString margin, int depthMax, int depthCurrent, int widthMax) { + this.indent = indent; + this.margin = margin; + this.depthMax = depthMax; + this.depthCurrent = depthCurrent; + this.widthMax = widthMax; + } + + ExceptionPrintContext(ExceptionPrintContext ctx) { + this.indent = ctx.indent; + this.margin = ctx.margin; + this.depthMax = ctx.depthMax; + this.depthCurrent = ctx.depthCurrent; + this.widthMax = ctx.widthMax; + } + + public ExceptionPrintContext withIndentIncrease() { + return new ExceptionPrintContext(indent + 2, margin, depthMax, depthCurrent, widthMax); + } + + public ExceptionPrintContext withIndent(int indent) { + return new ExceptionPrintContext(indent, margin, depthMax, depthCurrent, widthMax); + } + + public ExceptionPrintContext withMargin(TruffleString margin) { + return new ExceptionPrintContext(indent, margin, depthMax, depthCurrent, widthMax); + } + + public ExceptionPrintContext withDepthIncrease() { + return new ExceptionPrintContext(indent, margin, depthMax, depthCurrent + 1, widthMax); + } + } + private static SyntaxErrData parseSyntaxError(Object err) { Object v, msg; TruffleString fileName = null, text = null; @@ -1483,12 +1531,12 @@ private static void printErrorText(Object out, SyntaxErrData syntaxErrData) { @TruffleBoundary void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, PythonModule sys, Object out, Object value, Set seen) { - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, 0, tsLiteral("")); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, new ExceptionPrintContext(0, tsLiteral(""))); } @TruffleBoundary void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object value, Set seen, int indent, TruffleString margin) { + PythonModule sys, Object out, Object value, Set seen, ExceptionPrintContext ctx) { if (seen != null) { // Exception chaining add(seen, value); @@ -1498,22 +1546,22 @@ void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTraceback if (cause != PNone.NONE) { if (notSeen(seen, cause)) { - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, cause, seen, indent, margin); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, cause, seen, ctx); fileWriteString(out, T_CAUSE_MESSAGE); } } else if (context != PNone.NONE && !ExceptionNodes.GetSuppressContextNode.executeUncached(value)) { if (notSeen(seen, context)) { - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, context, seen, indent, margin); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, context, seen, ctx); fileWriteString(out, T_CONTEXT_MESSAGE); } } } } if (value instanceof PBaseExceptionGroup) { - printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, indent, tsLiteral("| ")); + printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, ctx.withMargin(tsLiteral("| "))); } else { - printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, indent, margin); + printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, ctx); } } @@ -1531,7 +1579,7 @@ protected static void fileWriteIndentedString(Object file, TruffleString string, } protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object excValue, int indent, TruffleString margin) { + PythonModule sys, Object out, Object excValue, ExceptionPrintContext ctx) { Object value = excValue; final Object type = getObjectClass(value); if (!PyExceptionInstanceCheckNode.executeUncached(value)) { @@ -1543,7 +1591,7 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac final Object tb = getExceptionTraceback(value); if (tb instanceof PTraceback) { - PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, tb, (value instanceof PBaseExceptionGroup), indent, margin); + PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, tb, (value instanceof PBaseExceptionGroup), ctx.indent, ctx.margin); } if (objectHasAttr(value, T_ATTR_PRINT_FILE_AND_LINE)) { @@ -1562,7 +1610,7 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac } } - fileWriteIndentedString(out, margin, indent); + fileWriteIndentedString(out, ctx.margin, ctx.indent); TruffleString className; try { @@ -1606,7 +1654,7 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac } protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object excValue, Set seen, int indent, TruffleString margin) { + PythonModule sys, Object out, Object excValue, Set seen, ExceptionPrintContext ctx) { Object value = excValue; final Object type = getObjectClass(value); if (!PyExceptionGroupInstanceCheckNode.executeUncached(value)) { @@ -1616,37 +1664,46 @@ protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTra return; } - if (indent == 0) { - indent = 2; + if (ctx.depthCurrent >= ctx.depthMax) { + fileWriteIndentedString(out, ctx.margin, ctx.indent); + fileWriteString(out, String.format("... (max_group_depth is %d)", ctx.depthMax)); + fileWriteString(out, T_NEWLINE); + return; } - printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excValue, indent, margin); + if (ctx.indent == 0) { + ctx = ctx.withIndent(2); + } + + ctx = ctx.withDepthIncrease(); + + printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excValue, ctx); PBaseExceptionGroup exceptionGroup = (PBaseExceptionGroup) excValue; int counter = 1; for (Object exception : exceptionGroup.getExceptions()) { if (counter == 1) { - fileWriteIndentedString(out, "+-", indent); + fileWriteIndentedString(out, "+-", ctx.indent); } else { - fileWriteString(out, getIndent(indent + 2)); + fileWriteString(out, getIndent(ctx.indent + 2)); } - if (counter <= INT_MAX_GROUP_WIDTH) { + if (counter <= ctx.widthMax) { fileWriteString(out, String.format("+---------------- %d ----------------", counter)); fileWriteString(out, T_NEWLINE); - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, exception, seen, indent + 2, margin); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, exception, seen, ctx.withIndentIncrease()); } else { fileWriteString(out, String.format("+---------------- ... ----------------", counter)); fileWriteString(out, T_NEWLINE); - fileWriteIndentedString(out, margin, indent + 2); - int exceptionsRemaining = exceptionGroup.getExceptions().length - INT_MAX_GROUP_WIDTH; + fileWriteIndentedString(out, ctx.margin, ctx.indent + 2); + int exceptionsRemaining = exceptionGroup.getExceptions().length - ctx.widthMax; fileWriteString(out, String.format("and %d more exception%s", exceptionsRemaining, exceptionsRemaining > 1 ? "s" : "")); fileWriteString(out, T_NEWLINE); break; } counter++; } - fileWriteString(out, getIndent(indent + 2)); - fileWriteString(out, "+------------------------------------"); + fileWriteString(out, getIndent(ctx.indent + 2)); + fileWriteString(out, "+------------------------------------"); //TODO: this will be printed one-after-another when max depth is reached (should be skipped) fileWriteString(out, T_NEWLINE); } From 1d7b1c88c8dcf4fce9109992346cdd8f833207e0 Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Mon, 19 Jan 2026 15:33:56 +0100 Subject: [PATCH 05/13] exception group pretty printing: context is now mutable; minor improvements --- .../builtins/modules/SysModuleBuiltins.java | 116 ++++++++++-------- 1 file changed, 63 insertions(+), 53 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index a4f95015ec..7202608ca3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -1348,9 +1348,11 @@ abstract static class ExceptHookNode extends PythonBuiltinNode { static final TruffleString T_ATTR_LINENO = tsInternedLiteral("lineno"); static final TruffleString T_ATTR_OFFSET = tsInternedLiteral("offset"); static final TruffleString T_ATTR_TEXT = tsInternedLiteral("text"); + static final TruffleString T_EG_MARGIN = tsInternedLiteral("| "); protected static final int INT_MAX_GROUP_WIDTH = 5; protected static final int INT_MAX_GROUP_DEPTH = 3; + protected static final int INT_INDENT_SIZE = 2; @ValueType static final class SyntaxErrData { @@ -1372,50 +1374,38 @@ static final class SyntaxErrData { } static class ExceptionPrintContext { - public final int indent; - public final TruffleString margin; - public final int depthMax; - public final int depthCurrent; - public final int widthMax; - - ExceptionPrintContext(int indent, TruffleString margin) { - this.indent = indent; - this.margin = margin; + public int depthMax; + public int depthCurrent; + public int widthMax; + public boolean needsToEnd; + + ExceptionPrintContext() { this.depthMax = INT_MAX_GROUP_DEPTH; this.depthCurrent = 0; this.widthMax = INT_MAX_GROUP_WIDTH; + this.needsToEnd = false; } - ExceptionPrintContext(int indent, TruffleString margin, int depthMax, int depthCurrent, int widthMax) { - this.indent = indent; - this.margin = margin; - this.depthMax = depthMax; - this.depthCurrent = depthCurrent; - this.widthMax = widthMax; - } - - ExceptionPrintContext(ExceptionPrintContext ctx) { - this.indent = ctx.indent; - this.margin = ctx.margin; - this.depthMax = ctx.depthMax; - this.depthCurrent = ctx.depthCurrent; - this.widthMax = ctx.widthMax; - } - - public ExceptionPrintContext withIndentIncrease() { - return new ExceptionPrintContext(indent + 2, margin, depthMax, depthCurrent, widthMax); + public TruffleString getMargin() { + if (this.depthCurrent > 0) { + return T_EG_MARGIN; + } else { + return tsLiteral(""); + } } - public ExceptionPrintContext withIndent(int indent) { - return new ExceptionPrintContext(indent, margin, depthMax, depthCurrent, widthMax); + public int getIndent() { + return this.depthCurrent * INT_INDENT_SIZE; } - public ExceptionPrintContext withMargin(TruffleString margin) { - return new ExceptionPrintContext(indent, margin, depthMax, depthCurrent, widthMax); + public void increaseDepth() { + this.depthCurrent++; } - public ExceptionPrintContext withDepthIncrease() { - return new ExceptionPrintContext(indent, margin, depthMax, depthCurrent + 1, widthMax); + public void decreaseDepth() { + if (depthCurrent > 0) { + this.depthCurrent--; + } } } @@ -1531,7 +1521,7 @@ private static void printErrorText(Object out, SyntaxErrData syntaxErrData) { @TruffleBoundary void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, PythonModule sys, Object out, Object value, Set seen) { - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, new ExceptionPrintContext(0, tsLiteral(""))); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, new ExceptionPrintContext()); } @TruffleBoundary @@ -1544,6 +1534,7 @@ void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTraceback Object cause = ExceptionNodes.GetCauseNode.executeUncached(value); Object context = ExceptionNodes.GetContextNode.executeUncached(value); + boolean needsToEnd = ctx.needsToEnd; if (cause != PNone.NONE) { if (notSeen(seen, cause)) { printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, cause, seen, ctx); @@ -1555,10 +1546,11 @@ void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTraceback fileWriteString(out, T_CONTEXT_MESSAGE); } } + ctx.needsToEnd = needsToEnd; } } if (value instanceof PBaseExceptionGroup) { - printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, ctx.withMargin(tsLiteral("| "))); + printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, ctx); } else { printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, ctx); @@ -1586,12 +1578,11 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac PyTraceBackPrint.fileWriteString(out, "TypeError: print_exception(): Exception expected for value, "); fileWriteString(out, getTypeName(type)); PyTraceBackPrint.fileWriteString(out, " found\n"); - return; } final Object tb = getExceptionTraceback(value); if (tb instanceof PTraceback) { - PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, tb, (value instanceof PBaseExceptionGroup), ctx.indent, ctx.margin); + PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, tb, (value instanceof PBaseExceptionGroup), ctx.getIndent(), ctx.getMargin()); } if (objectHasAttr(value, T_ATTR_PRINT_FILE_AND_LINE)) { @@ -1610,7 +1601,7 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac } } - fileWriteIndentedString(out, ctx.margin, ctx.indent); + fileWriteIndentedString(out, ctx.getMargin(), ctx.getIndent()); TruffleString className; try { @@ -1661,50 +1652,69 @@ protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTra PyTraceBackPrint.fileWriteString(out, "TypeError: print_exception_group(): Exception group expected for value, "); fileWriteString(out, getTypeName(type)); PyTraceBackPrint.fileWriteString(out, " found\n"); - return; } - if (ctx.depthCurrent >= ctx.depthMax) { - fileWriteIndentedString(out, ctx.margin, ctx.indent); + if (ctx.depthCurrent > ctx.depthMax) { + fileWriteIndentedString(out, ctx.getMargin(), ctx.getIndent()); fileWriteString(out, String.format("... (max_group_depth is %d)", ctx.depthMax)); fileWriteString(out, T_NEWLINE); return; } - if (ctx.indent == 0) { - ctx = ctx.withIndent(2); - } + ctx.needsToEnd = false; - ctx = ctx.withDepthIncrease(); + if (ctx.depthCurrent == 0) { + ctx.increaseDepth(); + } printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excValue, ctx); PBaseExceptionGroup exceptionGroup = (PBaseExceptionGroup) excValue; int counter = 1; + boolean lastException = false; for (Object exception : exceptionGroup.getExceptions()) { + if (counter == exceptionGroup.getExceptions().length) { + lastException = true; + ctx.needsToEnd = true; + } if (counter == 1) { - fileWriteIndentedString(out, "+-", ctx.indent); + fileWriteIndentedString(out, "+".concat("-".repeat(INT_INDENT_SIZE - 1)), ctx.getIndent()); } else { - fileWriteString(out, getIndent(ctx.indent + 2)); + fileWriteString(out, getIndent(ctx.getIndent() + INT_INDENT_SIZE)); } if (counter <= ctx.widthMax) { fileWriteString(out, String.format("+---------------- %d ----------------", counter)); fileWriteString(out, T_NEWLINE); - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, exception, seen, ctx.withIndentIncrease()); + ctx.increaseDepth(); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, exception, seen, ctx); + ctx.decreaseDepth(); } else { - fileWriteString(out, String.format("+---------------- ... ----------------", counter)); + fileWriteString(out, "+---------------- ... ----------------"); fileWriteString(out, T_NEWLINE); - fileWriteIndentedString(out, ctx.margin, ctx.indent + 2); + fileWriteIndentedString(out, ctx.getMargin(), ctx.getIndent() + INT_INDENT_SIZE); int exceptionsRemaining = exceptionGroup.getExceptions().length - ctx.widthMax; fileWriteString(out, String.format("and %d more exception%s", exceptionsRemaining, exceptionsRemaining > 1 ? "s" : "")); fileWriteString(out, T_NEWLINE); + + // this makes this exception in this exception group essentially last + lastException = true; + ctx.needsToEnd = true; break; } counter++; } - fileWriteString(out, getIndent(ctx.indent + 2)); - fileWriteString(out, "+------------------------------------"); //TODO: this will be printed one-after-another when max depth is reached (should be skipped) - fileWriteString(out, T_NEWLINE); + + if (lastException && ctx.needsToEnd) { + fileWriteString(out, getIndent(ctx.getIndent() + INT_INDENT_SIZE)); + fileWriteString(out, "+------------------------------------"); + fileWriteString(out, T_NEWLINE); + // let only the innermost exception print the end of an exception group cascade + ctx.needsToEnd = false; + } + + if (ctx.depthCurrent == 1) { + ctx.decreaseDepth(); + } } @TruffleBoundary(allowInlining = true) From 4303e65e5cda0805c94b25ccaecb2daf5f4453a1 Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Mon, 19 Jan 2026 15:34:23 +0100 Subject: [PATCH 06/13] fix constants --- .../graal/python/builtins/modules/SysModuleBuiltins.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index 7202608ca3..9e46f91091 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -1350,8 +1350,8 @@ abstract static class ExceptHookNode extends PythonBuiltinNode { static final TruffleString T_ATTR_TEXT = tsInternedLiteral("text"); static final TruffleString T_EG_MARGIN = tsInternedLiteral("| "); - protected static final int INT_MAX_GROUP_WIDTH = 5; - protected static final int INT_MAX_GROUP_DEPTH = 3; + protected static final int INT_MAX_GROUP_WIDTH = 15; + protected static final int INT_MAX_GROUP_DEPTH = 10; protected static final int INT_INDENT_SIZE = 2; @ValueType From af0ce92a1bc24adbc9b7e72d91c12be00785ae3c Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Mon, 19 Jan 2026 15:42:35 +0100 Subject: [PATCH 07/13] forgotten debug print rm --- .../src/com/oracle/graal/python/lib/PyTraceBackPrint.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java index 8bea087a8b..b400be469f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java @@ -405,9 +405,9 @@ public static void print(Node inliningTarget, TracebackBuiltins.GetTracebackFram } } if (isExceptionGroup) { - printIndentedHeader(out, "Exception Group Traceback (most recent call last) (Java traceback):", indent, "+ "); + printIndentedHeader(out, "Exception Group Traceback (most recent call last):", indent, "+ "); } else { - printIndentedHeader(out, "Traceback (most recent call last) (Java traceback):", indent, ""); + printIndentedHeader(out, "Traceback (most recent call last):", indent, ""); } printInternal(inliningTarget, getTbFrameNode, materializeStNode, out, tb, limit, indent, margin); } else { From 24ae5e974ea549da135c8ec436830d81d379cfed Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Mon, 19 Jan 2026 15:54:15 +0100 Subject: [PATCH 08/13] style --- .../python/builtins/modules/SysModuleBuiltins.java | 11 ++++------- .../python/lib/PyExceptionGroupInstanceCheckNode.java | 3 +-- .../com/oracle/graal/python/lib/PyTraceBackPrint.java | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index 9e46f91091..a06b02590e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -133,7 +133,6 @@ import static com.oracle.graal.python.nodes.StringLiterals.T_VALUE_UNKNOWN; import static com.oracle.graal.python.nodes.StringLiterals.T_VERSION; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; -import static com.oracle.graal.python.util.PythonUtils.toIntError; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static com.oracle.graal.python.util.PythonUtils.tsInternedLiteral; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; @@ -176,7 +175,6 @@ import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; import com.oracle.graal.python.builtins.objects.exception.GetEscapedExceptionNode; -import com.oracle.graal.python.builtins.objects.exception.PBaseException; import com.oracle.graal.python.builtins.objects.exception.PBaseExceptionGroup; import com.oracle.graal.python.builtins.objects.frame.PFrame; import com.oracle.graal.python.builtins.objects.function.PArguments; @@ -1520,7 +1518,7 @@ private static void printErrorText(Object out, SyntaxErrData syntaxErrData) { @TruffleBoundary void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object value, Set seen) { + PythonModule sys, Object out, Object value, Set seen) { printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, new ExceptionPrintContext()); } @@ -1551,8 +1549,7 @@ void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTraceback } if (value instanceof PBaseExceptionGroup) { printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, ctx); - } - else { + } else { printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, ctx); } } @@ -1645,7 +1642,7 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac } protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object excValue, Set seen, ExceptionPrintContext ctx) { + PythonModule sys, Object out, Object excValue, Set seen, ExceptionPrintContext ctx) { Object value = excValue; final Object type = getObjectClass(value); if (!PyExceptionGroupInstanceCheckNode.executeUncached(value)) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyExceptionGroupInstanceCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyExceptionGroupInstanceCheckNode.java index 77544e1459..b499f18115 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyExceptionGroupInstanceCheckNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyExceptionGroupInstanceCheckNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -42,7 +42,6 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; -import com.oracle.graal.python.builtins.objects.exception.PBaseException; import com.oracle.graal.python.builtins.objects.exception.PBaseExceptionGroup; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.object.GetClassNode; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java index b400be469f..e83cd8482b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -338,7 +338,7 @@ private static void printIndentedHeader(Object out, String header, int indent, S private static void printInternal(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, Object out, PTraceback traceback, long limit, - int indent, TruffleString margin) { + int indent, TruffleString margin) { int depth = 0; TruffleString lastFile = null; int lastLine = -1; From 4bb66c2b2f3a6e4860c9566cf9d180dcca369a25 Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Tue, 20 Jan 2026 10:26:32 +0100 Subject: [PATCH 09/13] accidentally removed a return --- .../oracle/graal/python/builtins/modules/SysModuleBuiltins.java | 1 + 1 file changed, 1 insertion(+) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index a06b02590e..8709e61724 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -1575,6 +1575,7 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac PyTraceBackPrint.fileWriteString(out, "TypeError: print_exception(): Exception expected for value, "); fileWriteString(out, getTypeName(type)); PyTraceBackPrint.fileWriteString(out, " found\n"); + return; } final Object tb = getExceptionTraceback(value); From 088a77d7bd4645ffa889acd6f53d4751f805328a Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Wed, 21 Jan 2026 17:13:12 +0100 Subject: [PATCH 10/13] begin adding tests --- .../src/tests/test_except_star.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py b/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py new file mode 100644 index 0000000000..2eb821bcc2 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py @@ -0,0 +1,49 @@ +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import unittest +import test.support + + +class ExceptStarPrintTest(unittest.TestCase): + def test_01(self): + with test.support.captured_output("stderr") as stderr: + eg = ExceptionGroup("eg", [ValueError(1), TypeError(2)]) + self.assertEqual() + assert stderr.getvalue() == "xffgh" \ No newline at end of file From bb5ce9ff6f745cd44911f4176e1c73e5cb0259fe Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Thu, 22 Jan 2026 10:43:37 +0100 Subject: [PATCH 11/13] except star tests added --- .../src/tests/test_except_star.py | 344 +++++++++++++++++- 1 file changed, 338 insertions(+), 6 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py b/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py index 2eb821bcc2..f8b73245d7 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py @@ -38,12 +38,344 @@ # SOFTWARE. import unittest -import test.support +import subprocess +import sys +import textwrap class ExceptStarPrintTest(unittest.TestCase): - def test_01(self): - with test.support.captured_output("stderr") as stderr: - eg = ExceptionGroup("eg", [ValueError(1), TypeError(2)]) - self.assertEqual() - assert stderr.getvalue() == "xffgh" \ No newline at end of file + def test_01_eg_simple(self): + script = textwrap.dedent(""" + raise ExceptionGroup("eg", [ + ValueError(1), + TypeError(2) + ]) + """) + p = subprocess.run([sys.executable, "-c", script], capture_output=True) + expected = [b' + Exception Group Traceback (most recent call last):', + b' | File "", line 2, in ', + b' | ExceptionGroup: eg (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ValueError: 1', + b' +---------------- 2 ----------------', + b' | TypeError: 2', + b' +------------------------------------'] + self.assertEqual(p.stderr.splitlines(), expected) + + def test_02_eg_nested(self): + script = textwrap.dedent(""" + raise ExceptionGroup("EG", [ + TypeError("1"), + ExceptionGroup("2", [ + IndexError("2.1"), + ValueError("2.2"), + ExceptionGroup("2.3", [ + IndexError("2.3.1"), + ExceptionGroup("2.3.2", [ + TypeError("2.3.2.1"), + IndexError("2.3.2.2"), + ExceptionGroup("2.3.2.3", [ + ImportError("2.3.2.3.1"), + ValueError("2.3.2.3.2") + ]) + ]), + IndexError("2.3.3"), + IndexError("2.3.4"), + IndexError("2.3.5"), + IndexError("2.3.6"), + ]), + ExceptionGroup("2.4", [ + IndexError("2.4.1"), + ExceptionGroup("2.4.2", [ + TypeError("2.4.2.1"), + IndexError("2.4.2.2"), + ExceptionGroup("2.4.3", [ + ImportError("2.4.3.1"), + ValueError("2.4.3.2") + ]) + ]) + ]) + ]), + ValueError("3"), + ValueError("4"), + ValueError("5"), + ]) + """) + p = subprocess.run([sys.executable, "-c", script], capture_output=True) + expected = [b' + Exception Group Traceback (most recent call last):', + b' | File "", line 2, in ', + b' | ExceptionGroup: EG (5 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | TypeError: 1', + b' +---------------- 2 ----------------', + b' | ExceptionGroup: 2 (4 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | IndexError: 2.1', + b' +---------------- 2 ----------------', + b' | ValueError: 2.2', + b' +---------------- 3 ----------------', + b' | ExceptionGroup: 2.3 (6 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | IndexError: 2.3.1', + b' +---------------- 2 ----------------', + b' | ExceptionGroup: 2.3.2 (3 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | TypeError: 2.3.2.1', + b' +---------------- 2 ----------------', + b' | IndexError: 2.3.2.2', + b' +---------------- 3 ----------------', + b' | ExceptionGroup: 2.3.2.3 (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ImportError: 2.3.2.3.1', + b' +---------------- 2 ----------------', + b' | ValueError: 2.3.2.3.2', + b' +------------------------------------', + b' +---------------- 3 ----------------', + b' | IndexError: 2.3.3', + b' +---------------- 4 ----------------', + b' | IndexError: 2.3.4', + b' +---------------- 5 ----------------', + b' | IndexError: 2.3.5', + b' +---------------- 6 ----------------', + b' | IndexError: 2.3.6', + b' +------------------------------------', + b' +---------------- 4 ----------------', + b' | ExceptionGroup: 2.4 (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | IndexError: 2.4.1', + b' +---------------- 2 ----------------', + b' | ExceptionGroup: 2.4.2 (3 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | TypeError: 2.4.2.1', + b' +---------------- 2 ----------------', + b' | IndexError: 2.4.2.2', + b' +---------------- 3 ----------------', + b' | ExceptionGroup: 2.4.3 (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ImportError: 2.4.3.1', + b' +---------------- 2 ----------------', + b' | ValueError: 2.4.3.2', + b' +------------------------------------', + b' +---------------- 3 ----------------', + b' | ValueError: 3', + b' +---------------- 4 ----------------', + b' | ValueError: 4', + b' +---------------- 5 ----------------', + b' | ValueError: 5', + b' +------------------------------------'] + self.maxDiff = None + self.assertEqual(p.stderr.splitlines(), expected) + + def test_03_eg_nested_truncated(self): + script = textwrap.dedent(""" + raise ExceptionGroup("EG", [ + TypeError("1"), + ExceptionGroup("2", [ + IndexError("2.1"), + ValueError("2.2"), + ExceptionGroup("2.3", [ + IndexError("2.3.1"), + ExceptionGroup("2.3.2", [ + TypeError("2.3.2.1"), + IndexError("2.3.2.2"), + ExceptionGroup("2.3.2.3", [ + ExceptionGroup("2.3.2.3.1", [ + ExceptionGroup("2.3.2.3.1.1", [ + ExceptionGroup("2.3.2.3.1.1.1", [ + ExceptionGroup("2.3.2.3.1.1.1.1", [ + ExceptionGroup("2.3.2.3.1.1.1.1.1", [ + ExceptionGroup("2.3.2.3.1.1.1.1.1.1", [ + ExceptionGroup("2.3.2.3.1.1.1.1.1.1.1", [ + ExceptionGroup("2.3.2.3.1.1.1.1.1.1.1.1", [ + ExceptionGroup("2.3.2.3.1.1.1.1.1.1.1.1.1", [ + IndexError(1) + ]), + ]), + ]), + ]), + ImportError("2.3.2.3.1.1.1.1.1.2"), + IndexError("2.3.2.3.1.1.1.1.1.3"), + ]), + ]), + ]), + ]), + ]), + ImportError("2.3.2.3.1"), + ValueError("2.3.2.3.2") + ]) + ]), + IndexError("2.3.3"), + IndexError("2.3.4"), + IndexError("2.3.5"), + IndexError("2.3.6"), + IndexError("2.3.7"), + IndexError("2.3.8"), + IndexError("2.3.9"), + IndexError("2.3.10"), + IndexError("2.3.11"), + IndexError("2.3.12"), + IndexError("2.3.13"), + IndexError("2.3.14"), + IndexError("2.3.15"), + IndexError("2.3.16"), + IndexError("2.3.17"), + IndexError("2.3.18"), + ]), + ExceptionGroup("2.4", [ + IndexError("2.4.1"), + ExceptionGroup("2.4.2", [ + TypeError("2.4.2.1"), + IndexError("2.4.2.2"), + ExceptionGroup("2.4.3", [ + ImportError("2.4.3.1"), + ValueError("2.4.3.2") + ]) + ]) + ]) + ]), + ValueError("3"), + ValueError("4"), + ValueError("5"), + ValueError("6"), + ValueError("7"), + ValueError("8"), + ValueError("9"), + ValueError("10"), + ValueError("11"), + ValueError("12"), + ValueError("13"), + ValueError("14"), + ValueError("15"), + ValueError("16"), + ValueError("17"), + ValueError("18"), + ValueError("19"), + ValueError("20"), + ValueError("21"), + ValueError("22"), + ValueError("23"), + ]) + """) + p = subprocess.run([sys.executable, "-c", script], capture_output=True) + expected = [b' + Exception Group Traceback (most recent call last):', + b' | File "", line 2, in ', + b' | ExceptionGroup: EG (23 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | TypeError: 1', + b' +---------------- 2 ----------------', + b' | ExceptionGroup: 2 (4 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | IndexError: 2.1', + b' +---------------- 2 ----------------', + b' | ValueError: 2.2', + b' +---------------- 3 ----------------', + b' | ExceptionGroup: 2.3 (18 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | IndexError: 2.3.1', + b' +---------------- 2 ----------------', + b' | ExceptionGroup: 2.3.2 (3 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | TypeError: 2.3.2.1', + b' +---------------- 2 ----------------', + b' | IndexError: 2.3.2.2', + b' +---------------- 3 ----------------', + b' | ExceptionGroup: 2.3.2.3 (3 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ExceptionGroup: 2.3.2.3.1 (1 sub-exception)', + b' +-+---------------- 1 ----------------', + b' | ExceptionGroup: 2.3.2.3.1.1 (1 sub-exception)', + b' +-+---------------- 1 ----------------', + b' | ExceptionGroup: 2.3.2.3.1.1.1 (1 sub-exception)', + b' +-+---------------- 1 ----------------', + b' | ExceptionGroup: 2.3.2.3.1.1.1.1 (1 sub-exception)', + b' +-+---------------- 1 ----------------', + b' | ExceptionGroup: 2.3.2.3.1.1.1.1.1 (3 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ... (max_group_depth is 10)', + b' +---------------- 2 ----------------', + b' | ImportError: 2.3.2.3.1.1.1.1.1.2', + b' +---------------- 3 ----------------', + b' | IndexError: 2.3.2.3.1.1.1.1.1.3', + b' +------------------------------------', + b' +---------------- 2 ----------------', + b' | ImportError: 2.3.2.3.1', + b' +---------------- 3 ----------------', + b' | ValueError: 2.3.2.3.2', + b' +------------------------------------', + b' +---------------- 3 ----------------', + b' | IndexError: 2.3.3', + b' +---------------- 4 ----------------', + b' | IndexError: 2.3.4', + b' +---------------- 5 ----------------', + b' | IndexError: 2.3.5', + b' +---------------- 6 ----------------', + b' | IndexError: 2.3.6', + b' +---------------- 7 ----------------', + b' | IndexError: 2.3.7', + b' +---------------- 8 ----------------', + b' | IndexError: 2.3.8', + b' +---------------- 9 ----------------', + b' | IndexError: 2.3.9', + b' +---------------- 10 ----------------', + b' | IndexError: 2.3.10', + b' +---------------- 11 ----------------', + b' | IndexError: 2.3.11', + b' +---------------- 12 ----------------', + b' | IndexError: 2.3.12', + b' +---------------- 13 ----------------', + b' | IndexError: 2.3.13', + b' +---------------- 14 ----------------', + b' | IndexError: 2.3.14', + b' +---------------- 15 ----------------', + b' | IndexError: 2.3.15', + b' +---------------- ... ----------------', + b' | and 3 more exceptions', + b' +------------------------------------', + b' +---------------- 4 ----------------', + b' | ExceptionGroup: 2.4 (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | IndexError: 2.4.1', + b' +---------------- 2 ----------------', + b' | ExceptionGroup: 2.4.2 (3 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | TypeError: 2.4.2.1', + b' +---------------- 2 ----------------', + b' | IndexError: 2.4.2.2', + b' +---------------- 3 ----------------', + b' | ExceptionGroup: 2.4.3 (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ImportError: 2.4.3.1', + b' +---------------- 2 ----------------', + b' | ValueError: 2.4.3.2', + b' +------------------------------------', + b' +---------------- 3 ----------------', + b' | ValueError: 3', + b' +---------------- 4 ----------------', + b' | ValueError: 4', + b' +---------------- 5 ----------------', + b' | ValueError: 5', + b' +---------------- 6 ----------------', + b' | ValueError: 6', + b' +---------------- 7 ----------------', + b' | ValueError: 7', + b' +---------------- 8 ----------------', + b' | ValueError: 8', + b' +---------------- 9 ----------------', + b' | ValueError: 9', + b' +---------------- 10 ----------------', + b' | ValueError: 10', + b' +---------------- 11 ----------------', + b' | ValueError: 11', + b' +---------------- 12 ----------------', + b' | ValueError: 12', + b' +---------------- 13 ----------------', + b' | ValueError: 13', + b' +---------------- 14 ----------------', + b' | ValueError: 14', + b' +---------------- 15 ----------------', + b' | ValueError: 15', + b' +---------------- ... ----------------', + b' | and 8 more exceptions', + b' +------------------------------------'] + self.maxDiff = None + self.assertEqual(p.stderr.splitlines(), expected) From f4ce4ba307812b43933e78c3f587ebaa155e94ae Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Fri, 23 Jan 2026 13:14:21 +0100 Subject: [PATCH 12/13] new tests and changes --- .../src/tests/test_except_star.py | 179 ++++++++++++++++++ .../builtins/modules/SysModuleBuiltins.java | 8 +- .../graal/python/lib/PyTraceBackPrint.java | 14 +- 3 files changed, 196 insertions(+), 5 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py b/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py index f8b73245d7..3351497368 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py @@ -42,8 +42,11 @@ import sys import textwrap +from tests.util import skipIfBytecodeDSL + class ExceptStarPrintTest(unittest.TestCase): + @skipIfBytecodeDSL("a") def test_01_eg_simple(self): script = textwrap.dedent(""" raise ExceptionGroup("eg", [ @@ -62,6 +65,7 @@ def test_01_eg_simple(self): b' +------------------------------------'] self.assertEqual(p.stderr.splitlines(), expected) + @skipIfBytecodeDSL("a") def test_02_eg_nested(self): script = textwrap.dedent(""" raise ExceptionGroup("EG", [ @@ -166,6 +170,7 @@ def test_02_eg_nested(self): self.maxDiff = None self.assertEqual(p.stderr.splitlines(), expected) + @skipIfBytecodeDSL("a") def test_03_eg_nested_truncated(self): script = textwrap.dedent(""" raise ExceptionGroup("EG", [ @@ -379,3 +384,177 @@ def test_03_eg_nested_truncated(self): b' +------------------------------------'] self.maxDiff = None self.assertEqual(p.stderr.splitlines(), expected) + + @skipIfBytecodeDSL("a") + def test_04_eg_cause(self): + script = textwrap.dedent(""" + EG = ExceptionGroup + try: + raise EG("eg1", [ValueError(1), TypeError(2)]) + except Exception as e: + raise EG("eg2", [ValueError(3), TypeError(4)]) from e + """) + + p = subprocess.run([sys.executable, "-c", script], capture_output=True) + + expected = [b' + Exception Group Traceback (most recent call last):', + b' | File "", line 4, in ', + b' | ExceptionGroup: eg1 (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ValueError: 1', + b' +---------------- 2 ----------------', + b' | TypeError: 2', + b' +------------------------------------', + b'', + b'The above exception was the direct cause of the following exception:', + b'', + b' + Exception Group Traceback (most recent call last):', + b' | File "", line 6, in ', + b' | ExceptionGroup: eg2 (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ValueError: 3', + b' +---------------- 2 ----------------', + b' | TypeError: 4', + b' +------------------------------------'] + + self.maxDiff = None + self.assertEqual(p.stderr.splitlines(), expected) + + @skipIfBytecodeDSL("a") + def test_05_eg_context_with_context(self): + script = textwrap.dedent(""" + EG = ExceptionGroup + try: + try: + raise EG("eg1", [ValueError(1), TypeError(2)]) + except EG: + raise EG("eg2", [ValueError(3), TypeError(4)]) + except EG: + raise ImportError(5) + """) + + p = subprocess.run([sys.executable, "-c", script], capture_output=True) + + expected = [b' + Exception Group Traceback (most recent call last):', + b' | File "", line 5, in ', + b' | ExceptionGroup: eg1 (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ValueError: 1', + b' +---------------- 2 ----------------', + b' | TypeError: 2', + b' +------------------------------------', + b'', + b'During handling of the above exception, another exception occurred:', + b'', + b' + Exception Group Traceback (most recent call last):', + b' | File "", line 7, in ', + b' | ExceptionGroup: eg2 (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ValueError: 3', + b' +---------------- 2 ----------------', + b' | TypeError: 4', + b' +------------------------------------', + b'', + b'During handling of the above exception, another exception occurred:', + b'', + b'Traceback (most recent call last):', + b' File "", line 9, in ', + b'ImportError: 5',] + + self.maxDiff = None + self.assertEqual(p.stderr.splitlines(), expected) + + @skipIfBytecodeDSL("a") + def test_06_eg_nested_with_context(self): + script = textwrap.dedent(""" + EG = ExceptionGroup + VE = ValueError + TE = TypeError + try: + try: + raise EG("nested", [TE(2), TE(3)]) + except Exception as e: + exc = e + raise EG("eg", [VE(1), exc, VE(4)]) + except EG: + raise EG("top", [VE(5)]) + """) + + p = subprocess.run([sys.executable, "-c", script], capture_output=True) + + expected = [b' + Exception Group Traceback (most recent call last):', + b' | File "", line 10, in ', + b' | ExceptionGroup: eg (3 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | ValueError: 1', + b' +---------------- 2 ----------------', + b' | Exception Group Traceback (most recent call last):', + b' | File "", line 7, in ', + b' | ExceptionGroup: nested (2 sub-exceptions)', + b' +-+---------------- 1 ----------------', + b' | TypeError: 2', + b' +---------------- 2 ----------------', + b' | TypeError: 3', + b' +------------------------------------', + b' +---------------- 3 ----------------', + b' | ValueError: 4', + b' +------------------------------------', + b'', + b'During handling of the above exception, another exception occurred:', + b'', + b' + Exception Group Traceback (most recent call last):', + b' | File "", line 12, in ', + b' | ExceptionGroup: top (1 sub-exception)', + b' +-+---------------- 1 ----------------', + b' | ValueError: 5', + b' +------------------------------------',] + + self.maxDiff = None + self.assertEqual(p.stderr.splitlines(), expected) + + def test_07_eg_with_notes(self): + script = textwrap.dedent(""" + try: + excs = [] + for msg in ['bad value', 'terrible value']: + try: + raise ValueError(msg) + except ValueError as e: + e.add_note(f'the {msg}') + excs.append(e) + raise ExceptionGroup("nested", excs) + except ExceptionGroup as e: + e.add_note(('>> Multi line note\\n' + '>> Because I am such\\n' + '>> an important exception.\\n' + '>> empty lines work too\\n' + '\\n' + '(that was an empty line)')) + raise + """) + + p = subprocess.run([sys.executable, "-c", script], capture_output=True) + + expected = [b' + Exception Group Traceback (most recent call last):', + b' | File "", line 10, in ', + b' | ExceptionGroup: nested (2 sub-exceptions)', + b' | >> Multi line note', + b' | >> Because I am such', + b' | >> an important exception.', + b' | >> empty lines work too', + b' | ', + b' | (that was an empty line)', + b' +-+---------------- 1 ----------------', + b' | Traceback (most recent call last):', + b' | File "", line 6, in ', + b' | ValueError: bad value', + b' | the bad value', + b' +---------------- 2 ----------------', + b' | Traceback (most recent call last):', + b' | File "", line 6, in ', + b' | ValueError: terrible value', + b' | the terrible value', + b' +------------------------------------',] + + self.maxDiff = None + self.assertEqual(p.stderr.splitlines(), expected) \ No newline at end of file diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index 8709e61724..3f5cd3abb9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -1253,7 +1253,7 @@ private void writeUnraisableExc(Node inliningTarget, TracebackBuiltins.GetTraceb } if (excTb != PNone.NONE) { - PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excTb, false, 0, null); + PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excTb, false, false, 0, null); } if (excType == PNone.NONE) { @@ -1580,7 +1580,11 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac final Object tb = getExceptionTraceback(value); if (tb instanceof PTraceback) { - PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, tb, (value instanceof PBaseExceptionGroup), ctx.getIndent(), ctx.getMargin()); + if (value instanceof PBaseExceptionGroup pbeg) { + PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, tb, true, ctx.depthCurrent > 1, ctx.getIndent(), ctx.getMargin()); + } else { + PyTraceBackPrint.print(inliningTarget, getTbFrameNode, materializeStNode, sys, out, tb, false, ctx.depthCurrent == 0, ctx.getIndent(), ctx.getMargin()); + } } if (objectHasAttr(value, T_ATTR_PRINT_FILE_AND_LINE)) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java index e83cd8482b..43a18b7723 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java @@ -382,7 +382,7 @@ private static void printInternal(Node inliningTarget, TracebackBuiltins.GetTrac } public static void print(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, PythonModule sys, - Object out, Object tbObj, boolean isExceptionGroup, int indent, TruffleString margin) { + Object out, Object tbObj, boolean isExceptionGroup, boolean printMarginControl, int indent, TruffleString margin) { // Although we should be behind TB, we need cached nodes, because they may do stack walking // and for that they must be connected to the currently executing root. In practice, it's // not strictly necessary, because they will never request the current frame, but in order @@ -405,9 +405,17 @@ public static void print(Node inliningTarget, TracebackBuiltins.GetTracebackFram } } if (isExceptionGroup) { - printIndentedHeader(out, "Exception Group Traceback (most recent call last):", indent, "+ "); + if (printMarginControl) { + printIndentedHeader(out, "Exception Group Traceback (most recent call last):", indent, margin.toString()); + } else { + printIndentedHeader(out, "Exception Group Traceback (most recent call last):", indent, "+ "); + } } else { - printIndentedHeader(out, "Traceback (most recent call last):", indent, ""); + if (printMarginControl) { + printIndentedHeader(out, "Traceback (most recent call last):", indent, ""); + } else { + printIndentedHeader(out, "Traceback (most recent call last):", indent, margin.toString()); + } } printInternal(inliningTarget, getTbFrameNode, materializeStNode, out, tb, limit, indent, margin); } else { From 64e9d72a65345a2b95b2fea96bf7bd0486c0dfc5 Mon Sep 17 00:00:00 2001 From: Michal Medvecky Date: Mon, 26 Jan 2026 13:58:14 +0100 Subject: [PATCH 13/13] add support for exception notes printing + new tests --- .../src/tests/test_except_star.py | 58 ++++++++++++++-- .../builtins/modules/SysModuleBuiltins.java | 68 +++++++++++++------ .../graal/python/lib/PyTraceBackPrint.java | 2 +- 3 files changed, 102 insertions(+), 26 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py b/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py index 3351497368..66457d4795 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_except_star.py @@ -46,7 +46,6 @@ class ExceptStarPrintTest(unittest.TestCase): - @skipIfBytecodeDSL("a") def test_01_eg_simple(self): script = textwrap.dedent(""" raise ExceptionGroup("eg", [ @@ -65,7 +64,6 @@ def test_01_eg_simple(self): b' +------------------------------------'] self.assertEqual(p.stderr.splitlines(), expected) - @skipIfBytecodeDSL("a") def test_02_eg_nested(self): script = textwrap.dedent(""" raise ExceptionGroup("EG", [ @@ -170,7 +168,6 @@ def test_02_eg_nested(self): self.maxDiff = None self.assertEqual(p.stderr.splitlines(), expected) - @skipIfBytecodeDSL("a") def test_03_eg_nested_truncated(self): script = textwrap.dedent(""" raise ExceptionGroup("EG", [ @@ -385,7 +382,6 @@ def test_03_eg_nested_truncated(self): self.maxDiff = None self.assertEqual(p.stderr.splitlines(), expected) - @skipIfBytecodeDSL("a") def test_04_eg_cause(self): script = textwrap.dedent(""" EG = ExceptionGroup @@ -420,7 +416,6 @@ def test_04_eg_cause(self): self.maxDiff = None self.assertEqual(p.stderr.splitlines(), expected) - @skipIfBytecodeDSL("a") def test_05_eg_context_with_context(self): script = textwrap.dedent(""" EG = ExceptionGroup @@ -464,7 +459,6 @@ def test_05_eg_context_with_context(self): self.maxDiff = None self.assertEqual(p.stderr.splitlines(), expected) - @skipIfBytecodeDSL("a") def test_06_eg_nested_with_context(self): script = textwrap.dedent(""" EG = ExceptionGroup @@ -556,5 +550,57 @@ def test_07_eg_with_notes(self): b' | the terrible value', b' +------------------------------------',] + self.maxDiff = None + self.assertEqual(p.stderr.splitlines(), expected) + + def test_08_eg_with_multiple_notes(self): + script = textwrap.dedent(""" + try: + excs = [] + for msg in ['bad value', 'terrible value']: + try: + raise ValueError(msg) + except ValueError as e: + e.add_note(f'the {msg}') + e.add_note(f'Goodbye {msg}') + excs.append(e) + raise ExceptionGroup("nested", excs) + except ExceptionGroup as e: + e.add_note(('>> Multi line note\\n' + '>> Because I am such\\n' + '>> an important exception.\\n' + '>> empty lines work too\\n' + '\\n' + '(that was an empty line)')) + e.add_note('Goodbye!') + raise + """) + + p = subprocess.run([sys.executable, "-c", script], capture_output=True) + + expected = [b' + Exception Group Traceback (most recent call last):', + b' | File "", line 11, in ', + b' | ExceptionGroup: nested (2 sub-exceptions)', + b' | >> Multi line note', + b' | >> Because I am such', + b' | >> an important exception.', + b' | >> empty lines work too', + b' | ', + b' | (that was an empty line)', + b' | Goodbye!', + b' +-+---------------- 1 ----------------', + b' | Traceback (most recent call last):', + b' | File "", line 6, in ', + b' | ValueError: bad value', + b' | the bad value', + b' | Goodbye bad value', + b' +---------------- 2 ----------------', + b' | Traceback (most recent call last):', + b' | File "", line 6, in ', + b' | ValueError: terrible value', + b' | the terrible value', + b' | Goodbye terrible value', + b' +------------------------------------',] + self.maxDiff = None self.assertEqual(p.stderr.splitlines(), expected) \ No newline at end of file diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index 3f5cd3abb9..5dff21f74c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -101,6 +101,7 @@ import static com.oracle.graal.python.nodes.BuiltinNames.T___DISPLAYHOOK__; import static com.oracle.graal.python.nodes.BuiltinNames.T___EXCEPTHOOK__; import static com.oracle.graal.python.nodes.BuiltinNames.T___GRAALPYTHON__; +import static com.oracle.graal.python.nodes.BuiltinNames.T___NOTES__; import static com.oracle.graal.python.nodes.BuiltinNames.T___STDERR__; import static com.oracle.graal.python.nodes.BuiltinNames.T___STDIN__; import static com.oracle.graal.python.nodes.BuiltinNames.T___STDOUT__; @@ -115,6 +116,7 @@ import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SIZEOF__; +import static com.oracle.graal.python.nodes.StringLiterals.J_NEWLINE; import static com.oracle.graal.python.nodes.StringLiterals.T_BACKSLASHREPLACE; import static com.oracle.graal.python.nodes.StringLiterals.T_BASE_PREFIX; import static com.oracle.graal.python.nodes.StringLiterals.T_BIG; @@ -180,6 +182,7 @@ import com.oracle.graal.python.builtins.objects.function.PArguments; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.ints.PInt; +import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes; import com.oracle.graal.python.builtins.objects.list.PList; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.namespace.PSimpleNamespace; @@ -1518,13 +1521,13 @@ private static void printErrorText(Object out, SyntaxErrData syntaxErrData) { @TruffleBoundary void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object value, Set seen) { - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, new ExceptionPrintContext()); + PythonModule sys, Object out, Object value, Set seen, IteratorNodes.ToArrayNode toArrayNode) { + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, new ExceptionPrintContext(), toArrayNode); } @TruffleBoundary void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object value, Set seen, ExceptionPrintContext ctx) { + PythonModule sys, Object out, Object value, Set seen, ExceptionPrintContext ctx, IteratorNodes.ToArrayNode toArrayNode) { if (seen != null) { // Exception chaining add(seen, value); @@ -1535,12 +1538,12 @@ void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTraceback boolean needsToEnd = ctx.needsToEnd; if (cause != PNone.NONE) { if (notSeen(seen, cause)) { - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, cause, seen, ctx); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, cause, seen, ctx, toArrayNode); fileWriteString(out, T_CAUSE_MESSAGE); } } else if (context != PNone.NONE && !ExceptionNodes.GetSuppressContextNode.executeUncached(value)) { if (notSeen(seen, context)) { - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, context, seen, ctx); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, context, seen, ctx, toArrayNode); fileWriteString(out, T_CONTEXT_MESSAGE); } } @@ -1548,9 +1551,9 @@ void printExceptionRecursive(Node inliningTarget, TracebackBuiltins.GetTraceback } } if (value instanceof PBaseExceptionGroup) { - printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, ctx); + printExceptionGroup(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, seen, ctx, toArrayNode); } else { - printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, ctx); + printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, value, ctx, toArrayNode); } } @@ -1568,7 +1571,7 @@ protected static void fileWriteIndentedString(Object file, TruffleString string, } protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object excValue, ExceptionPrintContext ctx) { + PythonModule sys, Object out, Object excValue, ExceptionPrintContext ctx, IteratorNodes.ToArrayNode toArrayNode) { Object value = excValue; final Object type = getObjectClass(value); if (!PyExceptionInstanceCheckNode.executeUncached(value)) { @@ -1644,10 +1647,35 @@ protected void printException(Node inliningTarget, TracebackBuiltins.GetTracebac } fileWriteString(out, T_NEWLINE); + + if (objectHasAttr(value, T___NOTES__)) { + // print notes + Object notes = objectLookupAttr(value, T___NOTES__); + if (notes instanceof PList noteList) { + Object[] arr = toArrayNode.execute(null, noteList); + for (Object oStr : arr) { + if (oStr instanceof TruffleString note) { + String n = note.toString(); + if (n.contains(J_NEWLINE)) { + String[] lines = n.split(J_NEWLINE); + for (String line : lines) { + fileWriteIndentedString(out, ctx.getMargin(), ctx.getIndent()); + fileWriteString(out, line); + fileWriteString(out, T_NEWLINE); + } + } else { + fileWriteIndentedString(out, ctx.getMargin(), ctx.getIndent()); + fileWriteString(out, note); + fileWriteString(out, T_NEWLINE); + } + } + } + } + } } protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object out, Object excValue, Set seen, ExceptionPrintContext ctx) { + PythonModule sys, Object out, Object excValue, Set seen, ExceptionPrintContext ctx, IteratorNodes.ToArrayNode toArrayNode) { Object value = excValue; final Object type = getObjectClass(value); if (!PyExceptionGroupInstanceCheckNode.executeUncached(value)) { @@ -1669,7 +1697,7 @@ protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTra ctx.increaseDepth(); } - printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excValue, ctx); + printException(inliningTarget, getTbFrameNode, materializeStNode, sys, out, excValue, ctx, toArrayNode); PBaseExceptionGroup exceptionGroup = (PBaseExceptionGroup) excValue; int counter = 1; @@ -1688,7 +1716,7 @@ protected void printExceptionGroup(Node inliningTarget, TracebackBuiltins.GetTra fileWriteString(out, String.format("+---------------- %d ----------------", counter)); fileWriteString(out, T_NEWLINE); ctx.increaseDepth(); - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, exception, seen, ctx); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, out, exception, seen, ctx, toArrayNode); ctx.decreaseDepth(); } else { fileWriteString(out, "+---------------- ... ----------------"); @@ -1757,10 +1785,11 @@ Object doHookWithTb(VirtualFrame frame, PythonModule sys, @SuppressWarnings("unu @Bind Node inliningTarget, @Shared @Cached TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, @Shared @Cached TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - @Shared @Cached("createFor($node)") BoundaryCallData boundaryCallData) { + @Shared @Cached("createFor($node)") BoundaryCallData boundaryCallData, + @Shared @Cached IteratorNodes.ToArrayNode toArrayNode) { Object saved = BoundaryCallContext.enter(frame, boundaryCallData); try { - doHookWithTbImpl(inliningTarget, getTbFrameNode, materializeStNode, sys, value, traceBack); + doHookWithTbImpl(inliningTarget, getTbFrameNode, materializeStNode, sys, value, traceBack, toArrayNode); } finally { BoundaryCallContext.exit(frame, boundaryCallData, saved); } @@ -1769,10 +1798,10 @@ Object doHookWithTb(VirtualFrame frame, PythonModule sys, @SuppressWarnings("unu @TruffleBoundary private void doHookWithTbImpl(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object value, PTraceback traceBack) { + PythonModule sys, Object value, PTraceback traceBack, IteratorNodes.ToArrayNode toArrayNode) { setExceptionTraceback(value, traceBack); Object stdErr = objectLookupAttr(sys, T_STDERR); - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, stdErr, value, createSet()); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, stdErr, value, createSet(), toArrayNode); fileFlush(stdErr); } @@ -1781,10 +1810,11 @@ Object doHookWithoutTb(VirtualFrame frame, PythonModule sys, @SuppressWarnings(" @Bind Node inliningTarget, @Shared @Cached TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, @Shared @Cached TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - @Shared @Cached("createFor($node)") BoundaryCallData boundaryCallData) { + @Shared @Cached("createFor($node)") BoundaryCallData boundaryCallData, + @Shared @Cached IteratorNodes.ToArrayNode toArrayNode) { Object saved = BoundaryCallContext.enter(frame, boundaryCallData); try { - doHookWithoutTbImpl(inliningTarget, getTbFrameNode, materializeStNode, sys, value); + doHookWithoutTbImpl(inliningTarget, getTbFrameNode, materializeStNode, sys, value, toArrayNode); } finally { BoundaryCallContext.exit(frame, boundaryCallData, saved); } @@ -1793,9 +1823,9 @@ Object doHookWithoutTb(VirtualFrame frame, PythonModule sys, @SuppressWarnings(" @TruffleBoundary private void doHookWithoutTbImpl(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, - PythonModule sys, Object value) { + PythonModule sys, Object value, IteratorNodes.ToArrayNode toArrayNode) { Object stdErr = objectLookupAttr(sys, T_STDERR); - printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, stdErr, value, createSet()); + printExceptionRecursive(inliningTarget, getTbFrameNode, materializeStNode, sys, stdErr, value, createSet(), toArrayNode); fileFlush(stdErr); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java index 43a18b7723..d813f4c9f8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java @@ -382,7 +382,7 @@ private static void printInternal(Node inliningTarget, TracebackBuiltins.GetTrac } public static void print(Node inliningTarget, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, PythonModule sys, - Object out, Object tbObj, boolean isExceptionGroup, boolean printMarginControl, int indent, TruffleString margin) { + Object out, Object tbObj, boolean isExceptionGroup, boolean printMarginControl, int indent, TruffleString margin) { // Although we should be behind TB, we need cached nodes, because they may do stack walking // and for that they must be connected to the currently executing root. In practice, it's // not strictly necessary, because they will never request the current frame, but in order