/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Python 3 │ │ https://docs.python.org/3/license.html │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" #include "libc/log/log.h" #include "libc/stdio/stdio.h" #include "third_party/python/Include/abstract.h" #include "third_party/python/Include/pyerrors.h" #include "third_party/python/Include/pylifecycle.h" #include "third_party/python/Include/pythonrun.h" #include "third_party/python/Include/sysmodule.h" #include "third_party/python/Include/traceback.h" /* clang-format off */ _Py_IDENTIFIER(flush); _Py_IDENTIFIER(stdout); _Py_IDENTIFIER(stderr); /* Import the site module (not into __main__ though) */ static void _Py_FatalError_DumpTracebacks(int fd) { fputc('\n', stderr); fflush(stderr); /* display the current Python stack */ _Py_DumpTracebackThreads(fd, NULL, NULL); } /* Print the current exception (if an exception is set) with its traceback, or display the current Python stack. Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is called on catastrophic cases. Return 1 if the traceback was displayed, 0 otherwise. */ static int _Py_FatalError_PrintExc(int fd) { PyObject *ferr, *res; PyObject *exception, *v, *tb; int has_tb; PyErr_Fetch(&exception, &v, &tb); if (exception == NULL) { /* No current exception */ return 0; } ferr = _PySys_GetObjectId(&PyId_stderr); if (ferr == NULL || ferr == Py_None) { /* sys.stderr is not set yet or set to None, no need to try to display the exception */ return 0; } PyErr_NormalizeException(&exception, &v, &tb); if (tb == NULL) { tb = Py_None; Py_INCREF(tb); } PyException_SetTraceback(v, tb); if (exception == NULL) { /* PyErr_NormalizeException() failed */ return 0; } has_tb = (tb != Py_None); PyErr_Display(exception, v, tb); Py_XDECREF(exception); Py_XDECREF(v); Py_XDECREF(tb); /* sys.stderr may be buffered: call sys.stderr.flush() */ res = _PyObject_CallMethodId(ferr, &PyId_flush, NULL); if (res == NULL) PyErr_Clear(); else Py_DECREF(res); return has_tb; } /* Print fatal error message and abort */ void Py_FatalError(const char *msg) { const int fd = fileno(stderr); static int reentrant = 0; #ifdef MS_WINDOWS size_t len; WCHAR* buffer; size_t i; #endif if (reentrant) { /* Py_FatalError() caused a second fatal error. Example: _Py_FlushStdFiles() raises a recursion error. */ goto exit; } reentrant = 1; fprintf(stderr, "Fatal Python error: %s\n", msg); fflush(stderr); /* it helps in Windows debug build */ /* Check if the current thread has a Python thread state and holds the GIL */ PyThreadState *tss_tstate = NULL; // PyGILState_GetThisThreadState(); if (tss_tstate != NULL) { PyThreadState *tstate = PyThreadState_GET(); if (tss_tstate != tstate) { /* The Python thread does not hold the GIL */ tss_tstate = NULL; } } else { /* Py_FatalError() has been called from a C thread which has no Python thread state. */ } int has_tstate_and_gil = (tss_tstate != NULL); /* If an exception is set, print the exception with its traceback */ if (!_Py_FatalError_PrintExc(fd)) { /* No exception is set, or an exception is set without traceback */ _Py_FatalError_DumpTracebacks(fd); } /* The main purpose of faulthandler is to display the traceback. We already * did our best to display it. So faulthandler can now be disabled. * (Don't trigger it on abort().) */ _PyFaulthandler_Fini(); /* Check if the current Python thread hold the GIL */ if (has_tstate_and_gil) { /* Flush sys.stdout and sys.stderr */ _Py_FlushStdFiles(); } #ifdef MS_WINDOWS len = strlen(msg); /* Convert the message to wchar_t. This uses a simple one-to-one conversion, assuming that the this error message actually uses ASCII only. If this ceases to be true, we will have to convert. */ buffer = alloca( (len+1) * (sizeof *buffer)); for( i=0; i<=len; ++i) buffer[i] = msg[i]; OutputDebugStringW(L"Fatal Python error: "); OutputDebugStringW(buffer); OutputDebugStringW(L"\n"); #endif /* MS_WINDOWS */ exit: #if defined(MS_WINDOWS) && defined(_DEBUG) DebugBreak(); #endif if (weaken(__die)) weaken(__die)(); abort(); }