| 112 | #if LINK_WITH_PYTHON |
| 113 | |
| 114 | void reportPythonError([[maybe_unused]] EnergyPlusData &state) |
| 115 | { |
| 116 | PyObject *exc_type = nullptr; |
| 117 | PyObject *exc_value = nullptr; |
| 118 | PyObject *exc_tb = nullptr; |
| 119 | PyErr_Fetch(&exc_type, &exc_value, &exc_tb); |
| 120 | // Normalizing the exception is needed. Without it, our custom EnergyPlusException go through just fine |
| 121 | // but any ctypes built-in exception for eg will have wrong types |
| 122 | PyErr_NormalizeException(&exc_type, &exc_value, &exc_tb); |
| 123 | PyObject *str_exc_value = PyObject_Repr(exc_value); // Now a unicode object |
| 124 | PyObject *pyStr2 = PyUnicode_AsEncodedString(str_exc_value, "utf-8", "Error ~"); |
| 125 | Py_DECREF(str_exc_value); |
| 126 | char *strExcValue = PyBytes_AsString(pyStr2); // NOLINT(hicpp-signed-bitwise) |
| 127 | Py_DECREF(pyStr2); |
| 128 | EnergyPlus::ShowContinueError(state, "Python error description follows: "); |
| 129 | EnergyPlus::ShowContinueError(state, strExcValue); |
| 130 | |
| 131 | // See if we can get a full traceback. |
| 132 | // Calls into python, and does the same as capturing the exception in `e` |
| 133 | // then `print(traceback.format_exception(e.type, e.value, e.tb))` |
| 134 | PyObject *pModuleName = PyUnicode_DecodeFSDefault("traceback"); |
| 135 | PyObject *pyth_module = PyImport_Import(pModuleName); |
| 136 | Py_DECREF(pModuleName); |
| 137 | |
| 138 | if (pyth_module == nullptr) { |
| 139 | EnergyPlus::ShowContinueError(state, "Cannot find 'traceback' module in reportPythonError(), this is weird"); |
| 140 | return; |
| 141 | } |
| 142 | |
| 143 | PyObject *pyth_func = PyObject_GetAttrString(pyth_module, "format_exception"); |
| 144 | Py_DECREF(pyth_module); // PyImport_Import returns a new reference, decrement it |
| 145 | |
| 146 | if ((pyth_func != nullptr) || (PyCallable_Check(pyth_func) != 0)) { |
| 147 | |
| 148 | PyObject *pyth_val = PyObject_CallFunction(pyth_func, "OOO", exc_type, exc_value, exc_tb); |
| 149 | |
| 150 | // traceback.format_exception returns a list, so iterate on that |
| 151 | if ((pyth_val == nullptr) || !PyList_Check(pyth_val)) { // NOLINT(hicpp-signed-bitwise) |
| 152 | EnergyPlus::ShowContinueError(state, "In reportPythonError(), traceback.format_exception did not return a list."); |
| 153 | return; |
| 154 | } |
| 155 | |
| 156 | Py_ssize_t numVals = PyList_Size(pyth_val); |
| 157 | if (numVals == 0) { |
| 158 | EnergyPlus::ShowContinueError(state, "No traceback available"); |
| 159 | return; |
| 160 | } |
| 161 | |
| 162 | EnergyPlus::ShowContinueError(state, "Python traceback follows: "); |
| 163 | |
| 164 | EnergyPlus::ShowContinueError(state, "```"); |
| 165 | |
| 166 | for (Py_ssize_t itemNum = 0; itemNum < numVals; itemNum++) { |
| 167 | PyObject *item = PyList_GetItem(pyth_val, itemNum); |
| 168 | if (PyUnicode_Check(item)) { // NOLINT(hicpp-signed-bitwise) -- something inside Python code causes warning |
| 169 | std::string traceback_line = PyUnicode_AsUTF8(item); |
| 170 | if (!traceback_line.empty() && traceback_line[traceback_line.length() - 1] == '\n') { |
| 171 | traceback_line.erase(traceback_line.length() - 1); |
no test coverage detected