Revision 91f15026ca2e94b8aa48b8cc0607f350f345c949 authored by Lorenzo Moneta on 11 August 2006, 15:34:38 UTC, committed by Lorenzo Moneta on 11 August 2006, 15:34:38 UTC

git-svn-id: http://root.cern.ch/svn/root/trunk@15987 27541ba8-7e3a-0410-8455-c3a389f83636
1 parent a745cb7
Raw File
RootModule.cxx
// @(#)root/pyroot:$Name: v5-12-00 $:$Id: RootModule.cxx,v 1.26 2006/07/01 21:19:55 brun Exp $
// Author: Wim Lavrijsen, Apr 2004

// Bindings
#include "PyROOT.h"
#include "PyRootType.h"
#include "ObjectProxy.h"
#include "MethodProxy.h"
#include "PropertyProxy.h"
#include "PyBufferFactory.h"
#include "RootWrapper.h"
#include "Utility.h"

// ROOT
#include "TROOT.h"
#include "TObject.h"

// Standard
#include <string>


//- data -----------------------------------------------------------------------
PyObject* gRootModule = 0;


//- private helpers ------------------------------------------------------------
namespace {

   using namespace PyROOT;

//____________________________________________________________________________
   PyObject* LookupRootEntity( PyObject* pyname, PyObject* args )
   {
      if ( ! ( pyname && PyString_CheckExact( pyname ) ) )
         if ( ! ( args && PyArg_ParseTuple( args, const_cast< char* >( "S" ), &pyname ) ) )
            return 0;

      std::string name = PyString_AS_STRING( pyname );

   // block search for privates
      if ( name.size() <= 2 || name.substr( 0, 2 ) != "__" )
      {
      // 1st attempt: look in myself
         PyObject* attr = PyObject_GetAttr( gRootModule, pyname );
         if ( attr != 0 )
            return attr;

      // 2nd attempt: construct name as a class
         PyErr_Clear();
         attr = MakeRootClassFromString( name );
         if ( attr != 0 )
            return attr;

      // 3rd attempt: lookup name as global variable
         PyErr_Clear();
         attr = GetRootGlobalFromString( name );
         if ( attr != 0 )
            return attr;

      // 4th attempt: find existing object (e.g. from file)
         PyErr_Clear();
         TObject* object = gROOT->FindObject( name.c_str() );
         if ( object != 0 )
            return BindRootObject( object, object->IsA() );
      }

   // still here? raise attribute error
      PyErr_Format( PyExc_AttributeError, "%s", name.c_str() );
      return 0;
   }

//____________________________________________________________________________
   PyDictEntry* RootLookDictString( PyDictObject* mp, PyObject* key, Long_t hash )
   {
   // first search dictionary itself
      PyDictEntry* ep = (*gDictLookupOrg)( mp, key, hash );
      if ( ! ep || ep->me_value != 0 || gDictLookupActive )
         return ep;

   // filter for builtins
      if ( PyDict_GetItem( PyEval_GetBuiltins(), key ) != 0 ) {
         return ep;
      }

   // all failed, start calling into ROOT
      gDictLookupActive = kTRUE;

   // attempt to get ROOT enum/global/class
      PyObject* val = LookupRootEntity( key, 0 );

      if ( val != 0 ) {
      // success ...
         if ( PropertyProxy_Check( val ) ) {
         // pretend something was actually found, but don't add to dictionary
            Py_INCREF( key );
            ep->me_key   = key;
            ep->me_hash  = hash;
            ep->me_value = val->ob_type->tp_descr_get( val, NULL, NULL );
         } else {
         // add reference to ROOT entity in the given dictionary
            ((DictLookup_t&)mp->ma_lookup) = gDictLookupOrg;     // prevent recursion
            if ( PyDict_SetItem( (PyObject*)mp, key, val ) == 0 ) {
               ep = (*gDictLookupOrg)( mp, key, hash );
            } else {
               ep->me_key   = 0;
               ep->me_value = 0;
            }
            ((DictLookup_t&)mp->ma_lookup) = RootLookDictString; // restore
         }

      // done with val
         Py_DECREF( val );
      } else
         PyErr_Clear();

   // stopped calling into ROOT
      gDictLookupActive = kFALSE;

      return ep;
   }

//____________________________________________________________________________
   PyObject* SetRootLazyLookup( PyObject*, PyObject* args )
   {
      PyDictObject* dict = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O!" ), &PyDict_Type, &dict ) )
         return 0;

      ((DictLookup_t&)dict->ma_lookup) = RootLookDictString;

      Py_INCREF( Py_None );
      return Py_None;
   }

//____________________________________________________________________________
   PyObject* MakeRootTemplateClass( PyObject*, PyObject* args )
   {
   // args is class name + template arguments, build full instantiation
      int nArgs = (int)PyTuple_GET_SIZE( args );
      if ( nArgs < 2 ) {
         PyErr_Format( PyExc_TypeError, "too few arguments for template instantiation" );
         return 0;
      }

   // copy initial argument (no check, comes from internal class)
      PyObject* pyname = PyString_FromString(
         PyString_AS_STRING( PyTuple_GET_ITEM( args, 0 ) ) );

   // build "< type, type, ... >" part of class name
      PyString_ConcatAndDel( &pyname, PyString_FromString( "<" ) );
      for ( int i = 1; i < nArgs; ++i ) {
      // add type as string to name
         PyObject* tn = PyTuple_GET_ITEM( args, i );
         if ( PyString_Check( tn ) )
            PyString_Concat( &pyname, tn );
         else if ( PyObject_HasAttrString( tn, const_cast< char* >( "__name__" ) ) ) {
         // this works for type objects
            PyObject* tpName = PyObject_GetAttrString( tn, const_cast< char* >( "__name__" ) );

         // special case for strings
            if ( strcmp( PyString_AS_STRING( tpName ), "str" ) == 0 ) {
               Py_DECREF( tpName );
               tpName = PyString_FromString( "std::string" );
            }

            PyString_ConcatAndDel( &pyname, tpName );
         } else {
         // last ditch attempt, works for things like int values
            PyObject* pystr = PyObject_Str( tn );
            if ( ! pystr ) {
               Py_DECREF( pyname );
               return 0;
            }

            PyString_ConcatAndDel( &pyname, pystr );
         }

      // add a comma, as needed
         if ( i != nArgs - 1 )
            PyString_ConcatAndDel( &pyname, PyString_FromString( "," ) );
      }

   // close template name; prevent '>>', which should be '> >'
      if ( PyString_AsString( pyname )[ PyString_Size( pyname ) - 1 ] == '>' )
         PyString_ConcatAndDel( &pyname, PyString_FromString( " >" ) );
      else
         PyString_ConcatAndDel( &pyname, PyString_FromString( ">" ) );

      std::string name = PyString_AS_STRING( pyname );
      Py_DECREF( pyname );

      return MakeRootClassFromString( name );
   }

//____________________________________________________________________________
   PyObject* AddressOf( PyObject*, PyObject* args )
   {
      ObjectProxy* pyobj = 0;
      PyObject* pyname = 0;
      if ( PyArg_ParseTuple( args, const_cast< char* >( "O|S" ), &pyobj, &pyname ) &&
           ObjectProxy_Check( pyobj ) && pyobj->fObject ) {

         if ( pyname != 0 ) {
         // locate property proxy for offset info
            PropertyProxy* pyprop = 0;

            PyObject* pyclass = PyObject_GetAttrString(
               (PyObject*)pyobj, const_cast< char* >( "__class__" ) );

            if ( pyclass ) {
               PyObject* dict = PyObject_GetAttrString( pyclass, const_cast< char* >( "__dict__" ) );
               pyprop = (PropertyProxy*)PyObject_GetItem( dict, pyname );
               Py_DECREF( dict );
            }
            Py_XDECREF( pyclass );

            if ( PropertyProxy_Check( pyprop ) ) {
            // this is an address of a value (i.e. &myobj->prop)
               PyObject* pybuf = BufFac_t::Instance()->PyBuffer_FromMemory(
                  (Long_t*)pyprop->GetAddress( pyobj ), 1 );
               Py_DECREF( pyprop );
               return pybuf;
            }

            Py_XDECREF( pyprop );

            PyErr_Format( PyExc_TypeError,
               "%s is not a valid data member", PyString_AS_STRING( pyname ) );
            return 0;
         }

      // this is an address of an address (i.e. &myobj, with myobj of type MyObj*)
         return BufFac_t::Instance()->PyBuffer_FromMemory( (Long_t*)&pyobj->fObject, 1 );
      }

      PyErr_SetString( PyExc_ValueError, "invalid argument for AddressOf()" );
      return 0;
   }

//____________________________________________________________________________
   PyObject* MakeNullPointer( PyObject*, PyObject* args )
   {
      int argc = PyTuple_GET_SIZE( args );
      if ( argc != 0 && argc != 1 ) {
         PyErr_Format( PyExc_TypeError,
            "MakeNullPointer takes at most 1 argument (%d given)", argc );
         return 0;
      }

   // no class given, use None as generic
      if ( argc == 0 ) {
         Py_INCREF( Py_None );
         return Py_None;
      }

   // check argument for either string name, or named python object
      PyObject* pyname = PyTuple_GET_ITEM( args, 0 );
      if ( ! PyString_Check( pyname ) ) {
         PyObject* nattr = PyObject_GetAttrString( pyname, "__name__" );
         if ( nattr )
            pyname = nattr;
         pyname = PyObject_Str(	pyname );
         Py_XDECREF( nattr );
      } else {
         Py_INCREF( pyname );
      }

      TClass* klass = gROOT->GetClass( PyString_AS_STRING( pyname ) );
      Py_DECREF( pyname );

      if ( ! klass ) {
         PyErr_SetString( PyExc_TypeError,
            "MakeNullPointer expects a valid class or class name as an argument" );
         return 0;
      }

      return BindRootObjectNoCast( 0, klass, kFALSE );
   }

//____________________________________________________________________________
   PyObject* SetMemoryPolicy( PyObject*, PyObject* args )
   {
      PyObject* policy = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O!" ), &PyInt_Type, &policy ) )
         return 0;

      Long_t l = PyInt_AS_LONG( policy );
      if ( Utility::SetMemoryPolicy( (Utility::EMemoryPolicy)l ) ) {
         Py_INCREF( Py_None );
         return Py_None;
      }

      PyErr_Format( PyExc_ValueError, "Unknown policy %ld", l );
      return 0;
   }

//____________________________________________________________________________
   PyObject* SetSignalPolicy( PyObject*, PyObject* args )
   {
      PyObject* policy = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O!" ), &PyInt_Type, &policy ) )
         return 0;

      Long_t l = PyInt_AS_LONG( policy );
      if ( Utility::SetSignalPolicy( (Utility::ESignalPolicy)l ) ) {
         Py_INCREF( Py_None );
         return Py_None;
      }

      PyErr_Format( PyExc_ValueError, "Unknown policy %ld", l );
      return 0;
   }

//____________________________________________________________________________
   PyObject* SetOwnership( PyObject*, PyObject* args )
   {
      ObjectProxy* pyobj = 0; PyObject* pykeep = 0;
      if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O!O!" ),
                &ObjectProxy_Type, (PyObject*)&pyobj, &PyInt_Type, &pykeep ) )
         return 0;

      (Bool_t)PyLong_AsLong( pykeep ) ? pyobj->HoldOn() : pyobj->Release();

      Py_INCREF( Py_None );
      return Py_None;
   }

} // unnamed namespace


//- data -----------------------------------------------------------------------
static PyMethodDef gPyROOTMethods[] = {
   { (char*) "MakeRootClass", (PyCFunction)PyROOT::MakeRootClass,
     METH_VARARGS, (char*) "PyROOT internal function" },
   { (char*) "GetRootGlobal", (PyCFunction)PyROOT::GetRootGlobal,
     METH_VARARGS, (char*) "PyROOT internal function" },
   { (char*) "LookupRootEntity", (PyCFunction)LookupRootEntity,
     METH_VARARGS, (char*) "PyROOT internal function" },
   { (char*) "SetRootLazyLookup", (PyCFunction)SetRootLazyLookup,
     METH_VARARGS, (char*) "PyROOT internal function" },
   { (char*) "MakeRootTemplateClass", (PyCFunction)MakeRootTemplateClass,
     METH_VARARGS, (char*) "PyROOT internal function" },
   { (char*) "AddressOf", (PyCFunction)AddressOf,
     METH_VARARGS, (char*) "Retrieve address of held object" },
   { (char*) "MakeNullPointer", (PyCFunction)MakeNullPointer,
     METH_VARARGS, (char*) "Create a NULL pointer of the given type" },
   { (char*) "SetMemoryPolicy", (PyCFunction)SetMemoryPolicy,
     METH_VARARGS, (char*) "Determines object ownership model" },
   { (char*) "SetSignalPolicy", (PyCFunction)SetSignalPolicy,
     METH_VARARGS, (char*) "Trap signals in safe mode to prevent interpreter abort" },
   { (char*) "SetOwnership", (PyCFunction)SetOwnership,
     METH_VARARGS, (char*) "Modify held C++ object ownership" },
   { NULL, NULL, 0, NULL }
};


//____________________________________________________________________________
extern "C" void initlibPyROOT()
{
   using namespace PyROOT;

// prepare for lazyness
   PyObject* dict = PyDict_New();
   gDictLookupOrg = (DictLookup_t)((PyDictObject*)dict)->ma_lookup;
   Py_DECREF( dict );

// setup PyROOT
   gRootModule = Py_InitModule( const_cast< char* >( "libPyROOT" ), gPyROOTMethods );
   if ( ! gRootModule )
      return;
   Py_INCREF( gRootModule );

// inject meta type
   if ( ! Utility::InitProxy( gRootModule, &PyRootType_Type, "PyRootType" ) )
      return;

// inject object proxy type
   if ( ! Utility::InitProxy( gRootModule, &ObjectProxy_Type, "ObjectProxy" ) )
      return;

// inject method proxy type
   if ( ! Utility::InitProxy( gRootModule, &MethodProxy_Type, "MethodProxy" ) )
      return;

// inject property proxy type
   if ( ! Utility::InitProxy( gRootModule, &PropertyProxy_Type, "PropertyProxy" ) )
      return;

// policy labels
   PyModule_AddObject( gRootModule, (char*)"kMemoryHeuristics", PyInt_FromLong( 1l ) );
   PyModule_AddObject( gRootModule, (char*)"kMemoryStrict",     PyInt_FromLong( 2l ) );
   PyModule_AddObject( gRootModule, (char*)"kSignalFast",       PyInt_FromLong( 1l ) );
   PyModule_AddObject( gRootModule, (char*)"kSignalSafe",       PyInt_FromLong( 2l ) );

// setup ROOT
   PyROOT::InitRoot();

// signal policy: don't abort interpreter in interactive mode
   Utility::SetSignalPolicy( gROOT->IsBatch() ? Utility::kFast : Utility::kSafe );

// inject ROOT namespace for convenience
   PyModule_AddObject( gRootModule, (char*)"ROOT", MakeRootClassFromString( "ROOT" ) );
}
back to top