#include "py_errors.h" #include "py_ptr.h" #include "py_cast.h" #include "py_utils.h" #include #include namespace NPython { // this function in conjuction with code after Py_Initialize // does approximately following: // // sys.stderr = StderrProxy(sys.stderr) // // ... // // sys.stderr._toggle_real_mode() // sys.excepthook( // sys.last_type, // sys.last_value, // sys.last_traceback) // sys.stderr._get_value() // sys.stderr._toggle_real_mode() // // where _toggle_real_mode, _get_value & all calls to stderr not in real mode // are handled in a thread-safe way // TString GetLastErrorAsString() { PyObject* etype; PyObject* evalue; PyObject* etraceback; PyErr_Fetch(&etype, &evalue, &etraceback); if (!etype) { return {}; } TPyObjectPtr etypePtr {etype, TPyObjectPtr::ADD_REF}; TPyObjectPtr evaluePtr {evalue, TPyObjectPtr::ADD_REF}; TPyObjectPtr etracebackPtr {etraceback, TPyObjectPtr::ADD_REF}; TPyObjectPtr stderrObject {PySys_GetObject("stderr"), TPyObjectPtr::ADD_REF}; if (!stderrObject) { return {}; } TPyObjectPtr unused = PyObject_CallMethod(stderrObject.Get(), "_toggle_real_mode", nullptr); PyErr_Restore(etypePtr.Get(), evaluePtr.Get(), etracebackPtr.Get()); // in unusual situations there may be low-level write to stderr // (by direct C FILE* write), but that's OK PyErr_Print(); TPyObjectPtr error = PyObject_CallMethod(stderrObject.Get(), "_get_value", nullptr); if (!error) { return {}; } unused.ResetSteal( PyObject_CallMethod(stderrObject.Get(), "_toggle_real_mode", nullptr) ); TString errorValue; if (!TryPyCast(error.Get(), errorValue)) { errorValue = TString("can't get error string from: ") += PyObjectRepr(error.Get()); } return errorValue; } } // namspace NPython