diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index 86e67d02c2..2c0da66626 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -987,7 +987,13 @@ accepted by asctime(), mktime() and strftime(). May be considered as a UnraisableHookArgs Type used to pass arguments to sys.unraisablehook.""")), + PExceptHookArgs( + "_ExceptHookArgs", + PTuple, + newBuilder().publishInModule(J__THREAD).slots(StructSequenceBuiltins.SLOTS, InstantiableStructSequenceBuiltins.SLOTS).doc(""" + _ExceptHookArgs + Type used to pass arguments to _thread._excepthook.""")), PSSLSession("SSLSession", PythonObject, newBuilder().publishInModule(J__SSL).disallowInstantiation()), PSSLContext("_SSLContext", PythonObject, newBuilder().publishInModule(J__SSL).basetype().slots(SSLContextBuiltins.SLOTS)), PSSLSocket("_SSLSocket", PythonObject, newBuilder().publishInModule(J__SSL).basetype()), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ThreadModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ThreadModuleBuiltins.java index 82dfdce7dd..ac2f70e562 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ThreadModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ThreadModuleBuiltins.java @@ -46,6 +46,7 @@ import static com.oracle.graal.python.nodes.BuiltinNames.T__THREAD; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.List; @@ -57,11 +58,18 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; +import com.oracle.graal.python.builtins.objects.exception.PBaseException; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.module.PythonModule; +import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.builtins.objects.thread.PLock; import com.oracle.graal.python.builtins.objects.thread.PThread; +import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.builtins.objects.tuple.StructSequence; +import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyNumberAsSizeNode; +import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.WriteUnraisableNode; @@ -76,11 +84,14 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.exception.ExceptionUtils; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.exception.PythonThreadKillException; import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleThreadBuilder; @@ -97,6 +108,15 @@ @CoreFunctions(defineModule = J__THREAD) public final class ThreadModuleBuiltins extends PythonBuiltins { + public static final StructSequence.BuiltinTypeDescriptor EXCEPTHOOK_ARGS_DESC = new StructSequence.BuiltinTypeDescriptor( + PythonBuiltinClassType.PExceptHookArgs, + 4, + new String[]{ + "exc_type", "exc_value", "exc_traceback", "thread"}, + new String[]{ + "Exception type", "Exception value", "Exception traceback", + "Exception thread"}); + @Override protected List> getNodeFactories() { return ThreadModuleBuiltinsFactory.getFactories(); @@ -106,6 +126,7 @@ protected List> getNodeFa public void initialize(Python3Core core) { addBuiltinConstant("error", core.lookupType(PythonBuiltinClassType.RuntimeError)); addBuiltinConstant("TIMEOUT_MAX", TIMEOUT_MAX); + StructSequence.initType(core, EXCEPTHOOK_ARGS_DESC); core.lookupBuiltinModule(T__THREAD).setModuleState(0); super.initialize(core); } @@ -173,6 +194,65 @@ static long getStackSize(VirtualFrame frame, Object stackSizeObj, } } + @Builtin(name = "_excepthook", minNumOfPositionalArgs = 2, declaresExplicitSelf = true) + @GenerateNodeFactory + abstract static class GetThreadExceptHookNode extends PythonBinaryBuiltinNode { + @Specialization + @TruffleBoundary + Object getExceptHook(PythonModule self, + Object exceptHookArgs, + @Cached PRaiseNode raiseNode) { + + Object argsType = GetClassNode.GetPythonObjectClassNode.executeUncached((PythonObject) exceptHookArgs); + if (!TypeNodes.IsSameTypeNode.executeUncached(argsType, PythonBuiltinClassType.PExceptHookArgs)) + throw PRaiseNode.getUncached().raise(raiseNode, PythonBuiltinClassType.TypeError, ErrorMessages.ARG_TYPE_MUST_BE, "_thread.excepthook", "ExceptHookArgs"); + + SequenceStorage seq = ((PTuple) exceptHookArgs).getSequenceStorage(); + if (seq.length() != 4) + throw PRaiseNode.getUncached().raise(raiseNode, PythonBuiltinClassType.TypeError, ErrorMessages.TAKES_EXACTLY_D_ARGUMENTS_D_GIVEN, 4, seq.length()); + + Object excType = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 0); + + if (TypeNodes.IsSameTypeNode.executeUncached(excType, PythonBuiltinClassType.SystemExit)) + return PNone.NONE; + + Object excValue = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 1); + Object excTraceback = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 2); + Object thread = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 3); + + CallNode callNode = CallNode.create(); + Object name = null; + + Object nameAttr = PyObjectLookupAttr.executeUncached(thread, tsLiteral("_name")); + if (nameAttr != null && nameAttr != PNone.NONE && nameAttr != PNone.NO_VALUE) { + name = nameAttr.toString(); + } + + if (name == null) { + Object getIdentBuiltin = PyObjectLookupAttr.executeUncached(thread, tsLiteral("get_ident")); + Object ident = callNode.executeWithoutFrame(getIdentBuiltin); + name = ident != null ? ident.toString() : ""; + } + + PrintWriter pw = new PrintWriter(getContext().getEnv().err(), true); + pw.printf("Exception in thread %s:\n", name); + + PException pException; + if (excValue instanceof PException) + pException = (PException) excValue; + else if (excValue instanceof PBaseException base) { + pException = PException.fromObject(base, base.getException().getLocation(), false); + pException.materializeMessage(); + } else { + pw.println(excTraceback.toString()); + return PNone.NONE; + } + + ExceptionUtils.printPythonLikeStackTrace(getContext(), pException); + return PNone.NONE; + } + } + @Builtin(name = "start_new_thread", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 3) @Builtin(name = "start_new", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 3) @GenerateNodeFactory diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/InstantiableStructSequenceBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/InstantiableStructSequenceBuiltins.java index 1f6054487c..b28db538d4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/InstantiableStructSequenceBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/InstantiableStructSequenceBuiltins.java @@ -90,7 +90,8 @@ PythonBuiltinClassType.PIntInfo, PythonBuiltinClassType.PHashInfo, PythonBuiltinClassType.PThreadInfo, - PythonBuiltinClassType.PUnraisableHookArgs}) + PythonBuiltinClassType.PUnraisableHookArgs, + PythonBuiltinClassType.PExceptHookArgs}) public class InstantiableStructSequenceBuiltins extends PythonBuiltins { public static final TpSlots SLOTS = InstantiableStructSequenceBuiltinsSlotsGen.SLOTS; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java index 8644f8e07a..449908e3f9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java @@ -116,7 +116,8 @@ PythonBuiltinClassType.PIntInfo, PythonBuiltinClassType.PHashInfo, PythonBuiltinClassType.PThreadInfo, - PythonBuiltinClassType.PUnraisableHookArgs}) + PythonBuiltinClassType.PUnraisableHookArgs, + PythonBuiltinClassType.PExceptHookArgs}) public final class StructSequenceBuiltins extends PythonBuiltins { public static final TpSlots SLOTS = StructSequenceBuiltinsSlotsGen.SLOTS; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java index 944f037bfa..5d93adacb8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java @@ -2603,7 +2603,7 @@ private static int getBuiltinTypeItemsize(PythonBuiltinClassType cls) { case PInt, Boolean -> 4; case PAsyncGenerator, PFlags, PHashInfo, PTuple, PCoroutine, PGenerator, PThreadInfo, PMemoryView, PStatResult, PUnameResult, PStructTime, PFloatInfo, PStatvfsResult, PIntInfo, PFrame, - PTerminalSize, PUnraisableHookArgs -> 8; + PTerminalSize, PUnraisableHookArgs, PExceptHookArgs -> 8; case PythonClass -> 40; default -> 0; };