+ 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, ...)
+{
+ int i, ret;
+ va_list args;
+ 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;
+
+ py_etype = py_evalue = py_etraceback = py_mod = py_func = NULL;
+