#include "exceptions.h" #include "cast.h" #include "module.h" #include <util/generic/algorithm.h> namespace NPyBind { namespace NPrivate { TPyObjectPtr CreatePyBindModule() { return TPyObjectPtr(TExceptionsHolder::DoInitPyBindModule(), true); } }//NPrivate TPyObjectPtr TExceptionsHolder::GetException(const TString& name) { if (name == "") return TPyObjectPtr(nullptr); if (!Exceptions[name].Get()) ythrow yexception() << "Wrong base class '" << name << "'"; return Exceptions[name]; } TPyObjectPtr TExceptionsHolder::GetExceptions(const TVector<TString>& names) { TVector<TString> tmp(names.begin(), names.end()); TVector<TString>::iterator end = std::unique(tmp.begin(), tmp.end()); TPyObjectPtr tuple(PyTuple_New(std::distance(tmp.begin(), end)), true); for (size_t i = 0; i < (size_t)std::distance(tmp.begin(), end); ++i) { if (!Exceptions[tmp[i]].Get()) ythrow yexception() << "Wrong base class '" << tmp[i] << "'"; PyTuple_SetItem(tuple.Get(), i, Exceptions[tmp[i]].Get()); } return tuple; } // def PyBindObjectReconstructor(cl, props): // return cl(__properties__=props) static PyObject* PyBindObjectReconstructor(PyObject*, PyObject* args) { TPyObjectPtr callable, props; if (!ExtractArgs(args, callable, props)) ythrow yexception() << "Wrong method arguments"; #if PY_MAJOR_VERSION >= 3 TPyObjectPtr noArgs(PyTuple_New(0), true); #else TPyObjectPtr noArgs(PyList_New(0), true); #endif TPyObjectPtr kw(PyDict_New(), true); PyDict_SetItemString(kw.Get(), "__properties__", props.Get()); TPyObjectPtr res(PyObject_Call(callable.Get(), noArgs.Get(), kw.Get()), true); return res.RefGet(); } static PyMethodDef PyBindMethods[] = { {"PyBindObjectReconstructor", PyBindObjectReconstructor, METH_VARARGS, "Tech method. It's required for unpickling."}, {nullptr, nullptr, 0, nullptr}}; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "pybind", NULL, -1, PyBindMethods, NULL, NULL, NULL, NULL }; static PyObject* InitPyBind() { return PyModule_Create(&moduledef); } #else static PyObject* InitPyBind() { return Py_InitModule("pybind", PyBindMethods); } #endif void TExceptionsHolder::DoInitPyBindModule2() { DoInitPyBindModule(); } PyObject* TExceptionsHolder::DoInitPyBindModule() { Instance().Module = NPyBind::TPyObjectPtr(InitPyBind(), true); if (!Instance().Module.Get()) return nullptr; for (TCheckersVector::const_iterator it = Instance().Checkers.begin(), end = Instance().Checkers.end(); it != end; ++it) { TString name = (*it)->GetName(); if (!!name) { //Ref to the object should be incremented before passing to AddObject auto res = PyModule_AddObject(Instance().Module.Get(), name.data(), (*it)->GetException().RefGet()); if (res < 0) { ythrow yexception() << "Failed to add object " << name << " to internal module pybind"; } } } return Instance().Module.RefGet(); } void TExceptionsHolder::Clear() { //Unfortunately in Python3 we can't retrack this object because of PyError_NewException //it's only the safe way to preserve GC gens in valid state during the finalization for (auto& ptr: Checkers) { if (!dynamic_cast<const TPyErrExceptionsChecker*>(ptr.Get())) { // no need to untrack standard PyExc_* exceptions from TPyErrExceptionsChecker if (auto exceptionPtr = ptr->GetException()) { PyObject_GC_UnTrack(exceptionPtr.Get()); } } } Checkers.clear(); Exceptions.clear(); Module.Drop(); } TExceptionsHolder::TExceptionsHolder() { AddException<std::exception>("yexception"); AddException<TSystemError>("TSystemError", "yexception"); AddException<TIoException>("TIoException", "yexception"); TVector<TString> names(2); names[0] = "TSystemError"; names[1] = "TIoException"; AddException<TIoSystemError>("TIoSystemError", names); AddException<TFileError>("TFileError", "TIoSystemError"); AddException<TBadCastException>("TBadCastException", "yexception"); Checkers.push_back(new TPyErrExceptionsChecker); // XXX: In Python 2.6, PyImport_AppendInittab() function takes non-const char*, this causes // "ISO C++11 does not allow conversion from string literal to 'char *'" warning. static char pybind[] = "pybind"; #if PY_MAJOR_VERSION >= 3 PyImport_AppendInittab(pybind, DoInitPyBindModule); NPrivate::AddFinalizationCallBack([this]() { Clear(); }); #else PyImport_AppendInittab(pybind, DoInitPyBindModule2); #endif } NPyBind::TPyObjectPtr TExceptionsHolder::ToPyException(const std::exception& ex) { for (TCheckersVector::const_reverse_iterator it = Checkers.rbegin(), end = Checkers.rend(); it != end; ++it) { if ((*it)->Check(ex)) return (*it)->GetException(); } return TPyObjectPtr(nullptr); } }