Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
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#include "TMVA/Tools.h"
23
24#include "TSystem.h"
25
26#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
27#include <numpy/arrayobject.h>
28
29#include <cwchar>
30
31using namespace TMVA;
32
33namespace TMVA {
34namespace Internal {
35class PyGILRAII {
36 PyGILState_STATE m_GILState;
37
38public:
39 PyGILRAII() : m_GILState(PyGILState_Ensure()) {}
40 ~PyGILRAII() { PyGILState_Release(m_GILState); }
41};
42} // namespace Internal
43
44/// get current Python executable used by ROOT
46 TString python_version = gSystem->GetFromPipe("root-config --python-version");
47 if (python_version.IsNull()) {
48 TMVA::gTools().Log() << kFATAL << "Can't find a valid Python version used to build ROOT" << Endl;
49 return nullptr;
50 }
51 if(python_version[0] == '2')
52 return "python";
53 else if (python_version[0] == '3')
54 return "python3";
55
56 TMVA::gTools().Log() << kFATAL << "Invalid Python version used to build ROOT : " << python_version << Endl;
57 return nullptr;
58}
59
60} // namespace TMVA
61
63
64// NOTE: Introduce here nothing that breaks if multiple instances
65// of the same method share these objects, e.g., the local namespace.
69
73
76
77///////////////////////////////////////////////////////////////////////////////
78
79PyMethodBase::PyMethodBase(const TString &jobName, Types::EMVA methodType, const TString &methodTitle, DataSetInfo &dsi,
80 const TString &theOption)
81 : MethodBase(jobName, methodType, methodTitle, dsi, theOption),
82 fClassifier(NULL)
83{
84 if (!PyIsInitialized()) {
86 }
87
88 // Set up private local namespace for each method instance
89 fLocalNS = PyDict_New();
90 if (!fLocalNS) {
91 Log() << kFATAL << "Can't init local namespace" << Endl;
92 }
93}
94
95///////////////////////////////////////////////////////////////////////////////
96
98 DataSetInfo &dsi,
99 const TString &weightFile): MethodBase(methodType, dsi, weightFile),
100 fClassifier(NULL)
101{
102 if (!PyIsInitialized()) {
103 PyInitialize();
104 }
105
106 // Set up private local namespace for each method instance
107 fLocalNS = PyDict_New();
108 if (!fLocalNS) {
109 Log() << kFATAL << "Can't init local namespace" << Endl;
110 }
111}
112
113///////////////////////////////////////////////////////////////////////////////
114
116{
117 // should we delete here fLocalNS ?
118}
119
120///////////////////////////////////////////////////////////////////////////////
121/// Evaluate Python code
122///
123/// \param[in] code Python code as string
124/// \return Python object from evaluation of code line
125///
126/// Take a Python code as input and evaluate it in the local namespace. Then,
127/// return the result as Python object.
128
130{
132 PyObject *pycode = Py_BuildValue("(sOO)", code.Data(), fGlobalNS, fLocalNS);
133 PyObject *result = PyObject_CallObject(fEval, pycode);
134 Py_DECREF(pycode);
135 return result;
136}
137
138///////////////////////////////////////////////////////////////////////////////
139/// Initialize Python interpreter
140///
141/// NOTE: We introduce a shared global namespace `fGlobalNS`, but using
142/// a private local namespace `fLocalNS`. This prohibits the interference
143/// of instances of the same method with the same factory, e.g., by overriding
144/// variables in the same local namespace.
145
147{
149
150 bool pyIsInitialized = PyIsInitialized();
151 if (!pyIsInitialized) {
152 Py_Initialize();
153 }
154
156 if (!pyIsInitialized) {
157 _import_array();
158 }
159
160 // note fMain is a borrowed reference
161 fMain = PyImport_AddModule("__main__");
162 if (!fMain) {
163 Log << kFATAL << "Can't import __main__" << Endl;
164 Log << Endl;
165 }
166 Py_INCREF(fMain);
167
168 fGlobalNS = PyModule_GetDict(fMain);
169 if (!fGlobalNS) {
170 Log << kFATAL << "Can't init global namespace" << Endl;
171 Log << Endl;
172 }
173 Py_INCREF(fGlobalNS);
174
175 #if PY_MAJOR_VERSION < 3
176 //preparing objects for eval
177 PyObject *bName = PyUnicode_FromString("__builtin__");
178 // Import the file as a Python module.
179 // returns a new reference
180 fModuleBuiltin = PyImport_Import(bName);
181 if (!fModuleBuiltin) {
182 Log << kFATAL << "Can't import __builtin__" << Endl;
183 Log << Endl;
184 }
185 #else
186 //preparing objects for eval
187 PyObject *bName = PyUnicode_FromString("builtins");
188 // Import the file as a Python module.
189 fModuleBuiltin = PyImport_Import(bName);
190 if (!fModuleBuiltin) {
191 Log << kFATAL << "Can't import builtins" << Endl;
192 Log << Endl;
193 }
194 #endif
195
196 // note mDict is a borrowed reference
197 PyObject *mDict = PyModule_GetDict(fModuleBuiltin);
198 fEval = PyDict_GetItemString(mDict, "eval");
199 fOpen = PyDict_GetItemString(mDict, "open");
200 // fEval and fOpen are borrowed referencers and we need to keep them alive
201 if (fEval) Py_INCREF(fEval);
202 if (fOpen) Py_INCREF(fOpen);
203
204 // bName is a new reference (from PyUnicode_FromString)
205 Py_DECREF(bName);
206
207 //preparing objects for pickle
208 PyObject *pName = PyUnicode_FromString("pickle");
209 // Import the file as a Python module.
210 // return object is a new reference !
211 fModulePickle = PyImport_Import(pName);
212 if (!fModulePickle) {
213 Log << kFATAL << "Can't import pickle" << Endl;
214 Log << Endl;
215 }
216 PyObject *pDict = PyModule_GetDict(fModulePickle);
217 // note the following return objects are borrowed references
218 fPickleDumps = PyDict_GetItemString(pDict, "dump");
219 fPickleLoads = PyDict_GetItemString(pDict, "load");
220 if (fPickleDumps) Py_INCREF(fPickleDumps);
221 if (fPickleLoads) Py_INCREF(fPickleLoads);
222
223 Py_DECREF(pName);
224}
225
226///////////////////////////////////////////////////////////////////////////////
227// Finalize Python interpreter
228
230{
231 if (fEval) Py_DECREF(fEval);
232 if (fOpen) Py_DECREF(fOpen);
233 if (fModuleBuiltin) Py_DECREF(fModuleBuiltin);
234 if (fPickleDumps) Py_DECREF(fPickleDumps);
235 if (fPickleLoads) Py_DECREF(fPickleLoads);
236 if(fMain) Py_DECREF(fMain);//objects fGlobalNS and fLocalNS will be free here
237 if (fGlobalNS) Py_DECREF(fGlobalNS);
238 Py_Finalize();
239}
240
241///////////////////////////////////////////////////////////////////////////////
242/// Set program name for Python interpeter
243///
244/// \param[in] name Program name
245
247{
248 #if PY_MAJOR_VERSION < 3
249 Py_SetProgramName(const_cast<char*>(name.Data()));
250 #else
251 Py_SetProgramName((wchar_t *)name.Data());
252 #endif
253}
254
255
256///////////////////////////////////////////////////////////////////////////////
257
258size_t mystrlen(const char* s) { return strlen(s); }
259
260///////////////////////////////////////////////////////////////////////////////
261
262size_t mystrlen(const wchar_t* s) { return wcslen(s); }
263
264///////////////////////////////////////////////////////////////////////////////
265/// Get program name from Python interpreter
266///
267/// \return Program name
268
270{
271 auto progName = ::Py_GetProgramName();
272 return std::string(progName, progName + mystrlen(progName));
273}
274
275///////////////////////////////////////////////////////////////////////////////
276/// Check Python interpreter initialization status
277///
278/// \return Boolean whether interpreter is initialized
279
281{
282 if (!Py_IsInitialized()) return kFALSE;
283 if (!fEval) return kFALSE;
284 if (!fModuleBuiltin) return kFALSE;
285 if (!fPickleDumps) return kFALSE;
286 if (!fPickleLoads) return kFALSE;
287 return kTRUE;
288}
289
290///////////////////////////////////////////////////////////////////////////////
291/// Serialize Python object
292///
293/// \param[in] path Path where object is written to file
294/// \param[in] obj Python object
295///
296/// The input Python object is serialized and written to a file. The Python
297/// module `pickle` is used to do so.
298
300{
302
303 PyObject *file_arg = Py_BuildValue("(ss)", path.Data(),"wb");
304 PyObject *file = PyObject_CallObject(fOpen,file_arg);
305 PyObject *model_arg = Py_BuildValue("(OO)", obj,file);
306 PyObject *model_data = PyObject_CallObject(fPickleDumps , model_arg);
307
308 Py_DECREF(file_arg);
309 Py_DECREF(file);
310 Py_DECREF(model_arg);
311 Py_DECREF(model_data);
312}
313
314///////////////////////////////////////////////////////////////////////////////
315/// Unserialize Python object
316///
317/// \param[in] path Path to serialized Python object
318/// \param[in] obj Python object where the unserialized Python object is loaded
319/// \return Error code
320
322{
323 // Load file
324 PyObject *file_arg = Py_BuildValue("(ss)", path.Data(),"rb");
325 PyObject *file = PyObject_CallObject(fOpen,file_arg);
326 if(!file) return 1;
327
328 // Load object from file using pickle
329 PyObject *model_arg = Py_BuildValue("(O)", file);
330 *obj = PyObject_CallObject(fPickleLoads , model_arg);
331 if(!obj) return 2;
332
333 Py_DECREF(file_arg);
334 Py_DECREF(file);
335 Py_DECREF(model_arg);
336
337 return 0;
338}
339
340///////////////////////////////////////////////////////////////////////////////
341/// Execute Python code from string
342///
343/// \param[in] code Python code as string
344/// \param[in] errorMessage Error message which shall be shown if the execution fails
345/// \param[in] start Start symbol
346///
347/// Helper function to run python code from string in local namespace with
348/// error handling
349/// `start` defines the start symbol defined in PyRun_String (Py_eval_input,
350/// Py_single_input, Py_file_input)
351
352void PyMethodBase::PyRunString(TString code, TString errorMessage, int start) {
353 fPyReturn = PyRun_String(code, start, fGlobalNS, fLocalNS);
354 if (!fPyReturn) {
355 Log() << kWARNING << "Failed to run python code: " << code << Endl;
356 Log() << kWARNING << "Python error message:" << Endl;
357 PyErr_Print();
358 Log() << kFATAL << errorMessage << Endl;
359 }
360}
361
362///////////////////////////////////////////////////////////////////////////////
363/// Execute Python code from string
364///
365/// \param[in] code Python code as string
366/// \param[in] globalNS Global Namespace for Python Session
367/// \param[in] localNS Local Namespace for Python Session
368///
369/// Overloaded static Helper function to run python code
370/// from string and throw runtime error if the Python session
371/// is unable to execute the code
372
373void PyMethodBase::PyRunString(TString code, PyObject *globalNS, PyObject *localNS){
374 PyObject *fPyReturn = PyRun_String(code, Py_single_input, globalNS, localNS);
375 if (!fPyReturn) {
376 std::cout<<"\nPython error message:\n";
377 PyErr_Print();
378 throw std::runtime_error("\nFailed to run python code: "+code);
379 }
380}
381
382///////////////////////////////////////////////////////////////////////////////
383/// Returns `const char*` from Python string in PyObject
384///
385/// \param[in] string Python String object
386/// \return String representation in `const char*`
387
389 PyObject* encodedString = PyUnicode_AsUTF8String(string);
390 const char* cstring = PyBytes_AsString(encodedString);
391 return cstring;
392}
393
394//////////////////////////////////////////////////////////////////////////////////
395/// \brief Utility function which retrieves and returns the values of the Tuple
396/// object as a vector of size_t
397///
398/// \param[in] tupleObject Python Tuple object
399/// \return vector of tuple members
400
401std::vector<size_t> PyMethodBase::GetDataFromTuple(PyObject* tupleObject){
402 std::vector<size_t>tupleVec;
403 for(Py_ssize_t tupleIter=0;tupleIter<PyTuple_Size(tupleObject);++tupleIter){
404 auto itemObj = PyTuple_GetItem(tupleObject,tupleIter);
405 if (itemObj == Py_None)
406 tupleVec.push_back(0); // case shape is for example (None,2,3)
407 else
408 tupleVec.push_back((size_t)PyLong_AsLong(itemObj));
409 }
410 return tupleVec;
411}
412
413//////////////////////////////////////////////////////////////////////////////////
414/// \brief Utility function which retrieves and returns the values of the List
415/// object as a vector of size_t
416///
417/// \param[in] listObject Python List object
418/// \return vector of list members
419
420std::vector<size_t> PyMethodBase::GetDataFromList(PyObject* listObject){
421 std::vector<size_t>listVec;
422 for(Py_ssize_t listIter=0; listIter<PyList_Size(listObject);++listIter){
423 listVec.push_back((size_t)PyLong_AsLong(PyList_GetItem(listObject,listIter)));
424 }
425 return listVec;
426}
#define PyBytes_AsString
Definition CPyCppyy.h:86
size_t mystrlen(const char *s)
_object PyObject
#define Py_single_input
const Bool_t kFALSE
Definition RtypesCore.h:101
const Bool_t kTRUE
Definition RtypesCore.h:100
#define ClassImp(name)
Definition Rtypes.h:364
char name[80]
Definition TGX11.cxx:110
R__EXTERN TSystem * gSystem
Definition TSystem.h:559
MsgLogger & Log() const
Class that contains all the data information.
Definition DataSetInfo.h:62
Virtual base Class for all MVA method.
Definition MethodBase.h:111
ostringstream derivative to redirect and format output
Definition MsgLogger.h:57
static std::vector< size_t > GetDataFromTuple(PyObject *tupleObject)
Utility function which retrieves and returns the values of the Tuple object as a vector of size_t.
static int PyIsInitialized()
Check Python interpreter initialization status.
static std::vector< size_t > GetDataFromList(PyObject *listObject)
Utility function which retrieves and returns the values of the List object as a vector of size_t.
static PyObject * fOpen
static PyObject * fPickleDumps
PyObject * Eval(TString code)
Evaluate Python code.
static TString Py_GetProgramName()
Get program name from Python interpreter.
static PyObject * fMain
static void PyInitialize()
Initialize Python interpreter.
static void Serialize(TString file, PyObject *classifier)
Serialize Python object.
static void PyFinalize()
static Int_t UnSerialize(TString file, PyObject **obj)
Unserialize Python object.
static const char * PyStringAsString(PyObject *string)
Returns const char* from Python string in PyObject.
static PyObject * fPickleLoads
static void PySetProgramName(TString name)
Set program name for Python interpeter.
static PyObject * fGlobalNS
static PyObject * fModulePickle
static PyObject * fModuleBuiltin
void PyRunString(TString code, TString errorMessage="Failed to run python code", int start=Py_single_input)
Execute Python code from string.
PyMethodBase(const TString &jobName, Types::EMVA methodType, const TString &methodTitle, DataSetInfo &dsi, const TString &theOption="")
static PyObject * fEval
MsgLogger & Log() const
Definition Tools.h:228
Basic string class.
Definition TString.h:136
const char * Data() const
Definition TString.h:369
Bool_t IsNull() const
Definition TString.h:407
virtual TString GetFromPipe(const char *command)
Execute command and return output in TString.
Definition TSystem.cxx:683
create variable transformations
TString Python_Executable()
Function to find current Python executable used by ROOT If Python2 is installed return "python" Inste...
Tools & gTools()
MsgLogger & Endl(MsgLogger &ml)
Definition MsgLogger.h:148
Definition file.py:1