edid: Shorten two code snippets.
[libsigrokdecode.git] / exception.c
index 1ce66a875db44b23f4024545defe5bf8f08ac054..9f8ee4ceb17b028179446b786d53ce318b7fbfb8 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "libsigrokdecode.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */
-#include "libsigrokdecode-internal.h"
-#include "config.h"
+#include <config.h>
+#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */
+#include "libsigrokdecode.h"
 #include <stdarg.h>
 #include <glib.h>
-#include <frameobject.h> /* Python header not pulled in by default. */
+
+static char *py_stringify(PyObject *py_obj)
+{
+       PyObject *py_str, *py_bytes;
+       char *str = NULL;
+
+       /* Note: Caller already ran PyGILState_Ensure(). */
+
+       if (!py_obj)
+               return NULL;
+
+       py_str = PyObject_Str(py_obj);
+       if (!py_str || !PyUnicode_Check(py_str))
+               goto cleanup;
+
+       py_bytes = PyUnicode_AsUTF8String(py_str);
+       if (!py_bytes)
+               goto cleanup;
+
+       str = g_strdup(PyBytes_AsString(py_bytes));
+       Py_DECREF(py_bytes);
+
+cleanup:
+       Py_XDECREF(py_str);
+       if (!str) {
+               PyErr_Clear();
+               srd_dbg("Failed to stringify object.");
+       }
+
+       return str;
+}
+
+static char *py_get_string_attr(PyObject *py_obj, const char *attr)
+{
+       PyObject *py_str, *py_bytes;
+       char *str = NULL;
+
+       /* Note: Caller already ran PyGILState_Ensure(). */
+
+       if (!py_obj)
+               return NULL;
+
+       py_str = PyObject_GetAttrString(py_obj, attr);
+       if (!py_str || !PyUnicode_Check(py_str))
+               goto cleanup;
+
+       py_bytes = PyUnicode_AsUTF8String(py_str);
+       if (!py_bytes)
+               goto cleanup;
+
+       str = g_strdup(PyBytes_AsString(py_bytes));
+       Py_DECREF(py_bytes);
+
+cleanup:
+       Py_XDECREF(py_str);
+       if (!str) {
+               PyErr_Clear();
+               srd_dbg("Failed to get object attribute %s.", attr);
+       }
+
+       return str;
+}
 
 /** @private */
 SRD_PRIV void srd_exception_catch(const char *format, ...)
 {
-       PyObject *etype, *evalue, *etb, *py_str;
-       PyTracebackObject *py_tb;
-       GString *msg;
+       int i, ret;
        va_list args;
-       char *ename, *str, *tracestr;
+       PyObject *py_etype, *py_evalue, *py_etraceback;
+       PyObject *py_mod, *py_func, *py_tracefmt;
+       char *msg, *etype_name, *evalue_str, *outstr;
+       const char *etype_name_fallback;
+       PyGILState_STATE gstate;
+       GString *s;
 
-       if (!PyErr_Occurred())
-               /* Nothing is wrong. */
-               return;
+       py_etype = py_evalue = py_etraceback = py_mod = py_func = NULL;
 
-       PyErr_Fetch(&etype, &evalue, &etb);
-       PyErr_NormalizeException(&etype, &evalue, &etb);
+       va_start(args, format);
+       msg = g_strdup_vprintf(format, args);
+       va_end(args);
+
+       gstate = PyGILState_Ensure();
 
-       if (!(py_str = PyObject_Str(evalue))) {
-               /* Shouldn't happen. */
-               srd_dbg("Failed to convert exception value to string.");
-               return;
+       PyErr_Fetch(&py_etype, &py_evalue, &py_etraceback);
+       if (!py_etype) {
+               /* No current exception, so just print the message. */
+               srd_err("%s.", msg);
+               goto cleanup;
        }
+       PyErr_NormalizeException(&py_etype, &py_evalue, &py_etraceback);
 
-       msg = g_string_sized_new(128);
-       va_start(args, format);
-       g_string_vprintf(msg, format, args);
-       va_end(args);
+       etype_name = py_get_string_attr(py_etype, "__name__");
+       evalue_str = py_stringify(py_evalue);
+       etype_name_fallback = (etype_name) ? etype_name : "(unknown exception)";
 
-       /* Can be NULL. */
-       if (evalue)
-               ename = (char *)Py_TYPE(evalue)->tp_name;
+       if (evalue_str)
+               srd_err("%s: %s: %s", etype_name_fallback, msg, evalue_str);
        else
-               ename = "(unknown exception)";
-
-       /* Send the exception error message(s) to srd_err(). */
-       py_str_as_str(py_str, &str);
-       g_string_append(msg, ename);
-       g_string_append(msg, ": ");
-       g_string_append(msg, str);
-       Py_DecRef(py_str);
-       srd_err(msg->str);
-
-       /* Send a more precise error location to srd_dbg(), if we have it. */
-       if (etb && etb != Py_None) {
-               tracestr = NULL;
-               py_tb = (PyTracebackObject *)etb;
-               py_str = PyUnicode_FromFormat("%U:%d in %U",
-                                       py_tb->tb_frame->f_code->co_filename,
-                                       py_tb->tb_frame->f_lineno,
-                                       py_tb->tb_frame->f_code->co_name);
-               py_str_as_str(py_str, &tracestr);
-               Py_DecRef(py_str);
-               g_string_printf(msg, "%s in %s: %s", ename, tracestr, str);
-               srd_dbg(msg->str);
-               g_free(tracestr);
+               srd_err("%s: %s.", etype_name_fallback, msg);
+
+       g_free(evalue_str);
+       g_free(etype_name);
+
+       /* If there is no traceback object, we are done. */
+       if (!py_etraceback)
+               goto cleanup;
+
+       py_mod = py_import_by_name("traceback");
+       if (!py_mod)
+               goto cleanup;
+
+       py_func = PyObject_GetAttrString(py_mod, "format_exception");
+       if (!py_func || !PyCallable_Check(py_func))
+               goto cleanup;
+
+       /* Call into Python to format the stack trace. */
+       py_tracefmt = PyObject_CallFunctionObjArgs(py_func,
+                       py_etype, py_evalue, py_etraceback, NULL);
+       if (!py_tracefmt || !PyList_Check(py_tracefmt))
+               goto cleanup;
+
+       s = g_string_sized_new(128);
+       for (i = 0; i < PyList_Size(py_tracefmt); i++) {
+               ret = py_listitem_as_str(py_tracefmt, i, &outstr);
+               if (ret == 0) {
+                       s = g_string_append(s, outstr);
+                       g_free(outstr);
+               }
        }
-       g_free(str);
-       g_string_free(msg, TRUE);
+       srd_err("%s", s->str);
+       g_string_free(s, TRUE);
+
+       Py_DECREF(py_tracefmt);
 
-       Py_XDECREF(etype);
-       Py_XDECREF(evalue);
-       Py_XDECREF(etb);
+cleanup:
+       Py_XDECREF(py_func);
+       Py_XDECREF(py_mod);
+       Py_XDECREF(py_etraceback);
+       Py_XDECREF(py_evalue);
+       Py_XDECREF(py_etype);
 
        /* Just in case. */
        PyErr_Clear();
+
+       PyGILState_Release(gstate);
+
+       g_free(msg);
 }