| 287 | #endif |
| 288 | |
| 289 | std::string consume_python_error_detailed() { |
| 290 | PyObject* type = nullptr; |
| 291 | PyObject* value = nullptr; |
| 292 | PyObject* tb = nullptr; |
| 293 | PyErr_Fetch(&type, &value, &tb); |
| 294 | PyErr_NormalizeException(&type, &value, &tb); |
| 295 | |
| 296 | std::string message = "(unknown error)"; |
| 297 | |
| 298 | auto pyobject_to_utf8 = [](PyObject* obj) -> std::string { |
| 299 | if (!obj) { |
| 300 | return {}; |
| 301 | } |
| 302 | PyObject* str = PyObject_Str(obj); |
| 303 | if (!str) { |
| 304 | return {}; |
| 305 | } |
| 306 | std::string result; |
| 307 | if (const char* text = PyUnicode_AsUTF8(str)) { |
| 308 | result = text; |
| 309 | } |
| 310 | Py_DECREF(str); |
| 311 | return result; |
| 312 | }; |
| 313 | |
| 314 | PyObject* traceback_module = PyImport_ImportModule("traceback"); |
| 315 | if (traceback_module) { |
| 316 | PyObject* format_exception = PyObject_GetAttrString(traceback_module, "format_exception"); |
| 317 | if (format_exception && PyCallable_Check(format_exception)) { |
| 318 | PyObject* args = PyTuple_Pack(3, type ? type : Py_None, value ? value : Py_None, tb ? tb : Py_None); |
| 319 | PyObject* lines = args ? PyObject_CallObject(format_exception, args) : nullptr; |
| 320 | if (lines) { |
| 321 | PyObject* empty = PyUnicode_FromString(""); |
| 322 | PyObject* joined = empty ? PyUnicode_Join(empty, lines) : nullptr; |
| 323 | if (joined) { |
| 324 | if (const char* text = PyUnicode_AsUTF8(joined)) { |
| 325 | message = text; |
| 326 | } |
| 327 | Py_DECREF(joined); |
| 328 | } |
| 329 | Py_XDECREF(empty); |
| 330 | Py_DECREF(lines); |
| 331 | } |
| 332 | Py_XDECREF(args); |
| 333 | } |
| 334 | Py_XDECREF(format_exception); |
| 335 | Py_DECREF(traceback_module); |
| 336 | } |
| 337 | |
| 338 | if (message == "(unknown error)" || message.empty()) { |
| 339 | if (const auto value_text = pyobject_to_utf8(value); !value_text.empty()) { |
| 340 | message = value_text; |
| 341 | } else if (const auto type_text = pyobject_to_utf8(type); !type_text.empty()) { |
| 342 | message = type_text; |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | Py_XDECREF(type); |
no test coverage detected