method.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. #pragma once
  2. #define PY_SSIZE_T_CLEAN
  3. #include <Python.h>
  4. #include <util/generic/string.h>
  5. #include <util/generic/map.h>
  6. #include <util/generic/set.h>
  7. #include <util/generic/vector.h>
  8. #include <util/generic/ptr.h>
  9. #include <util/generic/typetraits.h>
  10. #include <util/generic/function.h>
  11. #include "cast.h"
  12. namespace NPyBind {
  13. template <typename TObjType>
  14. class TBaseMethodCaller {
  15. public:
  16. virtual ~TBaseMethodCaller() {
  17. }
  18. virtual bool CallMethod(PyObject* owner, TObjType* self, PyObject* args, PyObject* kwargs, PyObject*& res) const = 0;
  19. virtual bool HasMethod(PyObject*, TObjType*, const TString&, const TSet<TString>&) {
  20. return true;
  21. }
  22. };
  23. template <typename TObjType>
  24. class TIsACaller;
  25. template <typename TObjType>
  26. class TMethodCallers {
  27. private:
  28. typedef TSimpleSharedPtr<TBaseMethodCaller<TObjType>> TCallerPtr;
  29. typedef TVector<TCallerPtr> TCallerList;
  30. typedef TMap<TString, TCallerList> TCallerMap;
  31. const TSet<TString>& HiddenAttrNames;
  32. TCallerMap Callers;
  33. public:
  34. TMethodCallers(const TSet<TString>& hiddenNames)
  35. : HiddenAttrNames(hiddenNames)
  36. {
  37. }
  38. void AddCaller(const TString& name, TCallerPtr caller) {
  39. Callers[name].push_back(caller);
  40. }
  41. bool HasCaller(const TString& name) const {
  42. return Callers.has(name);
  43. }
  44. PyObject* CallMethod(PyObject* owner, TObjType* self, PyObject* args, PyObject* kwargs, const TString& name) const {
  45. const TCallerList* lst = Callers.FindPtr(name);
  46. if (!lst)
  47. return nullptr;
  48. for (const auto& caller : *lst) {
  49. PyObject* res = nullptr;
  50. PyErr_Clear();
  51. if (caller->CallMethod(owner, self, args, kwargs, res))
  52. return res;
  53. }
  54. return nullptr;
  55. }
  56. bool HasMethod(PyObject* owner, TObjType* self, const TString& name) const {
  57. const TCallerList* lst = Callers.FindPtr(name);
  58. if (!lst)
  59. return false;
  60. for (const auto& caller : *lst) {
  61. if (caller->HasMethod(owner, self, name, HiddenAttrNames))
  62. return true;
  63. }
  64. return false;
  65. }
  66. void GetMethodsNames(PyObject* owner, TObjType* self, TVector<TString>& resultNames) const {
  67. for (const auto& it : Callers) {
  68. if (HasMethod(owner, self, it.first) && !HiddenAttrNames.contains(it.first))
  69. resultNames.push_back(it.first);
  70. }
  71. }
  72. void GetAllMethodsNames(TVector<TString>& resultNames) const {
  73. for (const auto& it : Callers) {
  74. resultNames.push_back(it.first);
  75. }
  76. }
  77. void GetPropertiesNames(PyObject*, TObjType* self, TVector<TString>& resultNames) const {
  78. const TCallerList* lst = Callers.FindPtr("IsA");
  79. if (!lst)
  80. return;
  81. for (const auto& caller : *lst) {
  82. TIsACaller<TObjType>* isACaller = dynamic_cast<TIsACaller<TObjType>*>(caller.Get());
  83. if (isACaller) {
  84. resultNames = isACaller->GetPropertiesNames(self);
  85. return;
  86. }
  87. }
  88. }
  89. };
  90. template <typename TObjType>
  91. class TIsACaller: public TBaseMethodCaller<TObjType> {
  92. private:
  93. class TIsAChecker {
  94. public:
  95. virtual ~TIsAChecker() {
  96. }
  97. virtual bool Check(const TObjType* obj) const = 0;
  98. };
  99. template <typename TConcrete>
  100. class TIsAConcreteChecker: public TIsAChecker {
  101. public:
  102. bool Check(const TObjType* obj) const override {
  103. return dynamic_cast<const TConcrete*>(obj) != nullptr;
  104. }
  105. };
  106. typedef TSimpleSharedPtr<TIsAChecker> TCheckerPtr;
  107. typedef TMap<TString, TCheckerPtr> TCheckersMap;
  108. TCheckersMap Checkers;
  109. bool Check(const TString& name, const TObjType* obj) const {
  110. const TCheckerPtr* checker = Checkers.FindPtr(name);
  111. if (!checker) {
  112. PyErr_Format(PyExc_KeyError, "unknown class name: %s", name.data());
  113. return false;
  114. }
  115. return (*checker)->Check(obj);
  116. }
  117. protected:
  118. TIsACaller() {
  119. }
  120. template <typename TConcrete>
  121. void AddChecker(const TString& name) {
  122. Checkers[name] = new TIsAConcreteChecker<TConcrete>;
  123. }
  124. public:
  125. bool CallMethod(PyObject*, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override {
  126. if (args == nullptr || !PyTuple_Check(args))
  127. return false;
  128. size_t cnt = PyTuple_Size(args);
  129. bool result = true;
  130. for (size_t i = 0; i < cnt; ++i) {
  131. result = result && Check(
  132. #if PY_MAJOR_VERSION >= 3
  133. PyUnicode_AsUTF8(
  134. #else
  135. PyString_AsString(
  136. #endif
  137. PyTuple_GetItem(args, i)), self);
  138. }
  139. if (PyErr_Occurred()) {
  140. return false;
  141. }
  142. res = BuildPyObject(result);
  143. return true;
  144. }
  145. TVector<TString> GetPropertiesNames(const TObjType* obj) const {
  146. TVector<TString> names;
  147. for (const auto& it : Checkers) {
  148. if (it.second->Check(obj)) {
  149. names.push_back(it.first);
  150. }
  151. }
  152. return names;
  153. }
  154. };
  155. template <typename TObjType>
  156. class TGenericMethodCaller: public TBaseMethodCaller<TObjType> {
  157. private:
  158. TString AttrName;
  159. public:
  160. TGenericMethodCaller(const TString& attrName)
  161. : AttrName(attrName)
  162. {
  163. }
  164. bool CallMethod(PyObject* obj, TObjType*, PyObject* args, PyObject*, PyObject*& res) const override {
  165. auto str = NameFromString(AttrName);
  166. PyObject* attr = PyObject_GenericGetAttr(obj, str.Get());
  167. if (!attr)
  168. ythrow yexception() << "Can't get generic attribute '" << AttrName << "'";
  169. res = PyObject_CallObject(attr, args);
  170. return res != nullptr;
  171. }
  172. };
  173. template <typename TObjType, typename TSubObject>
  174. class TSubObjectChecker: public TBaseMethodCaller<TObjType> {
  175. public:
  176. ~TSubObjectChecker() override {
  177. }
  178. bool HasMethod(PyObject*, TObjType* self, const TString&, const TSet<TString>&) override {
  179. return dynamic_cast<const TSubObject*>(self) != nullptr;
  180. }
  181. };
  182. template <typename TFunctor, typename Tuple, typename ResType, typename=std::enable_if_t<!std::is_same_v<ResType, void>>>
  183. void ApplyFunctor(TFunctor functor, Tuple resultArgs, PyObject*& res) {
  184. res = BuildPyObject(std::move(Apply(functor, resultArgs)));
  185. }
  186. template <typename TFunctor, typename Tuple, typename ResType, typename=std::enable_if_t<std::is_same_v<ResType, void>>, typename=void>
  187. void ApplyFunctor(TFunctor functor, Tuple resultArgs, PyObject*& res) {
  188. Py_INCREF(Py_None);
  189. res = Py_None;
  190. Apply(functor, resultArgs);
  191. }
  192. template <typename TObjType, typename TResType, typename... Args>
  193. class TFunctorCaller: public TBaseMethodCaller<TObjType> {
  194. using TFunctor = std::function<TResType(TObjType&,Args...)>;
  195. TFunctor Functor;
  196. public:
  197. explicit TFunctorCaller(TFunctor functor):
  198. Functor(functor){}
  199. bool CallMethod(PyObject*, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const {
  200. auto methodArgsTuple = GetArguments<Args...>(args);
  201. auto resultArgs = std::tuple_cat(std::tie(*self), methodArgsTuple);
  202. ApplyFunctor<TFunctor, decltype(resultArgs), TResType>(Functor, resultArgs, res);
  203. return true;
  204. }
  205. };
  206. template <typename TObjType, typename TRealType>
  207. class TGetStateCaller: public TSubObjectChecker<TObjType, TRealType> {
  208. protected:
  209. TPyObjectPtr AddFromCaller(PyObject* obj, const TString& methodName) const {
  210. PyObject* res = PyObject_CallMethod(obj, const_cast<char*>(methodName.c_str()), const_cast<char*>(""));
  211. if (!res) {
  212. PyErr_Clear();
  213. return TPyObjectPtr(Py_None);
  214. }
  215. return TPyObjectPtr(res, true);
  216. }
  217. void GetStandartAttrsDictionary(PyObject* obj, TRealType*, TMap<TString, TPyObjectPtr>& dict) const {
  218. TPyObjectPtr attrsDict(PyObject_GetAttrString(obj, "__dict__"), true);
  219. TMap<TString, TPyObjectPtr> attrs;
  220. if (!FromPyObject(attrsDict.Get(), attrs))
  221. ythrow yexception() << "Can't get '__dict__' attribute";
  222. dict.insert(attrs.begin(), attrs.end());
  223. }
  224. virtual void GetAttrsDictionary(PyObject* obj, TRealType* self, TMap<TString, TPyObjectPtr>& dict) const = 0;
  225. public:
  226. bool CallMethod(PyObject* obj, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override {
  227. if (!ExtractArgs(args))
  228. ythrow yexception() << "Can't parse arguments: it should be none";
  229. TRealType* rself = dynamic_cast<TRealType*>(self);
  230. if (!rself)
  231. return false;
  232. TMap<TString, TPyObjectPtr> dict;
  233. GetAttrsDictionary(obj, rself, dict);
  234. res = BuildPyObject(dict);
  235. return true;
  236. }
  237. };
  238. template <typename TObjType, typename TRealType>
  239. class TSetStateCaller: public TSubObjectChecker<TObjType, TRealType> {
  240. protected:
  241. void SetStandartAttrsDictionary(PyObject* obj, TRealType*, TMap<TString, TPyObjectPtr>& dict) const {
  242. TPyObjectPtr value(BuildPyObject(dict), true);
  243. PyObject_SetAttrString(obj, "__dict__", value.Get());
  244. }
  245. virtual void SetAttrsDictionary(PyObject* obj, TRealType* self, TMap<TString, TPyObjectPtr>& dict) const = 0;
  246. public:
  247. bool CallMethod(PyObject* obj, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override {
  248. TMap<TString, TPyObjectPtr> dict;
  249. if (!ExtractArgs(args, dict))
  250. ythrow yexception() << "Can't parse arguments: it should be one dictionary";
  251. TRealType* rself = dynamic_cast<TRealType*>(self);
  252. if (!rself)
  253. return false;
  254. SetAttrsDictionary(obj, rself, dict);
  255. Py_INCREF(Py_None);
  256. res = Py_None;
  257. return true;
  258. }
  259. };
  260. template <typename TObjType, typename TResult, typename TSubObject, typename TMethod, typename... Args>
  261. class TAnyParameterMethodCaller: public TSubObjectChecker<TObjType, TSubObject> {
  262. private:
  263. TMethod Method;
  264. public:
  265. TAnyParameterMethodCaller(TMethod method)
  266. : Method(method)
  267. {
  268. }
  269. public:
  270. bool CallMethod(PyObject*, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override {
  271. TSubObject* sub = dynamic_cast<TSubObject*>(self);
  272. if (sub == nullptr)
  273. return false;
  274. if (args && (!PyTuple_Check(args) || PyTuple_Size(args) != TFunctionArgs<TMethod>::Length)) {
  275. //ythrow yexception() << "Method takes " << (size_t)(TFunctionArgs<TMethod>::Length) << " arguments, " << PyTuple_Size(args) << " provided";
  276. return false;
  277. }
  278. try {
  279. class Applicant {
  280. public:
  281. TResult operator()(Args... theArgs) {
  282. return (Sub->*Method)(theArgs...);
  283. }
  284. TSubObject* Sub;
  285. TMethod Method;
  286. };
  287. res = BuildPyObject(std::move(Apply(Applicant{sub, Method}, GetArguments<Args...>(args))));
  288. } catch (cast_exception) {
  289. return false;
  290. } catch (...) {
  291. if (PyExc_StopIteration == PyErr_Occurred()) {
  292. // NB: it's replacement for geo_boost::python::throw_error_already_set();
  293. return true;
  294. }
  295. PyErr_SetString(PyExc_RuntimeError, CurrentExceptionMessage().data());
  296. return true;
  297. }
  298. return true;
  299. }
  300. };
  301. template <typename TObjType, typename TSubObject, typename TMethod, typename... Args>
  302. class TAnyParameterMethodCaller<TObjType, void, TSubObject, TMethod, Args...>: public TSubObjectChecker<TObjType, TSubObject> {
  303. private:
  304. TMethod Method;
  305. public:
  306. TAnyParameterMethodCaller(TMethod method)
  307. : Method(method)
  308. {
  309. }
  310. public:
  311. bool CallMethod(PyObject*, TObjType* self, PyObject* args, PyObject*, PyObject*& res) const override {
  312. TSubObject* sub = dynamic_cast<TSubObject*>(self);
  313. if (sub == nullptr) {
  314. return false;
  315. }
  316. if (args && (!PyTuple_Check(args) || PyTuple_Size(args) != TFunctionArgs<TMethod>::Length)) {
  317. return false;
  318. }
  319. try {
  320. class Applicant {
  321. public:
  322. void operator()(Args... theArgs) {
  323. (Sub->*Method)(theArgs...);
  324. }
  325. TSubObject* Sub;
  326. TMethod Method;
  327. };
  328. Apply(Applicant{sub, Method}, GetArguments<Args...>(args));
  329. Py_INCREF(Py_None);
  330. res = Py_None;
  331. } catch (cast_exception) {
  332. return false;
  333. } catch (...) {
  334. PyErr_SetString(PyExc_RuntimeError, CurrentExceptionMessage().data());
  335. return true;
  336. }
  337. return true;
  338. }
  339. };
  340. template <typename TResult, typename TSubObject, typename... Args>
  341. struct TConstTraits {
  342. typedef TResult (TSubObject::*TMethod)(Args... args) const;
  343. };
  344. template <typename TResult, typename TSubObject, typename... Args>
  345. struct TNonConstTraits {
  346. typedef TResult (TSubObject::*TMethod)(Args... args);
  347. };
  348. template <typename TObjType, typename TResult, typename TSubObject, typename TMethod, typename... Args>
  349. class TConstMethodCaller: public TAnyParameterMethodCaller<TObjType, TResult, const TSubObject, typename TConstTraits<TResult, TSubObject, Args...>::TMethod, Args...> {
  350. public:
  351. TConstMethodCaller(typename TConstTraits<TResult, TSubObject, Args...>::TMethod method)
  352. : TAnyParameterMethodCaller<TObjType, TResult, const TSubObject, typename TConstTraits<TResult, TSubObject, Args...>::TMethod, Args...>(method)
  353. {
  354. }
  355. };
  356. template <typename TObjType, typename TResult, typename TSubObject, typename... Args>
  357. TSimpleSharedPtr<TBaseMethodCaller<TObjType>> CreateConstMethodCaller(TResult (TSubObject::*method)(Args...) const) {
  358. return new TConstMethodCaller<TObjType, TResult, TSubObject, TResult (TSubObject::*)(Args...) const, Args...>(method);
  359. }
  360. template <typename TObjType, typename TResType, typename... Args>
  361. TSimpleSharedPtr<TBaseMethodCaller<TObjType>> CreateFunctorCaller(std::function<TResType(TObjType&, Args...)> functor) {
  362. return new TFunctorCaller<TObjType, TResType, Args...>(functor);
  363. }
  364. template <typename TObjType, typename TResult, typename TSubObject, typename TMethod, typename... Args>
  365. class TMethodCaller: public TAnyParameterMethodCaller<TObjType, TResult, TSubObject, typename TNonConstTraits<TResult, TSubObject, Args...>::TMethod, Args...> {
  366. public:
  367. TMethodCaller(typename TNonConstTraits<TResult, TSubObject, Args...>::TMethod method)
  368. : TAnyParameterMethodCaller<TObjType, TResult, TSubObject, typename TNonConstTraits<TResult, TSubObject, Args...>::TMethod, Args...>(method)
  369. {
  370. }
  371. };
  372. template <typename TObjType, typename TResult, typename TSubObject, typename... Args>
  373. TSimpleSharedPtr<TBaseMethodCaller<TObjType>> CreateMethodCaller(TResult (TSubObject::*method)(Args...)) {
  374. return new TMethodCaller<TObjType, TResult, TSubObject, TResult (TSubObject::*)(Args...), Args...>(method);
  375. }
  376. }