1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
#include "py_errors.h"
#include "py_ptr.h"
#include "py_cast.h"
#include "py_utils.h"
#include <util/generic/string.h>
#include <util/stream/output.h>
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
|