Logo ROOT   6.21/01
Reference Guide
TPySelector.cxx
Go to the documentation of this file.
1 // Author: Wim Lavrijsen March 2008
2 
3 // Bindings
4 #include "PyROOT.h"
5 #include "TPySelector.h"
6 #include "ObjectProxy.h"
7 #include "MethodProxy.h"
8 #include "RootWrapper.h"
9 #include "TMemoryRegulator.h"
10 
11 //- ROOT
12 #include "TPython.h"
13 #include "TString.h"
14 
15 //______________________________________________________________________________
16 // Python equivalent PROOF base class
17 // ==================================
18 //
19 // The problem with deriving a python class from a PyROOT bound class and then
20 // handing it back to a C++ framework, is that the virtual function dispatching
21 // of C++ is completely oblivious to the methods overridden in python. To work
22 // within the PROOF (C++) framework, a python class should derive from the class
23 // TPySelector. This class provides the proper overrides on the C++ side, and
24 // then forwards them, as apropriate, to python.
25 //
26 // This is an example set of scripts:
27 //
28 // ### PROOF running script, very close to equivalent .C (prooftest.py)
29 // import time
30 // from ROOT import *
31 //
32 // dataset = TDSet( 'TTree', 'h42' )
33 // dataset.Add( 'root:// .... myfile.root' )
34 //
35 // proof = TProof.Open('')
36 // time.sleep(1) # needed for GUI to settle
37 // print dataset.Process( 'TPySelector', 'aapje' )
38 // ### EOF
39 //
40 // ### selector module (aapje.py, name has to match as per above)
41 // from ROOT import TPySelector
42 //
43 // class MyPySelector( TPySelector ):
44 // def Begin( self ):
45 // print 'py: beginning'
46 //
47 // def SlaveBegin( self, tree ):
48 // print 'py: slave beginning'
49 //
50 // def Process( self, entry ):
51 // if self.fChain.GetEntry( entry ) <= 0:
52 // return 0
53 // print 'py: processing', self.fChain.MyVar
54 // return 1
55 //
56 // def SlaveTerminate( self ):
57 // print 'py: slave terminating'
58 //
59 // def Terminate( self ):
60 // print 'py: terminating'
61 // ### EOF
62 
63 
64 //- data ---------------------------------------------------------------------
66 
67 
68 //- private helpers ----------------------------------------------------------
70 {
71 // Install the python side identity of the TPySelector.
72  if ( fPySelf && fPySelf != Py_None )
73  return; // already created ...
74 
75 // split option as needed for the module part and the (optional) user part
76  std::string opt = GetOption();
77  std::string::size_type pos = opt.find( '#' );
78  std::string module = opt.substr( 0, pos );
79  std::string user = (pos == std::string::npos) ? "" : opt.substr( pos+1, std::string::npos );
80 
81  TString impst = TString::Format( "import %s", module.c_str() );
82 
83 // reset user option
84  SetOption( user.c_str() );
85 
86 // use TPython to ensure that the interpreter is initialized
87  if ( ! TPython::Exec( (const char*)impst ) ) {
88  Abort( "failed to load provided python module" ); // Exec already printed error trace
89  return;
90  }
91 
92 // get the TPySelector python class
93  PyObject* tpysel = PyObject_GetAttrString(
94  PyImport_AddModule( const_cast< char* >( "libPyROOT" ) ),
95  const_cast< char* >( "TPySelector" ) );
96 
97 // get handle to the module
98  PyObject* pymod = PyImport_AddModule( const_cast< char* >( module.c_str() ) );
99 
100 // get the module dictionary to loop over
101  PyObject* dict = PyModule_GetDict( pymod );
102  Py_INCREF( dict );
103 
104 // locate the TSelector derived class
105  PyObject* allvalues = PyDict_Values( dict );
106 
107  PyObject* pyclass = 0;
108  for ( int i = 0; i < PyList_GET_SIZE( allvalues ); ++i ) {
109  PyObject* value = PyList_GET_ITEM( allvalues, i );
110  Py_INCREF( value );
111 
112  if ( PyType_Check( value ) && PyObject_IsSubclass( value, tpysel ) ) {
113  if ( PyObject_RichCompareBool( value, tpysel, Py_NE ) ) { // i.e., if not equal
114  pyclass = value;
115  break;
116  }
117  }
118 
119  Py_DECREF( value );
120  }
121 
122  Py_DECREF( allvalues );
123  Py_DECREF( dict );
124  Py_DECREF( tpysel );
125 
126  if ( ! pyclass ) {
127  Abort( "no TSelector derived class available in provided module" );
128  return;
129  }
130 
131  PyObject* args = PyTuple_New( 0 );
132  PyObject* self = PyObject_Call( pyclass, args, 0 );
133  Py_DECREF( args );
134  Py_DECREF( pyclass );
135 
136 // final check before declaring success ...
137  if ( ! self || ! PyROOT::ObjectProxy_Check( self ) ) {
138  if ( ! PyErr_Occurred() )
139  PyErr_SetString( PyExc_RuntimeError, "could not create python selector" );
140  Py_XDECREF( self );
141  Abort( 0 );
142  return;
143  }
144 
145 // steal reference to new self, since the deletion will come from the C++ side
146  Py_XDECREF( fPySelf );
147  fPySelf = self;
148 
149 // inject ourselves into the base of self; destroy old identity if need be (which happens
150 // if the user calls the default ctor unnecessarily)
152  ((PyROOT::ObjectProxy*)fPySelf)->fObject = this;
153  if ( oldselector ) {
155  delete oldselector;
156  }
157 }
158 
159 ////////////////////////////////////////////////////////////////////////////////
160 /// Forward <method> to python.
161 
162 PyObject* TPySelector::CallSelf( const char* method, PyObject* pyobject )
163 {
164  if ( ! fPySelf || fPySelf == Py_None ) {
165  Py_INCREF( Py_None );
166  return Py_None;
167  }
168 
169  PyObject* result = 0;
170 
171 // get the named method and check for python side overload by not accepting the
172 // binding's methodproxy
173  PyObject* pymethod = PyObject_GetAttrString( fPySelf, const_cast< char* >( method ) );
174  if ( ! PyROOT::MethodProxy_CheckExact( pymethod ) ) {
175  if ( pyobject )
176  result = PyObject_CallFunction( pymethod, const_cast< char* >( "O" ), pyobject );
177  else
178  result = PyObject_CallFunction( pymethod, const_cast< char* >( "" ) );
179  } else {
180  // silently ignore if method not overridden (note that the above can't lead
181  // to a python exception, since this (TPySelector) class contains the method
182  // so it is always to be found)
183  Py_INCREF( Py_None );
184  result = Py_None;
185  }
186 
187  Py_XDECREF( pymethod );
188 
189  if ( ! result )
190  Abort( 0 );
191 
192  return result;
193 }
194 
195 
196 //- constructors/destructor --------------------------------------------------
197 TPySelector::TPySelector( TTree*, PyObject* self ) : fChain( 0 ), fPySelf( 0 )
198 {
199 // Construct a TSelector derived with <self> as the underlying, which is
200 // generally 0 to start out with in the current PROOF framework.
201  if ( self ) {
202  // steal reference as this is us, as seen from python
203  fPySelf = self;
204  } else {
205  Py_INCREF( Py_None ); // using None allows clearer diagnostics
206  fPySelf = Py_None;
207  }
208 }
209 
210 ////////////////////////////////////////////////////////////////////////////////
211 /// Destructor. Only deref if still holding on to Py_None (circular otherwise).
212 
214 {
215  if ( fPySelf == Py_None ) {
216  Py_DECREF( fPySelf );
217  }
218 }
219 
220 
221 //- public functions ---------------------------------------------------------
223 // Return version number of this selector. First forward; if not overridden, then
224 // yield an obvious "undefined" number,
225  PyObject* result = const_cast< TPySelector* >( this )->CallSelf( "Version" );
226  if ( result && result != Py_None ) {
227  Int_t ires = (Int_t)PyLong_AsLong( result );
228  Py_DECREF( result );
229  return ires;
230  } else if ( result == Py_None ) {
231  Py_DECREF( result );
232  }
233  return -99;
234 }
235 
236 ////////////////////////////////////////////////////////////////////////////////
237 /// Boilerplate get entry; same as for generated code; not forwarded.
238 
240 {
241  return fChain ? fChain->GetTree()->GetEntry( entry, getall ) : 0;
242 }
243 
244 ////////////////////////////////////////////////////////////////////////////////
245 /// Initialize with the current tree to be used; not forwarded (may be called
246 /// multiple times, and is called from Begin() and SlaveBegin() ).
247 
249 {
250  if ( ! tree )
251  return;
252 
253 // set the fChain beforehand so that the python side may correct if needed
254  fChain = tree;
255 
256 // forward call
257  PyObject* pytree = PyROOT::BindCppObject( (void*)tree, tree->IsA()->GetName() );
258  PyObject* result = CallSelf( "Init", pytree );
259  Py_DECREF( pytree );
260 
261  if ( ! result )
262  Abort( 0 );
263 
264  Py_XDECREF( result );
265 }
266 
267 ////////////////////////////////////////////////////////////////////////////////
268 /// Forward call to derived Notify() if available.
269 
271 {
272  PyObject* result = CallSelf( "Notify" );
273 
274  if ( ! result )
275  Abort( 0 );
276 
277  Py_XDECREF( result );
278 
279 // by default, return kTRUE, b/c the Abort will stop the processing anyway on
280 // a real error, so if we get here it usually means that there is no Notify()
281 // override on the python side of things
282  return kTRUE;
283 }
284 
285 ////////////////////////////////////////////////////////////////////////////////
286 /// First function called, and used to setup the python self; forward call.
287 
289 {
290  SetupPySelf();
291 
292 // As per the generated code: the tree argument is deprecated (on PROOF 0 is
293 // passed), and hence not forwarded.
294  PyObject* result = CallSelf( "Begin" );
295 
296  if ( ! result )
297  Abort( 0 );
298 
299  Py_XDECREF( result );
300 }
301 
302 ////////////////////////////////////////////////////////////////////////////////
303 /// First function called on worker node, needs to make sure python self is setup,
304 /// then store the tree to be used, initialize client, and forward call.
305 
307 {
308  SetupPySelf();
309  Init( tree );
310 
311  PyObject* result = 0;
312  if ( tree ) {
313  PyObject* pytree = PyROOT::BindCppObject( (void*)tree, tree->IsA()->GetName() );
314  result = CallSelf( "SlaveBegin", pytree );
315  Py_DECREF( pytree );
316  } else {
317  result = CallSelf( "SlaveBegin", Py_None );
318  }
319 
320  if ( ! result )
321  Abort( 0 );
322 
323  Py_XDECREF( result );
324 }
325 
326 ////////////////////////////////////////////////////////////////////////////////
327 /// Actual processing; call is forwarded to python self.
328 
330 {
331  if ( ! fPySelf || fPySelf == Py_None ) {
332  // would like to set a python error, but can't risk that in case of a
333  // configuration problem, as it would be absorbed ...
334 
335  // simply returning kFALSE will not stop processing; need to set abort
336  Abort( "no python selector instance available" );
337  return kFALSE;
338  }
339 
340  PyObject* result = PyObject_CallMethod( fPySelf,
341  const_cast< char* >( "Process" ), const_cast< char* >( "L" ), entry );
342  if ( ! result ) {
343  Abort( 0 );
344  return kFALSE;
345  }
346 
347  Bool_t bresult = (Bool_t)PyLong_AsLong( result );
348  Py_DECREF( result );
349  return bresult;
350 }
351 
352 ////////////////////////////////////////////////////////////////////////////////
353 /// End of client; call is forwarded to python self.
354 
356 {
357  PyObject* result = CallSelf( "SlaveTerminate" );
358 
359  if ( ! result )
360  Abort( 0 );
361 
362  Py_XDECREF( result );
363 }
364 
365 ////////////////////////////////////////////////////////////////////////////////
366 /// End of job; call is forwarded to python self.
367 
369 {
370  PyObject* result = CallSelf( "Terminate" );
371 
372  if ( ! result )
373  Abort( 0 );
374 
375  Py_XDECREF( result );
376 }
377 
378 ////////////////////////////////////////////////////////////////////////////////
379 /// If no 'why' given, read from python error
380 
381 void TPySelector::Abort( const char* why, EAbort what )
382 {
383  if ( ! why && PyErr_Occurred() ) {
384  PyObject *pytype = 0, *pyvalue = 0, *pytrace = 0;
385  PyErr_Fetch( &pytype, &pyvalue, &pytrace );
386 
387  // abort is delayed (done at end of loop, message is current)
388  PyObject* pystr = PyObject_Str( pyvalue );
389  Abort( PyROOT_PyUnicode_AsString( pystr ), what );
390  Py_DECREF( pystr );
391 
392  PyErr_Restore( pytype, pyvalue, pytrace );
393  } else
394  TSelector::Abort( why ? why : "", what );
395 }
virtual void Abort(const char *why, EAbort what=kAbortProcess)
If no &#39;why&#39; given, read from python error.
virtual Bool_t Notify()
Forward call to derived Notify() if available.
long long Long64_t
Definition: RtypesCore.h:69
static Bool_t Exec(const char *cmd)
Execute a python statement (e.g. "import ROOT").
Definition: TPython.cxx:353
virtual Int_t GetEntry(Long64_t entry=0, Int_t getall=0)
Read all branches of entry and return total number of bytes read.
Definition: TTree.cxx:5443
Basic string class.
Definition: TString.h:131
virtual Int_t GetEntry(Long64_t entry, Int_t getall=0)
Boilerplate get entry; same as for generated code; not forwarded.
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
virtual void Begin(TTree *tree=0)
First function called, and used to setup the python self; forward call.
virtual Int_t Version() const
PyObject * CallSelf(const char *method, PyObject *pyobject=0)
Forward <method> to python.
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString...
Definition: TString.cxx:2311
virtual const char * GetOption() const
Definition: TSelector.h:59
virtual void SlaveBegin(TTree *tree)
First function called on worker node, needs to make sure python self is setup, then store the tree to...
virtual Bool_t Process(Long64_t entry)
Actual processing; call is forwarded to python self.
virtual void Init(TTree *tree)
Initialize with the current tree to be used; not forwarded (may be called multiple times...
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:78
virtual TTree * GetTree() const
Definition: TTree.h:496
virtual void SlaveTerminate()
End of client; call is forwarded to python self.
TObject * fObject
! Current object if processing object (vs. TTree)
Definition: TSelector.h:42
Bool_t ObjectProxy_Check(T *object)
Definition: ObjectProxy.h:91
virtual void Abort(const char *why, EAbort what=kAbortProcess)
Abort processing.
Definition: TSelector.cxx:116
static Bool_t UnregisterObject(TObject *object)
stop tracking <object>, without notification
PyObject * fPySelf
Definition: TPySelector.h:64
const Bool_t kFALSE
Definition: RtypesCore.h:88
virtual void Terminate()
End of job; call is forwarded to python self.
void SetupPySelf()
Definition: TPySelector.cxx:69
#define ClassImp(name)
Definition: Rtypes.h:365
TPySelector(TTree *=0, PyObject *self=0)
virtual ~TPySelector()
Destructor. Only deref if still holding on to Py_None (circular otherwise).
virtual void SetOption(const char *option)
Definition: TSelector.h:66
Definition: tree.py:1
A TTree represents a columnar dataset.
Definition: TTree.h:72
Bool_t MethodProxy_CheckExact(T *object)
Definition: MethodProxy.h:69
TTree * fChain
Definition: TPySelector.h:35
const Bool_t kTRUE
Definition: RtypesCore.h:87
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, Bool_t isRef=kFALSE)
if the object is a null pointer, return a typed one (as needed for overloading)
_object PyObject
Definition: TPyArg.h:20