123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- #pragma once
- #define PY_SSIZE_T_CLEAN
- #include <Python.h>
- #include <util/generic/strbuf.h>
- #include <util/generic/vector.h>
- #include <util/generic/set.h>
- #include <util/generic/yexception.h>
- #include <util/generic/hash.h>
- #include <util/generic/map.h>
- #include <util/generic/maybe.h>
- #include <utility>
- #include <initializer_list>
- #include "ptr.h"
- namespace NPyBind {
- PyObject* GetTrueRef(bool incref = true);
- PyObject* GetFalseRef(bool incref = true);
- PyObject* BuildPyObject(int val);
- PyObject* BuildPyObject(unsigned int val);
- PyObject* BuildPyObject(long int val);
- PyObject* BuildPyObject(unsigned long int val);
- #ifdef PY_LONG_LONG
- PyObject* BuildPyObject(PY_LONG_LONG val);
- PyObject* BuildPyObject(unsigned PY_LONG_LONG val);
- #endif
- PyObject* BuildPyObject(float val);
- PyObject* BuildPyObject(double val);
- PyObject* BuildPyObject(const TStringBuf& val);
- PyObject* BuildPyObject(const char* val);
- PyObject* BuildPyObject(const TWtringBuf& val);
- PyObject* BuildPyObject(const TBuffer& val);
- PyObject* BuildPyObject(bool val);
- PyObject* BuildPyObject(PyObject*);
- PyObject* BuildPyObject(TPyObjectPtr);
- template <typename T>
- PyObject* BuildPyObject(const TVector<T>& val);
- template <typename T>
- PyObject* BuildPyObject(const TSet<T>& val);
- template <typename TKey, typename TVal>
- PyObject* BuildPyObject(const THashMap<TKey, TVal>& val);
- template <typename T1, typename T2>
- PyObject* BuildPyObject(const std::pair<T1, T2>& val) {
- TPyObjectPtr first(BuildPyObject(val.first), true);
- if (!first) {
- return nullptr;
- }
- TPyObjectPtr second(BuildPyObject(val.second), true);
- if (!first || !second) {
- return nullptr;
- }
- TPyObjectPtr res(PyList_New(2), true);
- PyList_SetItem(res.Get(), 0, first.RefGet());
- PyList_SetItem(res.Get(), 1, second.RefGet());
- return res.RefGet();
- }
- template <typename T>
- PyObject* BuildPyObject(const TVector<T>& val) {
- TPyObjectPtr res(PyList_New(val.size()), true);
- for (size_t i = 0, size = val.size(); i < size; ++i) {
- auto pythonVal = BuildPyObject(std::move(val[i]));
- if (!pythonVal) {
- return nullptr;
- }
- PyList_SetItem(res.Get(), i, pythonVal);
- }
- return res.RefGet();
- }
- template <typename T>
- PyObject* BuildPyObject(TVector<T>&& val) {
- TPyObjectPtr res(PyList_New(val.size()), true);
- for (size_t i = 0, size = val.size(); i < size; ++i) {
- auto pythonVal = BuildPyObject(std::move(val[i]));
- if (!pythonVal) {
- return nullptr;
- }
- PyList_SetItem(res.Get(), i, pythonVal);
- }
- return res.RefGet();
- }
- template <typename T>
- PyObject* BuildPyObject(const TSet<T>& val) {
- TPyObjectPtr res(PySet_New(nullptr), true);
- for (const auto& v : val) {
- auto pythonVal = BuildPyObject(std::move(v));
- if (!pythonVal) {
- return nullptr;
- }
- PySet_Add(res.Get(), pythonVal);
- }
- return res.RefGet();
- }
- template <typename T>
- PyObject* BuildPyObject(const THashSet<T>& val) {
- TPyObjectPtr res(PySet_New(nullptr), true);
- for (const auto& v : val) {
- auto pythonVal = BuildPyObject(std::move(v));
- if (!pythonVal) {
- return nullptr;
- }
- PySet_Add(res.Get(), pythonVal);
- }
- return res.RefGet();
- }
- template <typename TKey, typename TVal>
- PyObject* BuildPyObject(const THashMap<TKey, TVal>& val) {
- TPyObjectPtr res(PyDict_New(), true);
- for (typename THashMap<TKey, TVal>::const_iterator it = val.begin(), end = val.end(); it != end; ++it) {
- auto prevOccurred = PyErr_Occurred();
- Y_UNUSED(prevOccurred);
- TPyObjectPtr k(BuildPyObject(it->first), true);
- if (!k) {
- return nullptr;
- }
- TPyObjectPtr v(BuildPyObject(it->second), true);
- if (!v) {
- return nullptr;
- }
- PyDict_SetItem(res.Get(), k.Get(), v.Get());
- }
- return res.RefGet();
- }
- template <typename TKey, typename TVal>
- PyObject* BuildPyObject(const TMap<TKey, TVal>& val) {
- TPyObjectPtr res(PyDict_New(), true);
- for (typename TMap<TKey, TVal>::const_iterator it = val.begin(), end = val.end(); it != end; ++it) {
- TPyObjectPtr k(BuildPyObject(it->first), true);
- if (!k) {
- return nullptr;
- }
- TPyObjectPtr v(BuildPyObject(it->second), true);
- if (!v) {
- return nullptr;
- }
- PyDict_SetItem(res.Get(), k.Get(), v.Get());
- }
- return res.RefGet();
- }
- template <typename TKey, typename TVal>
- PyObject* BuildPyObject(const TMultiMap<TKey, TVal>& val) {
- TPyObjectPtr res(PyDict_New(), true);
- TMaybe<TKey> prevKey;
- TPyObjectPtr currentEntry(PyList_New(0), true);
- for (const auto& [key, value]: val) {
- if (prevKey.Defined() && prevKey != key) {
- TPyObjectPtr pyPrevKey(BuildPyObject(*prevKey), true);
- if (!pyPrevKey) {
- return nullptr;
- }
- PyDict_SetItem(res.Get(), pyPrevKey.Get(), currentEntry.Get());
- currentEntry = TPyObjectPtr(PyList_New(0), true);
- }
- TPyObjectPtr pyValue(BuildPyObject(value), true);
- if (!pyValue) {
- return nullptr;
- }
- PyList_Append(currentEntry.Get(), pyValue.Get());
- prevKey = key;
- }
- if (prevKey.Defined()) {
- TPyObjectPtr pyPrevKey(BuildPyObject(*prevKey), true);
- if (!pyPrevKey) {
- return nullptr;
- }
- PyDict_SetItem(res.Get(), pyPrevKey.Get(), currentEntry.Get());
- }
- return res.RefGet();
- }
- template <typename T>
- PyObject* BuildPyObject(const TMaybe<T>& val) {
- if (!val.Defined())
- Py_RETURN_NONE;
- return BuildPyObject(val.GetRef());
- }
- template <typename T, typename C, typename D>
- PyObject* BuildPyObject(const TSharedPtr<T, C, D>& val) {
- if (!val.Get())
- Py_RETURN_NONE;
- return BuildPyObject(*val.Get());
- }
- template <typename T>
- bool FromPyObject(PyObject* obj, T& res);
- bool FromPyObject(PyObject* obj, TString& res);
- bool FromPyObject(PyObject* obj, TStringBuf& res);
- bool FromPyObject(PyObject* obj, TUtf16String& res);
- bool FromPyObject(PyObject* obj, TBuffer& res);
- template <typename T>
- bool FromPyObject(PyObject* obj, TMaybe<T>& res) {
- //we need to save current error before trying derserialize the value
- //because it can produce conversion errors in python that we don't need to handle
- struct TError {
- public:
- TError() {
- PyErr_Fetch(&Type, &Value, &Traceback);
- }
- ~TError() {
- PyErr_Restore(Type, Value, Traceback);
- }
- private:
- PyObject* Type = nullptr;
- PyObject* Value = nullptr;
- PyObject* Traceback = nullptr;
- } currentPyExcInfo;
- T val;
- if (FromPyObject(obj, val)) {
- res = val;
- return true;
- }
- if (obj == Py_None) {
- res = Nothing();
- return true;
- }
- return false;
- }
- template <typename T1, typename T2>
- bool FromPyObject(PyObject* obj, std::pair<T1, T2>& res) {
- PyObject* first;
- PyObject* second;
- if (PyTuple_Check(obj) && 2 == PyTuple_Size(obj)) {
- first = PyTuple_GET_ITEM(obj, 0);
- second = PyTuple_GET_ITEM(obj, 1);
- } else if (PyList_Check(obj) && 2 == PyList_Size(obj)) {
- first = PyList_GET_ITEM(obj, 0);
- second = PyList_GET_ITEM(obj, 1);
- } else {
- return false;
- }
- return FromPyObject(first, res.first) && FromPyObject(second, res.second);
- }
- template <typename T>
- bool FromPyObject(PyObject* obj, TVector<T>& res) {
- if (!PyList_Check(obj))
- return false;
- size_t cnt = PyList_Size(obj);
- res.resize(cnt);
- for (size_t i = 0; i < cnt; ++i) {
- PyObject* item = PyList_GET_ITEM(obj, i);
- if (!FromPyObject(item, res[i]))
- return false;
- }
- return true;
- }
- template <typename K, typename V>
- bool FromPyObject(PyObject* obj, THashMap<K, V>& res) {
- if (!PyDict_Check(obj))
- return false;
- TPyObjectPtr list(PyDict_Keys(obj), true);
- size_t cnt = PyList_Size(list.Get());
- for (size_t i = 0; i < cnt; ++i) {
- PyObject* key = PyList_GET_ITEM(list.Get(), i);
- PyObject* value = PyDict_GetItem(obj, key);
- K rkey;
- V rvalue;
- if (!FromPyObject(key, rkey))
- return false;
- if (!FromPyObject(value, rvalue))
- return false;
- res[rkey] = rvalue;
- }
- return true;
- }
- template <typename K, typename V>
- bool FromPyObject(PyObject* obj, TMap<K, V>& res) {
- if (!PyDict_Check(obj))
- return false;
- TPyObjectPtr list(PyDict_Keys(obj), true);
- size_t cnt = PyList_Size(list.Get());
- for (size_t i = 0; i < cnt; ++i) {
- PyObject* key = PyList_GET_ITEM(list.Get(), i);
- PyObject* value = PyDict_GetItem(obj, key);
- K rkey;
- V rvalue;
- if (!FromPyObject(key, rkey))
- return false;
- if (!FromPyObject(value, rvalue))
- return false;
- res[rkey] = rvalue;
- }
- return true;
- }
- class cast_exception: public TBadCastException {
- };
- template <typename T>
- T FromPyObject(PyObject* obj) {
- T res;
- if (!FromPyObject(obj, res))
- ythrow cast_exception() << "Cannot cast argument to " << TypeName<T>();
- return res;
- }
- template <class... Args, std::size_t... I>
- bool ExtractArgs(std::index_sequence<I...>, PyObject* args, Args&... outArgs) {
- if (!args || !PyTuple_Check(args) || PyTuple_Size(args) != sizeof...(Args))
- return false;
- bool res = true;
- (void)std::initializer_list<bool>{(res = res && NPyBind::FromPyObject(PyTuple_GET_ITEM(args, I), outArgs))...};
- return res;
- }
- template <class... Args>
- bool ExtractArgs(PyObject* args, Args&... outArgs) {
- return ExtractArgs(std::index_sequence_for<Args...>(), args, outArgs...);
- }
- template <class... Args, std::size_t... I>
- bool ExtractOptionalArgs(std::index_sequence<I...>, PyObject* args, PyObject* kwargs, const char* keywords[], Args&... outArgs) {
- PyObject* pargs[sizeof...(Args)] = {};
- static const char format[sizeof...(Args) + 2] = {'|', ((void)I, 'O')..., 0};
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, format, const_cast<char**>(keywords), &pargs[I]...))
- return false;
- bool res = true;
- (void)std::initializer_list<bool>{(res = res && (!pargs[I] || NPyBind::FromPyObject(pargs[I], outArgs)))...};
- return res;
- }
- template <class... Args>
- bool ExtractOptionalArgs(PyObject* args, PyObject* kwargs, const char* keywords[], Args&... outArgs) {
- return ExtractOptionalArgs(std::index_sequence_for<Args...>(), args, kwargs, keywords, outArgs...);
- }
- template <typename... Args, std::size_t... I>
- static auto GetArguments(std::index_sequence<I...>, PyObject* args) {
- Y_UNUSED(args); // gcc bug
- return std::make_tuple(FromPyObject<std::remove_cv_t<std::remove_reference_t<Args>>>(PyTuple_GetItem(args, I))...);
- }
- template <typename... Args>
- static auto GetArguments(PyObject* args) {
- return GetArguments<Args...>(std::index_sequence_for<Args...>(), args);
- }
- inline PyObject* ReturnString(TStringBuf s) {
- #if PY_MAJOR_VERSION >= 3
- return PyUnicode_FromStringAndSize(s.data(), s.size());
- #else
- return PyBytes_FromStringAndSize(s.data(), s.size());
- #endif
- }
- inline TPyObjectPtr ReturnBytes(TStringBuf s) {
- return TPyObjectPtr(PyBytes_FromStringAndSize(s.data(), s.size()), true);
- }
- inline TPyObjectPtr NameFromString(TStringBuf s) {
- return TPyObjectPtr(ReturnString(s), true);
- }
- }
|