Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
PyMethodBase.cxx
Go to the documentation of this file.
1 // @(#)root/tmva/pymva $Id$
2 // Authors: Omar Zapata, Lorenzo Moneta, Sergei Gleyzer 2015, Stefan Wunsch 2017
3 
4 /**********************************************************************************
5  * Project: TMVA - a Root-integrated toolkit for multivariate data analysis *
6  * Package: TMVA *
7  * Class : PyMethodBase *
8  * *
9  * Description: *
10  * Virtual base class for all MVA method based on python *
11  * *
12  **********************************************************************************/
13 
14 #include <Python.h> // Needs to be included first to avoid redefinition of _POSIX_C_SOURCE
15 #include <TMVA/PyMethodBase.h>
16 
17 #include "TMVA/DataSet.h"
18 #include "TMVA/DataSetInfo.h"
19 #include "TMVA/MsgLogger.h"
20 #include "TMVA/Results.h"
21 #include "TMVA/Timer.h"
22 
23 #include <TApplication.h>
24 
25 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
26 #include <numpy/arrayobject.h>
27 
28 #include <fstream>
29 #include <wchar.h>
30 
31 using namespace TMVA;
32 
33 namespace TMVA {
34 namespace Internal {
35 class PyGILRAII {
36  PyGILState_STATE m_GILState;
37 
38 public:
39  PyGILRAII() : m_GILState(PyGILState_Ensure()) {}
40  ~PyGILRAII() { PyGILState_Release(m_GILState); }
41 };
42 } // namespace Internal
43 } // namespace TMVA
44 
45 ClassImp(PyMethodBase);
46 
47 // NOTE: Introduce here nothing that breaks if multiple instances
48 // of the same method share these objects, e.g., the local namespace.
49 PyObject *PyMethodBase::fModuleBuiltin = NULL;
50 PyObject *PyMethodBase::fEval = NULL;
51 PyObject *PyMethodBase::fOpen = NULL;
52 
53 PyObject *PyMethodBase::fModulePickle = NULL;
54 PyObject *PyMethodBase::fPickleDumps = NULL;
55 PyObject *PyMethodBase::fPickleLoads = NULL;
56 
57 PyObject *PyMethodBase::fMain = NULL;
58 PyObject *PyMethodBase::fGlobalNS = NULL;
59 
60 ///////////////////////////////////////////////////////////////////////////////
61 
62 PyMethodBase::PyMethodBase(const TString &jobName, Types::EMVA methodType, const TString &methodTitle, DataSetInfo &dsi,
63  const TString &theOption)
64  : MethodBase(jobName, methodType, methodTitle, dsi, theOption),
65  fClassifier(NULL)
66 {
67  if (!PyIsInitialized()) {
68  PyInitialize();
69  }
70 
71  // Set up private local namespace for each method instance
72  fLocalNS = PyDict_New();
73  if (!fLocalNS) {
74  Log() << kFATAL << "Can't init local namespace" << Endl;
75  }
76 }
77 
78 ///////////////////////////////////////////////////////////////////////////////
79 
80 PyMethodBase::PyMethodBase(Types::EMVA methodType,
81  DataSetInfo &dsi,
82  const TString &weightFile): MethodBase(methodType, dsi, weightFile),
83  fClassifier(NULL)
84 {
85  if (!PyIsInitialized()) {
86  PyInitialize();
87  }
88 
89  // Set up private local namespace for each method instance
90  fLocalNS = PyDict_New();
91  if (!fLocalNS) {
92  Log() << kFATAL << "Can't init local namespace" << Endl;
93  }
94 }
95 
96 ///////////////////////////////////////////////////////////////////////////////
97 
98 PyMethodBase::~PyMethodBase()
99 {
100 }
101 
102 ///////////////////////////////////////////////////////////////////////////////
103 /// Evaluate Python code
104 ///
105 /// \param[in] code Python code as string
106 /// \return Python object from evaluation of code line
107 ///
108 /// Take a Python code as input and evaluate it in the local namespace. Then,
109 /// return the result as Python object.
110 
111 PyObject *PyMethodBase::Eval(TString code)
112 {
113  if(!PyIsInitialized()) PyInitialize();
114  PyObject *pycode = Py_BuildValue("(sOO)", code.Data(), fGlobalNS, fLocalNS);
115  PyObject *result = PyObject_CallObject(fEval, pycode);
116  Py_DECREF(pycode);
117  return result;
118 }
119 
120 ///////////////////////////////////////////////////////////////////////////////
121 /// Initialize Python interpreter
122 ///
123 /// NOTE: We introduce a shared global namespace `fGlobalNS`, but using
124 /// a private local namespace `fLocalNS`. This prohibits the interference
125 /// of instances of the same method with the same factory, e.g., by overriding
126 /// variables in the same local namespace.
127 
128 void PyMethodBase::PyInitialize()
129 {
130  TMVA::MsgLogger Log;
131 
132  bool pyIsInitialized = PyIsInitialized();
133  if (!pyIsInitialized) {
134  Py_Initialize();
135  }
136 
137  TMVA::Internal::PyGILRAII raii;
138  if (!pyIsInitialized) {
139  _import_array();
140  }
141 
142  fMain = PyImport_AddModule("__main__");
143  if (!fMain) {
144  Log << kFATAL << "Can't import __main__" << Endl;
145  Log << Endl;
146  }
147 
148  fGlobalNS = PyModule_GetDict(fMain);
149  if (!fGlobalNS) {
150  Log << kFATAL << "Can't init global namespace" << Endl;
151  Log << Endl;
152  }
153 
154  #if PY_MAJOR_VERSION < 3
155  //preparing objects for eval
156  PyObject *bName = PyUnicode_FromString("__builtin__");
157  // Import the file as a Python module.
158  fModuleBuiltin = PyImport_Import(bName);
159  if (!fModuleBuiltin) {
160  Log << kFATAL << "Can't import __builtin__" << Endl;
161  Log << Endl;
162  }
163  #else
164  //preparing objects for eval
165  PyObject *bName = PyUnicode_FromString("builtins");
166  // Import the file as a Python module.
167  fModuleBuiltin = PyImport_Import(bName);
168  if (!fModuleBuiltin) {
169  Log << kFATAL << "Can't import builtins" << Endl;
170  Log << Endl;
171  }
172  #endif
173 
174  PyObject *mDict = PyModule_GetDict(fModuleBuiltin);
175  fEval = PyDict_GetItemString(mDict, "eval");
176  fOpen = PyDict_GetItemString(mDict, "open");
177 
178  Py_DECREF(bName);
179  Py_DECREF(mDict);
180  //preparing objects for pickle
181  PyObject *pName = PyUnicode_FromString("pickle");
182  // Import the file as a Python module.
183  fModulePickle = PyImport_Import(pName);
184  if (!fModulePickle) {
185  Log << kFATAL << "Can't import pickle" << Endl;
186  Log << Endl;
187  }
188  PyObject *pDict = PyModule_GetDict(fModulePickle);
189  fPickleDumps = PyDict_GetItemString(pDict, "dump");
190  fPickleLoads = PyDict_GetItemString(pDict, "load");
191 
192  Py_DECREF(pName);
193  Py_DECREF(pDict);
194 }
195 
196 ///////////////////////////////////////////////////////////////////////////////
197 // Finalize Python interpreter
198 
199 void PyMethodBase::PyFinalize()
200 {
201  Py_Finalize();
202  if (fEval) Py_DECREF(fEval);
203  if (fModuleBuiltin) Py_DECREF(fModuleBuiltin);
204  if (fPickleDumps) Py_DECREF(fPickleDumps);
205  if (fPickleLoads) Py_DECREF(fPickleLoads);
206  if(fMain) Py_DECREF(fMain);//objects fGlobalNS and fLocalNS will be free here
207 }
208 
209 ///////////////////////////////////////////////////////////////////////////////
210 /// Set program name for Python interpeter
211 ///
212 /// \param[in] name Program name
213 
214 void PyMethodBase::PySetProgramName(TString name)
215 {
216  #if PY_MAJOR_VERSION < 3
217  Py_SetProgramName(const_cast<char*>(name.Data()));
218  #else
219  Py_SetProgramName((wchar_t *)name.Data());
220  #endif
221 }
222 
223 
224 ///////////////////////////////////////////////////////////////////////////////
225 
226 size_t mystrlen(const char* s) { return strlen(s); }
227 
228 ///////////////////////////////////////////////////////////////////////////////
229 
230 size_t mystrlen(const wchar_t* s) { return wcslen(s); }
231 
232 ///////////////////////////////////////////////////////////////////////////////
233 /// Get program name from Python interpreter
234 ///
235 /// \return Program name
236 
237 TString PyMethodBase::Py_GetProgramName()
238 {
239  auto progName = ::Py_GetProgramName();
240  return std::string(progName, progName + mystrlen(progName));
241 }
242 
243 ///////////////////////////////////////////////////////////////////////////////
244 /// Check Python interpreter initialization status
245 ///
246 /// \return Boolean whether interpreter is initialized
247 
248 int PyMethodBase::PyIsInitialized()
249 {
250  if (!Py_IsInitialized()) return kFALSE;
251  if (!fEval) return kFALSE;
252  if (!fModuleBuiltin) return kFALSE;
253  if (!fPickleDumps) return kFALSE;
254  if (!fPickleLoads) return kFALSE;
255  return kTRUE;
256 }
257 
258 ///////////////////////////////////////////////////////////////////////////////
259 /// Serialize Python object
260 ///
261 /// \param[in] path Path where object is written to file
262 /// \param[in] obj Python object
263 ///
264 /// The input Python object is serialized and written to a file. The Python
265 /// module `pickle` is used to do so.
266 
267 void PyMethodBase::Serialize(TString path, PyObject *obj)
268 {
269  if(!PyIsInitialized()) PyInitialize();
270 
271  PyObject *file_arg = Py_BuildValue("(ss)", path.Data(),"wb");
272  PyObject *file = PyObject_CallObject(fOpen,file_arg);
273  PyObject *model_arg = Py_BuildValue("(OO)", obj,file);
274  PyObject *model_data = PyObject_CallObject(fPickleDumps , model_arg);
275 
276  Py_DECREF(file_arg);
277  Py_DECREF(file);
278  Py_DECREF(model_arg);
279  Py_DECREF(model_data);
280 }
281 
282 ///////////////////////////////////////////////////////////////////////////////
283 /// Unserialize Python object
284 ///
285 /// \param[in] path Path to serialized Python object
286 /// \param[in] obj Python object where the unserialized Python object is loaded
287 /// \return Error code
288 
289 Int_t PyMethodBase::UnSerialize(TString path, PyObject **obj)
290 {
291  // Load file
292  PyObject *file_arg = Py_BuildValue("(ss)", path.Data(),"rb");
293  PyObject *file = PyObject_CallObject(fOpen,file_arg);
294  if(!file) return 1;
295 
296  // Load object from file using pickle
297  PyObject *model_arg = Py_BuildValue("(O)", file);
298  *obj = PyObject_CallObject(fPickleLoads , model_arg);
299  if(!obj) return 2;
300 
301  Py_DECREF(file_arg);
302  Py_DECREF(file);
303  Py_DECREF(model_arg);
304 
305  return 0;
306 }
307 
308 ///////////////////////////////////////////////////////////////////////////////
309 /// Execute Python code from string
310 ///
311 /// \param[in] code Python code as string
312 /// \param[in] errorMessage Error message which shall be shown if the execution fails
313 /// \param[in] start Start symbol
314 ///
315 /// Helper function to run python code from string in local namespace with
316 /// error handling
317 /// `start` defines the start symbol defined in PyRun_String (Py_eval_input,
318 /// Py_single_input, Py_file_input)
319 
320 void PyMethodBase::PyRunString(TString code, TString errorMessage, int start) {
321  fPyReturn = PyRun_String(code, start, fGlobalNS, fLocalNS);
322  if (!fPyReturn) {
323  Log() << kWARNING << "Failed to run python code: " << code << Endl;
324  Log() << kWARNING << "Python error message:" << Endl;
325  PyErr_Print();
326  Log() << kFATAL << errorMessage << Endl;
327  }
328 }