Logo ROOT  
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
162PyObject* 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 --------------------------------------------------
197TPySelector::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
381void 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}
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:78
int Int_t
Definition: RtypesCore.h:41
const Bool_t kFALSE
Definition: RtypesCore.h:88
bool Bool_t
Definition: RtypesCore.h:59
long long Long64_t
Definition: RtypesCore.h:69
const Bool_t kTRUE
Definition: RtypesCore.h:87
#define ClassImp(name)
Definition: Rtypes.h:365
_object PyObject
Definition: TPyArg.h:20
static Bool_t UnregisterObject(TObject *object)
stop tracking <object>, without notification
virtual void SlaveTerminate()
End of client; call is forwarded to python self.
virtual void Abort(const char *why, EAbort what=kAbortProcess)
If no 'why' given, read from python error.
virtual Bool_t Process(Long64_t entry)
Actual processing; call is forwarded to python self.
PyObject * CallSelf(const char *method, PyObject *pyobject=0)
Forward <method> to python.
virtual Int_t Version() const
virtual Bool_t Notify()
Forward call to derived Notify() if available.
virtual void Begin(TTree *tree=0)
First function called, and used to setup the python self; forward call.
virtual Int_t GetEntry(Long64_t entry, Int_t getall=0)
Boilerplate get entry; same as for generated code; not forwarded.
TPySelector(TTree *=0, PyObject *self=0)
virtual void Init(TTree *tree)
Initialize with the current tree to be used; not forwarded (may be called multiple times,...
TTree * fChain
Definition: TPySelector.h:35
virtual ~TPySelector()
Destructor. Only deref if still holding on to Py_None (circular otherwise).
PyObject * fPySelf
Definition: TPySelector.h:64
TObject * fObject
! Current object if processing object (vs. TTree)
Definition: TSelector.h:42
virtual void Terminate()
End of job; call is forwarded to python self.
virtual void SlaveBegin(TTree *tree)
First function called on worker node, needs to make sure python self is setup, then store the tree to...
void SetupPySelf()
Definition: TPySelector.cxx:69
static Bool_t Exec(const char *cmd)
Execute a python statement (e.g. "import ROOT").
Definition: TPython.cxx:353
virtual void SetOption(const char *option)
Definition: TSelector.h:66
virtual const char * GetOption() const
Definition: TSelector.h:59
virtual void Abort(const char *why, EAbort what=kAbortProcess)
Abort processing.
Definition: TSelector.cxx:116
Basic string class.
Definition: TString.h:131
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
A TTree represents a columnar dataset.
Definition: TTree.h:72
virtual TTree * GetTree() const
Definition: TTree.h:504
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:5497
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)
Bool_t ObjectProxy_Check(T *object)
Definition: ObjectProxy.h:91
Bool_t MethodProxy_CheckExact(T *object)
Definition: MethodProxy.h:69
Definition: tree.py:1