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