Logo ROOT  
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
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} // namespace TMVA
44
46
47// NOTE: Introduce here nothing that breaks if multiple instances
48// of the same method share these objects, e.g., the local namespace.
49PyObject *PyMethodBase::fModuleBuiltin = NULL;
50PyObject *PyMethodBase::fEval = NULL;
51PyObject *PyMethodBase::fOpen = NULL;
52
53PyObject *PyMethodBase::fModulePickle = NULL;
54PyObject *PyMethodBase::fPickleDumps = NULL;
55PyObject *PyMethodBase::fPickleLoads = NULL;
56
57PyObject *PyMethodBase::fMain = NULL;
58PyObject *PyMethodBase::fGlobalNS = NULL;
59
60///////////////////////////////////////////////////////////////////////////////
61
62PyMethodBase::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()) {
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
81 DataSetInfo &dsi,
82 const TString &weightFile): MethodBase(methodType, dsi, weightFile),
83 fClassifier(NULL)
84{
85 if (!PyIsInitialized()) {
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
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
112{
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
129{
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
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
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
226size_t mystrlen(const char* s) { return strlen(s); }
227
228///////////////////////////////////////////////////////////////////////////////
229
230size_t mystrlen(const wchar_t* s) { return wcslen(s); }
231
232///////////////////////////////////////////////////////////////////////////////
233/// Get program name from Python interpreter
234///
235/// \return Program name
236
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
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
268{
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
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
320void 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}
size_t mystrlen(const char *s)
_object PyObject
Definition: PyMethodBase.h:41
const Bool_t kFALSE
Definition: RtypesCore.h:90
const Bool_t kTRUE
Definition: RtypesCore.h:89
#define ClassImp(name)
Definition: Rtypes.h:361
char name[80]
Definition: TGX11.cxx:109
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:111
ostringstream derivative to redirect and format output
Definition: MsgLogger.h:59
PyObject * fPyReturn
Definition: PyMethodBase.h:113
static int PyIsInitialized()
Check Python interpreter initialization status.
static PyObject * fOpen
Definition: PyMethodBase.h:121
static PyObject * fPickleDumps
Definition: PyMethodBase.h:125
PyObject * Eval(TString code)
Evaluate Python code.
static TString Py_GetProgramName()
Get program name from Python interpreter.
static PyObject * fMain
Definition: PyMethodBase.h:128
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:126
static void PySetProgramName(TString name)
Set program name for Python interpeter.
virtual ~PyMethodBase()
static PyObject * fGlobalNS
Definition: PyMethodBase.h:129
static PyObject * fModulePickle
Definition: PyMethodBase.h:124
static PyObject * fModuleBuiltin
Definition: PyMethodBase.h:119
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:120
PyObject * fLocalNS
Definition: PyMethodBase.h:130
Basic string class.
Definition: TString.h:131
const char * Data() const
Definition: TString.h:364
static constexpr double s
create variable transformations
MsgLogger & Endl(MsgLogger &ml)
Definition: MsgLogger.h:158
Definition: file.py:1