TPySelector.cxx

Go to the documentation of this file.
00001 // Author: Wim Lavrijsen   March 2008
00002 
00003 // Bindings
00004 #include "PyROOT.h"
00005 #include "TPySelector.h"
00006 #include "ObjectProxy.h"
00007 #include "MethodProxy.h"
00008 #include "RootWrapper.h"
00009 #include "MemoryRegulator.h"
00010 
00011 //- ROOT
00012 #include "TPython.h"
00013 #include "TString.h"
00014 
00015 //______________________________________________________________________________
00016 //                      Python equivalent PROOF base class
00017 //                      ==================================
00018 //
00019 // The problem with deriving a python class from a PyROOT bound class and then
00020 // handing it back to a C++ framework, is that the virtual function dispatching
00021 // of C++ is completely oblivious to the methods overridden in python. To work
00022 // within the PROOF (C++) framework, a python class should derive from the class
00023 // TPySelector. This class provides the proper overrides on the C++ side, and
00024 // then forwards them, as apropriate, to python.
00025 //
00026 // This is an example set of scripts:
00027 //
00028 // ### PROOF running script, very close to equivalent .C (prooftest.py)
00029 // import time
00030 // from ROOT import *
00031 //
00032 // dataset = TDSet( 'TTree', 'h42' )
00033 // dataset.Add( 'root:// .... myfile.root' )
00034 //
00035 // proof = TProof.Open('')
00036 // time.sleep(1)                     # needed for GUI to settle
00037 // print dataset.Process( 'TPySelector', 'aapje' )
00038 // ### EOF
00039 //
00040 // ### selector module (aapje.py, name has to match as per above)
00041 // from ROOT import TPySelector
00042 //
00043 // class MyPySelector( TPySelector ):
00044 //    def Begin( self ):
00045 //       print 'py: beginning'
00046 //
00047 //    def SlaveBegin( self, tree ):
00048 //       print 'py: slave beginning'
00049 //
00050 //    def Process( self, entry ):
00051 //       if self.fChain.GetEntry( entry ) <= 0:
00052 //          return 0
00053 //       print 'py: processing', self.fChain.MyVar
00054 //       return 1
00055 //
00056 //   def SlaveTerminate( self ):
00057 //       print 'py: slave terminating'
00058 //
00059 //   def Terminate( self ):
00060 //       print 'py: terminating'
00061 // ### EOF
00062 
00063 
00064 //- data ---------------------------------------------------------------------
00065 ClassImp(TPySelector)
00066 
00067 
00068 //- private helpers ----------------------------------------------------------
00069 void TPySelector::SetupPySelf()
00070 {
00071    if ( fPySelf && fPySelf != Py_None )
00072       return;                      // already created ...
00073 
00074    TString impst = TString::Format( "import %s", GetOption() );
00075 
00076 // use TPython to ensure that the interpreter is initialized
00077    if ( ! TPython::Exec( (const char*)impst ) ) {
00078       Abort( "failed to load provided python module" );  // Exec already printed error trace
00079       return;
00080    }
00081 
00082 // get the TPySelector python class
00083    PyObject* tpysel = PyObject_GetAttrString(
00084       PyImport_AddModule( const_cast< char* >( "libPyROOT" ) ),
00085       const_cast< char* >( "TPySelector" ) );
00086 
00087 // get handle to the module
00088    PyObject* pymod = PyImport_AddModule( const_cast< char* >( GetOption() ) );
00089 
00090 // get the module dictionary to loop over
00091    PyObject* dict = PyModule_GetDict( pymod );
00092    Py_INCREF( dict );
00093 
00094 // locate the TSelector derived class
00095    PyObject* allvalues = PyDict_Values( dict );
00096 
00097    PyObject* pyclass = 0;
00098    for ( int i = 0; i < PyList_GET_SIZE( allvalues ); ++i ) {
00099       PyObject* value = PyList_GET_ITEM( allvalues, i );
00100       Py_INCREF( value );
00101 
00102       if ( PyType_Check( value ) && PyObject_IsSubclass( value, tpysel ) ) {
00103          if ( PyObject_RichCompareBool( value, tpysel, Py_NE ) ) {   // i.e., if not equal
00104             pyclass = value;
00105             break;
00106          }
00107       }
00108 
00109       Py_DECREF( value );
00110    }
00111 
00112    Py_DECREF( allvalues );
00113    Py_DECREF( dict );
00114    Py_DECREF( tpysel );
00115 
00116    if ( ! pyclass ) {
00117       Abort( "no TSelector derived class available in provided module" );
00118       return;
00119    }
00120 
00121    PyObject* args = PyTuple_New( 0 );
00122    PyObject* self = PyObject_Call( pyclass, args, 0 );
00123    Py_DECREF( args );
00124    Py_DECREF( pyclass );
00125 
00126 // final check before declaring success ...
00127    if ( ! self || ! PyROOT::ObjectProxy_Check( self ) ) {
00128       if ( ! PyErr_Occurred() )
00129          PyErr_SetString( PyExc_RuntimeError, "could not create python selector" );
00130       Py_XDECREF( self );
00131       Abort( 0 );
00132       return;
00133    }
00134 
00135 // steal reference to new self, since the deletion will come from the C++ side
00136    Py_XDECREF( fPySelf );
00137    fPySelf = self;
00138 
00139 // inject ourselves into the base of self; destroy old identity if need be (which happens
00140 // if the user calls the default ctor unnecessarily)
00141    TPySelector* oldselector = (TPySelector*)((PyROOT::ObjectProxy*)fPySelf)->fObject;
00142    ((PyROOT::ObjectProxy*)fPySelf)->fObject = this;
00143    if ( oldselector ) {
00144       PyROOT::TMemoryRegulator::UnregisterObject( oldselector );
00145       delete oldselector;
00146    }
00147 }
00148 
00149 //____________________________________________________________________________
00150 PyObject* TPySelector::CallSelf( const char* method, PyObject* pyobject )
00151 {
00152 // Forward <method> to python.
00153    if ( ! fPySelf || fPySelf == Py_None ) {
00154       Py_INCREF( Py_None );
00155       return Py_None;
00156    }
00157 
00158    PyObject* result = 0;
00159 
00160 // get the named method and check for python side overload by not accepting the
00161 // binding's methodproxy
00162    PyObject* pymethod = PyObject_GetAttrString( fPySelf, const_cast< char* >( method ) );
00163    if ( ! PyROOT::MethodProxy_CheckExact( pymethod ) ) {
00164       if ( pyobject )
00165          result = PyObject_CallFunction( pymethod, const_cast< char* >( "O" ), pyobject );
00166       else
00167          result = PyObject_CallFunction( pymethod, const_cast< char* >( "" ) );
00168    } else {
00169    // silently ignore if method not overridden (note that the above can't lead
00170    // to a python exception, since this (TPySelector) class contains the method
00171    // so it is always to be found)
00172       Py_INCREF( Py_None );
00173       result = Py_None;
00174    }
00175 
00176    Py_XDECREF( pymethod );
00177 
00178    if ( ! result )
00179       Abort( 0 );
00180 
00181    return result;
00182 }
00183 
00184 
00185 //- constructors/destructor --------------------------------------------------
00186 TPySelector::TPySelector( TTree*, PyObject* self ) : fChain( 0 ), fPySelf( 0 )
00187 {
00188 // Construct a TSelector derived with <self> as the underlying, which is
00189 // generally 0 to start out with in the current PROOF framework.
00190    if ( self ) {
00191    // steal reference as this is us, as seen from python
00192       fPySelf = self;
00193    } else {
00194       Py_INCREF( Py_None );        // using None allows clearer diagnostics
00195       fPySelf = Py_None;
00196    }
00197 }
00198 
00199 //____________________________________________________________________________
00200 TPySelector::~TPySelector()
00201 {
00202 // Destructor. Only deref if still holding on to Py_None (circular otherwise).
00203    if ( fPySelf == Py_None ) {
00204       Py_DECREF( fPySelf );
00205    }
00206 }
00207 
00208 
00209 //- public functions ---------------------------------------------------------
00210 Int_t TPySelector::Version() const {
00211 // Return version number of this selector. First forward; if not overridden, then
00212 // yield an obvious "undefined" number, 
00213    PyObject* result = const_cast< TPySelector* >( this )->CallSelf( "Version" );
00214    if ( result && result != Py_None ) {
00215       Int_t ires = (Int_t)PyLong_AsLong( result );
00216       Py_DECREF( result );
00217       return ires;
00218    } else if ( result == Py_None ) {
00219       Py_DECREF( result );
00220    }
00221    return -99;
00222 }
00223 
00224 //____________________________________________________________________________
00225 Int_t TPySelector::GetEntry( Long64_t entry, Int_t getall )
00226 {
00227 // Boilerplate get entry; same as for generated code; not forwarded.
00228    return fChain ? fChain->GetTree()->GetEntry( entry, getall ) : 0;
00229 }
00230 
00231 //____________________________________________________________________________
00232 void TPySelector::Init( TTree* tree )
00233 {
00234 // Initialize with the current tree to be used; not forwarded (may be called
00235 // multiple times, and is called from Begin() and SlaveBegin() ).
00236    if ( ! tree )
00237       return;
00238 
00239 // set the fChain beforehand so that the python side may correct if needed
00240    fChain = tree;
00241 
00242 // forward call
00243    PyObject* pytree = PyROOT::BindRootObject( (void*)tree, tree->IsA() );
00244    PyObject* result = CallSelf( "Init", pytree );
00245    Py_DECREF( pytree );
00246 
00247    if ( ! result )
00248       Abort( 0 );
00249 
00250    Py_XDECREF( result );
00251 }
00252 
00253 //____________________________________________________________________________
00254 Bool_t TPySelector::Notify()
00255 {
00256 // Forward call to derived Notify() if available.
00257    PyObject* result = CallSelf( "Notify" );
00258 
00259    if ( ! result )
00260       Abort( 0 );
00261 
00262    Py_XDECREF( result );
00263 
00264 // by default, return kTRUE, b/c the Abort will stop the processing anyway on
00265 // a real error, so if we get here it usually means that there is no Notify()
00266 // override on the python side of things
00267    return kTRUE;
00268 }
00269 
00270 //____________________________________________________________________________
00271 void TPySelector::Begin( TTree* )
00272 {
00273 // First function called, and used to setup the python self; forward call.
00274    SetupPySelf();
00275 
00276 // As per the generated code: the tree argument is deprecated (on PROOF 0 is
00277 // passed), and hence not forwarded.
00278    PyObject* result = CallSelf( "Begin" );
00279 
00280    if ( ! result )
00281       Abort( 0 );
00282 
00283    Py_XDECREF( result );
00284 }
00285 
00286 //____________________________________________________________________________
00287 void TPySelector::SlaveBegin( TTree* tree )
00288 {
00289 // First function called on worker node, needs to make sure python self is setup,
00290 // then store the tree to be used, initialize client, and forward call.
00291    SetupPySelf();
00292    Init( tree );
00293 
00294    PyObject* result = 0;
00295    if ( tree ) {
00296       PyObject* pytree = PyROOT::BindRootObject( (void*)tree, tree->IsA() );
00297       result = CallSelf( "SlaveBegin", pytree );
00298       Py_DECREF( pytree );
00299    } else {
00300       result = CallSelf( "SlaveBegin", Py_None );
00301    }
00302 
00303    if ( ! result )
00304       Abort( 0 );
00305 
00306    Py_XDECREF( result );
00307 }
00308 
00309 //____________________________________________________________________________
00310 Bool_t TPySelector::Process( Long64_t entry )
00311 {
00312 // Actual processing; call is forwarded to python self.
00313    if ( ! fPySelf || fPySelf == Py_None ) {
00314    // would like to set a python error, but can't risk that in case of a
00315    // configuration problem, as it would be absorbed ...
00316 
00317    // simply returning kFALSE will not stop processing; need to set abort
00318       Abort( "no python selector instance available" );
00319       return kFALSE;
00320    }
00321 
00322    PyObject* result = PyObject_CallMethod( fPySelf,
00323       const_cast< char* >( "Process" ), const_cast< char* >( "L" ), entry );
00324    if ( ! result ) {
00325       Abort( 0 );
00326       return kFALSE;
00327    }
00328 
00329    Bool_t bresult = (Bool_t)PyLong_AsLong( result );
00330    Py_DECREF( result );
00331    return bresult;
00332 }
00333 
00334 //____________________________________________________________________________
00335 void TPySelector::SlaveTerminate()
00336 {
00337 // End of client; call is forwarded to python self.
00338    PyObject* result = CallSelf( "SlaveTerminate" );
00339 
00340    if ( ! result )
00341       Abort( 0 );
00342 
00343    Py_XDECREF( result );
00344 }
00345 
00346 //____________________________________________________________________________
00347 void TPySelector::Terminate()
00348 {
00349 // End of job; call is forwarded to python self.
00350    PyObject* result = CallSelf( "Terminate" );
00351 
00352    if ( ! result )
00353       Abort( 0 );
00354 
00355    Py_XDECREF( result );
00356 }
00357 
00358 //____________________________________________________________________________
00359 void TPySelector::Abort( const char* why, EAbort what )
00360 {
00361 // If no 'why' given, read from python error
00362    if ( ! why && PyErr_Occurred() ) {
00363       PyObject *pytype = 0, *pyvalue = 0, *pytrace = 0;
00364       PyErr_Fetch( &pytype, &pyvalue, &pytrace );
00365 
00366    // abort is delayed (done at end of loop, message is current)
00367       PyObject* pystr = PyObject_Str( pyvalue );
00368       Abort( PyROOT_PyUnicode_AsString( pystr ), what );
00369       Py_DECREF( pystr );
00370 
00371       PyErr_Restore( pytype, pyvalue, pytrace );
00372    } else
00373       TSelector::Abort( why ? why : "", what );
00374 }

Generated on Tue Jul 5 14:10:16 2011 for ROOT_528-00b_version by  doxygen 1.5.1