| 97 | } |
| 98 | |
| 99 | void PyExceptionForward::throw_() { |
| 100 | PyObject *etype, *obj, *trace; |
| 101 | PyErr_Fetch(&etype, &obj, &trace); |
| 102 | PyErr_NormalizeException(&etype, &obj, &trace); |
| 103 | |
| 104 | std::string msg{"python exception"}; |
| 105 | bool succ = false; |
| 106 | if (etype && obj && trace) { |
| 107 | auto run = [&]() { |
| 108 | #define DEF(name, expr) \ |
| 109 | PyObjRefKeeper name{expr}; \ |
| 110 | if (!name.get()) \ |
| 111 | return |
| 112 | DEF(mod, PyImport_ImportModule("traceback")); |
| 113 | DEF(result, |
| 114 | PyObject_CallMethod( |
| 115 | mod.get(), "format_exception", "(OOO)", etype, obj, trace)); |
| 116 | if (!PyList_Check(result.get())) |
| 117 | return; |
| 118 | auto size = PyList_Size(result.get()); |
| 119 | msg.append(":\n"); |
| 120 | for (Py_ssize_t i = 0; i < size; ++i) { |
| 121 | msg.append(" "); |
| 122 | msg.append(PyUnicode_AsUTF8(PyList_GetItem(result.get(), i))); |
| 123 | } |
| 124 | msg.pop_back(); // remove last \n |
| 125 | succ = true; |
| 126 | #undef DEF |
| 127 | }; |
| 128 | run(); |
| 129 | } |
| 130 | if (!succ) { |
| 131 | PyObject* obj_str_py; |
| 132 | if (obj && (obj_str_py = PyObject_Repr(obj))) { |
| 133 | msg.append(" with message "); |
| 134 | msg.append(PyUnicode_AsUTF8(obj_str_py)); |
| 135 | Py_DECREF(obj_str_py); |
| 136 | } else { |
| 137 | msg.append(" with unknown message"); |
| 138 | } |
| 139 | } |
| 140 | // throwing exception may cause abort due to unknown reasons; so we first |
| 141 | // log the message |
| 142 | mgb_log_error("caught exception from python callback: %s", msg.c_str()); |
| 143 | fflush(stdout); |
| 144 | fflush(stderr); |
| 145 | throw PyExceptionForward{etype, obj, trace, msg}; |
| 146 | } |
| 147 | |
| 148 | /* ============== namespace npy ============== */ |
| 149 | |