aboutsummaryrefslogtreecommitdiffstats
path: root/yql/essentials/udfs/common/python/bindings/py_errors.cpp
blob: 5741978d543d3872653e242129af199da7cd00e0 (plain) (blame)
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