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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
#ifndef WREPORT_PYTHON_CORE_H
#define WREPORT_PYTHON_CORE_H
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdexcept>
namespace wreport {
namespace python {
/**
* unique_ptr-like object that contains PyObject pointers, and that calls
* Py_DECREF on destruction.
*/
template<typename Obj>
class py_unique_ptr
{
protected:
Obj* ptr;
public:
py_unique_ptr() : ptr(nullptr) {}
py_unique_ptr(Obj* o) : ptr(o) {}
py_unique_ptr(const py_unique_ptr&) = delete;
py_unique_ptr(py_unique_ptr&& o) : ptr(o.ptr) { o.ptr = nullptr; }
~py_unique_ptr() { Py_XDECREF(ptr); }
py_unique_ptr& operator=(const py_unique_ptr&) = delete;
py_unique_ptr& operator=(py_unique_ptr&& o)
{
if (this == &o) return *this;
Py_XDECREF(ptr);
ptr = o.ptr;
o.ptr = nullptr;
return *this;
}
void incref() { Py_XINCREF(ptr); }
void decref() { Py_XDECREF(ptr); }
void clear()
{
Py_XDECREF(ptr);
ptr = nullptr;
}
void reset(Obj* o)
{
Py_XDECREF(ptr);
ptr = o;
}
/// Release the reference without calling Py_DECREF
Obj* release()
{
Obj* res = ptr;
ptr = nullptr;
return res;
}
/// Call repr() on the object
std::string repr()
{
if (!ptr)
return "(null)";
py_unique_ptr<PyObject> py_repr(PyObject_Repr(ptr));
if (!py_repr)
{
PyErr_Clear();
return "(repr failed)";
}
Py_ssize_t size;
const char* res = PyUnicode_AsUTF8AndSize(py_repr, &size);
return std::string(res, size);
}
/// Call str() on the object
std::string str()
{
if (!ptr)
return "(null)";
py_unique_ptr<PyObject> py_repr(PyObject_Str(ptr));
if (!py_repr)
{
PyErr_Clear();
return "(str failed)";
}
Py_ssize_t size;
const char* res = PyUnicode_AsUTF8AndSize(py_repr, &size);
return std::string(res, size);
}
/// Use it as a Obj
operator Obj*() { return ptr; }
/// Use it as a Obj
operator Obj*() const { return ptr; }
/// Use it as a Obj
Obj* operator->() { return ptr; }
/// Use it as a Obj
Obj* operator->() const { return ptr; }
/// Get the pointer (useful for passing to Py_BuildValue)
Obj* get() { return ptr; }
/// Get the pointer (useful for passing to Py_BuildValue)
Obj* get() const { return ptr; }
};
typedef py_unique_ptr<PyObject> pyo_unique_ptr;
/**
* Release the GIL during the lifetime of this object;
*/
class ReleaseGIL
{
PyThreadState *_save = nullptr;
public:
ReleaseGIL()
{
_save = PyEval_SaveThread();
}
~ReleaseGIL()
{
lock();
}
void lock()
{
if (!_save) return;
PyEval_RestoreThread(_save);
_save = nullptr;
}
};
/**
* Acquire the GIL prior to invoking python code.
*
* This also works correctly if the GIL is already acquired, so it can be used
* to wrap invocation of python code regardless of the GIL status.
*/
class AcquireGIL
{
PyGILState_STATE _state;
public:
AcquireGIL()
{
_state = PyGILState_Ensure();
}
~AcquireGIL()
{
PyGILState_Release(_state);
}
};
/**
* Exception raised when a python function returns an error with an exception
* set.
*
* When catching this exception, python exception information is already set,
* so the only thing to do is to return the appropriate error to the python
* caller.
*
* This exception carries to information, because it is all set in the python
* exception information.
*/
struct PythonException : public std::exception {};
/**
* Throw PythonException if the given pointer is nullptr.
*
* This can be used to wrap Python API invocations, throwing PythonException if
* the API call returned an error.
*/
template<typename T>
inline T* throw_ifnull(T* o)
{
if (!o) throw PythonException();
return o;
}
}
}
#endif
|