py-common: make bt_py_common_format_exception accept an arbitrary exception
authorSimon Marchi <simon.marchi@efficios.com>
Sun, 21 Jul 2019 16:49:05 +0000 (12:49 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Wed, 24 Jul 2019 04:18:54 +0000 (00:18 -0400)
A following patch will need to format each exception of the exception
chain independently.  It will therefore need a function like
bt_py_common_format_exception, but that:

- takes the exception to format as a parameter, rather than relying on
  the Python error indicator
- has an option for formatting just that exception, not following the
  causes chain

This patch changes bt_py_common_format_exception to meet these
requirements.  The behavior of the old bt_py_common_format_exception is
now offered by bt_py_common_format_current_exception, so the existing
callers are updated to use that one.

Change-Id: I7bbb442bc1dc363e5441b3b11ed6f0c6e213ba79
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1737
Tested-by: jenkins <jenkins@lttng.org>
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
src/bindings/python/bt2/bt2/native_bt_component_class.i
src/py-common/py-common.c
src/py-common/py-common.h
src/python-plugin-provider/python-plugin-provider.c

index 4b718015738c76f8be9254a6e5fcfccd62a4d9a6..0b679ec8359a646d000e87672babc4b33adb210e 100644 (file)
@@ -182,9 +182,9 @@ void log_exception_and_maybe_append_error(int log_level,
        GString *gstr;
 
        BT_ASSERT(PyErr_Occurred());
-       gstr = bt_py_common_format_exception(BT_LOG_OUTPUT_LEVEL);
+       gstr = bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL);
        if (!gstr) {
-               /* bt_py_common_format_exception() logs errors */
+               /* bt_py_common_format_current_exception() logs errors */
                goto end;
        }
 
index 4fea4d27b5475ecfa5c6c28208ef8de3a0d79795..465f35015ce2bdb76daf108f496b9cec17e8d6d0 100644 (file)
 #include "py-common.h"
 
 BT_HIDDEN
-GString *bt_py_common_format_exception(int log_level)
+GString *bt_py_common_format_exception(PyObject *py_exc_type,
+               PyObject *py_exc_value, PyObject *py_exc_tb,
+               int log_level, bool chain)
 {
-       PyObject *type = NULL;
-       PyObject *value = NULL;
-       PyObject *traceback = NULL;
        PyObject *traceback_module = NULL;
        PyObject *format_exception_func = NULL;
        PyObject *exc_str_list = NULL;
@@ -44,13 +43,6 @@ GString *bt_py_common_format_exception(int log_level)
        const char *format_exc_func_name;
        Py_ssize_t i;
 
-       BT_ASSERT(PyErr_Occurred());
-       PyErr_Fetch(&type, &value, &traceback);
-       BT_ASSERT(type);
-
-       /* Make sure `value` is what we expected: an instance of `type` */
-       PyErr_NormalizeException(&type, &value, &traceback);
-
        /*
         * Import the standard `traceback` module which contains the
         * functions to format an exception.
@@ -62,12 +54,12 @@ GString *bt_py_common_format_exception(int log_level)
        }
 
        /*
-        * `traceback` can be `NULL`, when we fail to call a Python
+        * `py_exc_tb` can be `NULL`, when we fail to call a Python
         * function from native code (there is no Python stack at that
         * point). For example, a function which takes 5 positional
         * arguments, but 8 were given.
         */
-       format_exc_func_name = traceback ? "format_exception" :
+       format_exc_func_name = py_exc_tb ? "format_exception" :
                "format_exception_only";
        format_exception_func = PyObject_GetAttrString(traceback_module,
                format_exc_func_name);
@@ -83,8 +75,13 @@ GString *bt_py_common_format_exception(int log_level)
                goto error;
        }
 
+       /*
+        * If we are calling format_exception_only, `py_exc_tb` is NULL, so the
+        * effective argument list is of length 2.
+        */
        exc_str_list = PyObject_CallFunctionObjArgs(format_exception_func,
-               type, value, traceback, NULL);
+               py_exc_type, py_exc_value, py_exc_tb, Py_None /* limit */,
+               chain ? Py_True : Py_False /* chain */, NULL);
        if (!exc_str_list) {
                if (BT_LOG_ON_ERROR) {
                        BT_LOGE("Failed to call `traceback.%s` function:",
@@ -139,6 +136,30 @@ end:
        Py_XDECREF(format_exception_func);
        Py_XDECREF(traceback_module);
 
+       return msg_buf;
+}
+
+BT_HIDDEN
+GString *bt_py_common_format_current_exception(int log_level)
+{
+       GString *result;
+       PyObject *py_exc_type = NULL;
+       PyObject *py_exc_value = NULL;
+       PyObject *py_exc_tb = NULL;
+
+       BT_ASSERT(PyErr_Occurred());
+       PyErr_Fetch(&py_exc_type, &py_exc_value, &py_exc_tb);
+       BT_ASSERT(py_exc_type);
+
+       /*
+        * Make sure `py_exc_value` is what we expected: an instance of
+        * `py_exc_type`.
+        */
+       PyErr_NormalizeException(&py_exc_type, &py_exc_value, &py_exc_tb);
+
+       result = bt_py_common_format_exception(py_exc_type, py_exc_value,
+               py_exc_tb, log_level, true);
+
        /*
         * We can safely call PyErr_Restore() because we always call
         * PyErr_Fetch(), and having an error indicator is a function's
@@ -146,7 +167,7 @@ end:
         *
         * PyErr_Restore() steals our references.
         */
-       PyErr_Restore(type, value, traceback);
+       PyErr_Restore(py_exc_type, py_exc_value, py_exc_tb);
 
-       return msg_buf;
+       return result;
 }
index dbfd8825225a5f9afa8726cc83551902419ee5a6..27a3a619c517960a6ba9e22e9d4c0f7500244966 100644 (file)
  */
 
 #include <glib.h>
+#include <Python.h>
 
 #include "common/macros.h"
 
 /*
- * Formats the Python error indicator (exception) and returns the
- * formatted string, or `NULL` on error. The returned string does NOT
- * end with a newline.
+ * Formats the Python exception described by `py_exc_type`, `py_exc_value`
+ * and `py_exc_tb` and returns the formatted string, or `NULL` on error. The
+ * returned string does NOT end with a newline.
+ *
+ * If `chain` is true, include all exceptions in the causality chain
+ * (see parameter `chain` of Python's traceback.format_exception).
+ */
+BT_HIDDEN
+GString *bt_py_common_format_exception(PyObject *py_exc_type,
+               PyObject *py_exc_value, PyObject *py_exc_tb,
+               int log_level, bool chain);
+
+/*
+ * Wrapper for `bt_py_common_format_exception` that passes the Python error
+ * indicator (the exception currently being raised).  Always include the
+ * full exception chain.
  *
  * You must ensure that the error indicator is set with PyErr_Occurred()
  * before you call this function.
@@ -41,6 +55,6 @@
  * that is fetched is always restored.
  */
 BT_HIDDEN
-GString *bt_py_common_format_exception(int log_level);
+GString *bt_py_common_format_current_exception(int log_level);
 
 #endif /* BABELTRACE_PY_COMMON_INTERNAL_H */
index a1324cff97ff7bbc8c4d259df24c97645687f39f..765035e2df60dcde37713ffb72c2c699ba3e4b81 100644 (file)
@@ -72,7 +72,7 @@ void append_python_traceback_error_cause(void)
        GString *exc = NULL;
 
        if (Py_IsInitialized() && PyErr_Occurred()) {
-               exc = bt_py_common_format_exception(BT_LOG_OUTPUT_LEVEL);
+               exc = bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL);
                if (!exc) {
                        BT_LOGE_STR("Failed to format Python exception.");
                        goto end;
@@ -94,7 +94,7 @@ void log_python_traceback(int log_level)
        GString *exc = NULL;
 
        if (Py_IsInitialized() && PyErr_Occurred()) {
-               exc = bt_py_common_format_exception(BT_LOG_OUTPUT_LEVEL);
+               exc = bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL);
                if (!exc) {
                        BT_LOGE_STR("Failed to format Python exception.");
                        goto end;
This page took 0.029719 seconds and 4 git commands to generate.