Commits

Anonymous committed 5f934cb

re-packaging of StoreGate bindings to get leaner dependencies

Comments (0)

Files changed (6)

+2007-12-13  Sebastien Binet  <binet@lblbox>
+
+	* tagging StoreGateBindings-00-00-00
+	* re-packaging of StoreGate bindings to get leaner dependencies
+	
+

StoreGateBindings/selection.xml

+<lcgdict>
+   <class name="StoreGate"/>
+   <class name="StoreGateSvc"/>
+
+   <namespace name="AthenaInternal"/>
+   <function name="AthenaInternal::retrieveObjectFromStore"/>
+   <function name="AthenaInternal::recordObjectToStore"/>
+</lcgdict>
+package StoreGateBindings
+
+author P. Calafiura    <Paolo.Calafiura@cern.ch>
+author Wim Lavrijsen   <WLavrijsen@lbl.gov>
+author Sebastien Binet <binet@cern.ch>
+
+## For Athena policies: it has to be the first use statement
+use AtlasPolicy 	AtlasPolicy-*
+
+## For Gaudi tools, services and objects
+use GaudiInterface 	GaudiInterface-* 	External
+
+## Put here your package dependencies...
+use AtlasROOT	 	AtlasROOT-*		External
+use AtlasPython		AtlasPython-*		External 
+use AtlasPyROOT		AtlasPyROOT-*		External -no_auto_imports
+use AtlasReflex  	AtlasReflex-*  		External
+use SGTools             SGTools-*               Control
+use StoreGate	 	StoreGate-*		Control 
+##
+
+branches src doc python
+
+library StoreGateBindings *.cxx
+apply_pattern installed_library
+
+#apply_pattern component_library
+#apply_pattern declare_joboptions files="*.py"
+apply_pattern declare_python_modules files="*.py"
+
+macro SG_PyROOT_linkopts " -L$(ROOT_home)/lib -lPyROOT"
+macro_append StoreGateBindings_shlibflags " $(SG_PyROOT_linkopts)"
+macro_append StoreGateBindings_shlibflags " $(Python_linkopts)"
+
+private
+#
+# dictionary creation (patterns from Gaudi/SEAL) for bindings
+#
+use AtlasReflex  AtlasReflex-*  External -no_auto_imports
+use AtlasPython  AtlasPython-*  External -no_auto_imports
+
+apply_pattern lcgdict dict=StoreGateBindings \
+   selectionfile=selection.xml \
+   headerfiles="../src/StoreGateBindingsDict.h"
+macro_append StoreGateBindingsDict_shlibflags "  -lStoreGateBindings"
+
+end_private

python/__init__.py

+## hook for the StoreGateBindings py-module

src/StoreGateBindingsDict.h

+///////////////////////// -*- C++ -*- /////////////////////////////
+#ifndef STOREGATEBINDINGS_STOREGATEBINDINGSDICT_H 
+#define STOREGATEBINDINGS_STOREGATEBINDINGSDICT_H  
+
+#include "StoreGate/StoreGateSvc.h"
+#include "StoreGate/StoreGate.h"
+
+struct _object;
+typedef _object PyObject;
+
+namespace AthenaInternal {
+
+   PyObject* retrieveObjectFromStore( StoreGateSvc*, 
+				      PyObject* tp, PyObject* key, void*& res );
+
+   PyObject* recordObjectToStore( StoreGateSvc*, 
+				  PyObject* obj, PyObject* key );
+
+}
+
+#endif // not STOREGATEBINDINGS_STOREGATEBINDINGSDICT_H 

src/StoreGatePyExt.cxx

+// Hide this #include from checkreq so that it doesn't keep giving us warnings.
+#define PYTHON_INC "Python.h"
+#include PYTHON_INC
+
+#include "StoreGate/StoreGateDict.h"
+#include "SGTools/DataBucketBase.h"
+#include "GaudiKernel/Bootstrap.h"
+#include "GaudiKernel/DataObject.h"
+#include "AthenaKernel/IClassIDSvc.h"
+
+// Reflex includes
+#include "Reflex/Type.h"
+#include "Reflex/Object.h"
+
+// ROOT includes
+#include "TClass.h"
+#include "TClassRef.h"
+#include "TROOT.h"
+#include "Api.h"
+
+#include <string>
+
+// temporaryly duplicate PyROOT's API...
+// Wim will fix that up
+
+namespace PyROOT {
+
+class ObjectProxy {
+public:
+  enum EFlags { kNone = 0x0, kIsOwner = 0x0001, kIsReference = 0x0002 };
+
+  TClass* ObjectIsA() const
+  {
+    return fClass.GetClass();                        // may return null
+  }
+
+  void* GetObject() const
+  {
+    if ( fObject && ( fFlags & kIsReference ) )
+      return *(reinterpret_cast< void** >( const_cast< void* >( fObject ) ));
+    else
+      return const_cast< void* >( fObject );        // may be null
+  }
+
+  void HoldOn() { fFlags |= kIsOwner; }
+
+  PyObject_HEAD
+  void*     fObject;
+  TClassRef fClass;
+  int       fFlags;
+};
+
+R__EXTERN PyTypeObject ObjectProxy_Type;
+
+template< typename T >
+inline Bool_t ObjectProxy_Check( T* object )
+{
+  return object && PyObject_TypeCheck( object, &ObjectProxy_Type );
+}
+
+PyObject* BindRootObjectNoCast( void* object, TClass* klass, Bool_t isRef = kFALSE );
+PyObject* BindRootObject( void* object, TClass* klass, Bool_t isRef = kFALSE );
+
+union TParameter;
+
+class TConverter {
+public:
+  virtual ~TConverter() {}
+
+public:
+  virtual Bool_t SetArg( PyObject*, TParameter&, G__CallFunc* = 0 ) = 0;
+  virtual PyObject* FromMemory( void* address );
+  virtual Bool_t ToMemory( PyObject* value, void* address );
+};
+
+TConverter* CreateConverter( const std::string& fullType, Long_t user = -1 );
+
+class PyCallable;
+
+R__EXTERN PyTypeObject MethodProxy_Type;
+
+struct MethodInfo_t;
+
+class MethodProxy
+{
+public:
+  typedef std::map< Long_t, Int_t >  DispatchMap_t;
+  typedef std::vector< PyCallable* > Methods_t;
+
+  void Set( const std::string& name, std::vector< PyCallable* >& methods );
+  const std::string& GetName() const;
+
+public:               // public, as the python C-API works with C structs
+  PyObject_HEAD
+  ObjectProxy*   fSelf;        // The instance it is bound to, or NULL
+  MethodInfo_t*  fMethodInfo;
+};
+
+MethodProxy* MethodProxy_New(
+         const std::string& name, std::vector< PyCallable* >& methods )
+{
+  MethodProxy* pymeth = (MethodProxy*)MethodProxy_Type.tp_new( &MethodProxy_Type, 0, 0 );
+  pymeth->Set( name, methods );
+  return pymeth;
+}
+
+MethodProxy* MethodProxy_New( const std::string& name, PyCallable* method )
+{
+  std::vector< PyCallable* > p;
+  p.push_back( method );
+  return MethodProxy_New( name, p );
+}
+
+
+
+struct MethodInfo_t {
+  std::string                 fName;
+  MethodProxy::DispatchMap_t  fDispatchMap;
+  MethodProxy::Methods_t      fMethods;
+  std::string                 fDoc;
+  Bool_t                      fIsSorted;
+
+  int* fRefCount;
+};
+
+const std::string& MethodProxy::GetName() const { return fMethodInfo->fName; }
+
+class PyCallable {
+public:
+  virtual ~PyCallable() {}
+
+public:
+  virtual PyObject* GetSignature() = 0;
+  virtual PyObject* GetPrototype() = 0;
+  virtual PyObject* GetDocString();
+  virtual Int_t GetPriority();
+
+public:
+  virtual PyObject* operator()( ObjectProxy* self, PyObject* args, PyObject* kwds ) = 0;
+};
+
+} //> namespace PyROOT
+
+namespace SG {
+
+  /**
+   * @brief Concrete @c DataBucket that holds the object via a @c void*
+   *        and uses the Root dictionary to do conversions.
+   *
+   * A concrete @c DataBucket instance holds a pointer to an arbitrary
+   * object, and is able to further convert it to pointers to other
+   * types related by inheritance.  This variant is used for PyAthena,
+   * where don't have the type available at compile time and thus cannot
+   * use templates.  However, we know that we do have the Root dictionary
+   * available for the types, so we can use that information for the
+   * conversions.
+   */
+  class PyDataBucket : public DataBucketBase
+  {
+  public:
+    /**
+     * @brief Constructor.
+     * @param rflxType The Reflex dict. for the held object.
+     * @param pyObj The (pyroot) object proxy to hold.
+     * @param clid The class ID of the wrapped object.
+     */
+    PyDataBucket( const ROOT::Reflex::Type& rflxType, 
+		  PyROOT::ObjectProxy* pyObj,
+		  CLID clid );
+
+
+    /**
+     * @brief Destructor.
+     */
+    ~PyDataBucket() { Py_DECREF( m_pyObj ); }
+    /**
+     * @brief Return the held object.
+     */
+    virtual void* object() { return m_pyObj->GetObject(); }
+
+
+    /**
+     * @brief Return the contents of the @c DataBucket,
+     *        converted to type given by @a clid.  Note that only
+     *        derived->base conversions are allowed here.
+     * @param clid The class ID to which to convert.
+     */
+    virtual void* cast (CLID clid) const;
+
+
+    /**
+     * @brief Return the contents of the @c DataBucket,
+     *        converted to type given by @c std::type_info.  Note that only
+     *        derived->base conversions are allowed here.
+     * @param tinfo The @c std::type_info of the type to which to convert.
+     */
+    virtual void* cast (const std::type_info& tinfo) const;
+
+    /**
+     * @brief Retrieve reference to class definition structure
+     */
+    virtual const CLID& clID() const { return m_clid; }
+
+    /**
+     * @brief Return a new @c DataBucket whose payload has been cloned from the
+     *        original one.
+     */
+    virtual SG::PyDataBucket* clone() const;
+
+  private:
+    /// Pointer to the held pyroot object.
+    PyROOT::ObjectProxy* m_pyObj;
+
+    /// The class ID of the wrapped object.
+    CLID m_clid;
+
+    /// The Reflex dictionary of the held object.
+    ROOT::Reflex::Type m_type;
+
+    /// Pointer to the @ IClassIDSvc to be able to cast objects based
+    /// the @a clid.
+    static IClassIDSvc* s_clidSvc;
+
+    /// Access (and initialize) the pointer to the @c IClassIDSvc
+    static IClassIDSvc* clidSvc();
+  };
+}
+
+PyObject* AthenaInternal::retrieveObjectFromStore(
+      StoreGateSvc* store, PyObject* tp, PyObject* key, void*& res )
+{
+   static IClassIDSvc* s_clidsvc = 0;
+
+// unlikely to happen, but checking is cheap
+   if ( ! store ) {
+      PyErr_SetString( PyExc_RuntimeError, "no store available, is Athena initialized?" );
+      return 0;
+   }
+
+// expect a type or type name and an optional string key
+   PyObject* pyname = 0;
+   if ( ! PyType_Check( tp ) ) {
+      if ( ! PyString_Check( tp ) ) {
+         PyErr_SetString( PyExc_TypeError, "retrieve() argument 1 must be type or class name" );
+         return 0;
+      } else {
+         Py_INCREF( tp );
+         pyname = tp;
+      }
+   } else {
+      pyname = PyObject_GetAttrString( tp, (char*)"__name__" );
+      if ( pyname && ! PyString_Check( pyname ) ) {
+         PyObject* pystr = PyObject_Str( pyname );
+         if ( pystr ) {
+            Py_DECREF( pyname );
+            pyname = pystr;
+         }
+      }
+
+      if ( PyErr_Occurred() )
+         return 0;
+   }
+
+   if ( key != Py_None && ! PyString_Check( key ) ) {
+      PyErr_SetString( PyExc_TypeError, "retrieve() argument 2 must be string key" );
+      return 0;
+   }
+
+// retrieve CLID corresponding to the request
+   if ( ! s_clidsvc ) {
+      StatusCode sc = Gaudi::svcLocator()->getService( "ClassIDSvc", (IService*&)s_clidsvc );
+      if ( sc.isFailure() ) {
+         PyErr_SetString( PyExc_RuntimeError, "ClassIDSvc does not exist" );
+         return 0;
+      }
+   }
+
+   std::string name = PyString_AS_STRING( pyname );
+   CLID id = CLID_NULL;
+   StatusCode sc = s_clidsvc->getIDOfTypeName( name, id );
+   if ( sc.isFailure() ) {
+      PyErr_Format( PyExc_NameError, "ID of %s is unknown", name.c_str() );
+      return 0;
+   }
+
+   SG::DataProxy* proxy = 0;
+   if ( key != Py_None ) {
+      std::string skey = PyString_AS_STRING( key );
+      proxy = store->proxy( id, skey );
+   } else {
+      proxy = store->proxy( id );
+   }
+
+   if ( ! proxy ) {
+      PyErr_Format( PyExc_LookupError, "no proxies for \"%s\"", name.c_str() );
+      return 0;
+   }
+
+// cast proxy to pointer type if needed (setting on return type is evil hack)
+   DataObject* dobj = proxy->accessData();
+   if ( ! dobj ) {
+      PyErr_Format( PyExc_LookupError, "no such object \"%s\"", name.c_str() );
+      return 0;
+   }
+
+   DataBucketBase* dbb = dynamic_cast< DataBucketBase* >( dobj );
+   if ( ! dbb ) {
+      PyErr_SetString( PyExc_TypeError,
+         "unexpected kind of DataObject: can not verify final type" );
+      return 0;
+   }
+
+   CLID realID = dbb->clID();
+   if ( id == realID ) {
+      res = dbb->object();
+
+      if ( ! res ) {
+         PyErr_Format( PyExc_RuntimeError, "found an invalid object" );
+         return 0;
+      }
+
+   } else {
+   // either use CLID BaseInfo<> or Reflex, try both as appropriate
+      res = dbb->cast( id );
+
+      if ( ! res ) {
+         std::string realName;
+         StatusCode sc = s_clidsvc->getTypeNameOfID( realID, name );
+         if ( sc.isFailure() ) {
+            PyErr_Format( PyExc_TypeError, "actual type of CLID %lu unknown",
+                          (long unsigned int)realID );
+            return 0;
+         }
+
+         const ROOT::Reflex::Type& fromType = ROOT::Reflex::Type::ByName( realName );
+
+         if ( (bool)fromType ) {
+            ROOT::Reflex::Object realobj( fromType, dbb->object() );
+
+            const ROOT::Reflex::Type& toType = ROOT::Reflex::Type::ByName( name );
+            const ROOT::Reflex::Object& finalobj = fromType.CastObject( toType, realobj );
+
+            res = (void*)finalobj.Address();
+         }
+
+      }
+
+      if ( ! res ) {
+         PyErr_SetString( PyExc_TypeError, "cast to requested type failed" );
+         return 0;
+      }
+
+   }
+
+   Py_INCREF( Py_None );
+   return Py_None;
+}
+
+PyObject* 
+AthenaInternal::recordObjectToStore( StoreGateSvc* store, 
+				     PyObject* obj, PyObject* key )
+{
+  static IClassIDSvc* s_clidsvc = 0;
+
+  // unlikely to happen, but checking is cheap
+  if ( !store ) {
+    PyErr_SetString( PyExc_RuntimeError, "no store available, is Athena initialized?" );
+    return 0;
+  }
+  
+  // expect a type or type name
+  PyObject* tp = PyObject_GetAttrString( obj, (char*)"__class__" );
+  if ( ! PyType_Check( tp ) ) {
+      PyErr_SetString( PyExc_RuntimeError, 
+		       "could not retrieve type of object" );
+      return 0;
+  }
+  PyObject* pyname = PyObject_GetAttrString( tp, (char*)"__name__" );
+  if ( pyname && ! PyString_Check( pyname ) ) {
+    PyObject* pystr = PyObject_Str( pyname );
+    if ( pystr ) {
+      Py_DECREF( pyname );
+      pyname = pystr;
+    }
+  }
+  if ( PyErr_Occurred() )
+    return 0;
+    
+  if ( ! PyString_Check( key ) ) {
+    PyErr_SetString( PyExc_TypeError, 
+		     "record() argument 2 must be string key" );
+    return 0;
+  }
+  
+  // retrieve CLID corresponding to the request
+  if ( ! s_clidsvc ) {
+    StatusCode sc = Gaudi::svcLocator()->getService( "ClassIDSvc", (IService*&)s_clidsvc );
+    if ( sc.isFailure() ) {
+      PyErr_SetString( PyExc_RuntimeError, "ClassIDSvc does not exist" );
+      return 0;
+    }
+  }
+  
+  std::string name = PyString_AS_STRING( pyname );
+  CLID id = CLID_NULL;
+  StatusCode sc = s_clidsvc->getIDOfTypeName( name, id );
+  if ( sc.isFailure() ) {
+    PyErr_Format( PyExc_NameError, "ID of %s is unknown", name.c_str() );
+    return 0;
+  }
+  
+  PyROOT::ObjectProxy* pyRootObj = (PyROOT::ObjectProxy*)obj;
+  DataObject* dataObj = new SG::PyDataBucket( ROOT::Reflex::Type::ByName(name),
+					      pyRootObj,
+					      id );
+  static bool allowMods = true;
+  if ( !store->typeless_record( dataObj, PyString_AS_STRING(key),
+				pyRootObj->GetObject(),
+				allowMods ).isSuccess() ) {
+      PyErr_SetString( PyExc_RuntimeError, "Failed to record py-dataobject" );
+      return 0;
+  }
+
+  Py_INCREF( Py_None );
+  return Py_None;
+}
+
+//
+namespace SG {
+  IClassIDSvc* PyDataBucket::s_clidSvc = 0;
+  IClassIDSvc* PyDataBucket::clidSvc()
+  {
+    if ( 0 == s_clidSvc ) {
+      Gaudi::svcLocator()->getService( "ClassIDSvc", 
+				       (IService*&)s_clidSvc ).ignore();
+    }
+    return s_clidSvc;
+  }
+
+  PyDataBucket::PyDataBucket( const ROOT::Reflex::Type& rflxType,
+			      PyROOT::ObjectProxy* pyObj,
+			      CLID clid ) :
+    DataBucketBase(),
+    m_pyObj( pyObj ),
+    m_clid ( clid  ),
+    m_type ( rflxType )
+  {
+    // prevent Python from sweeping the rug under our feet
+    Py_INCREF( pyObj );
+  }
+
+  void* PyDataBucket::cast( CLID clid ) const
+  {
+    // if requested type is same than myself ==> no conversion needed
+    if ( clid == m_clid ) {
+      return m_pyObj->GetObject();
+    }
+
+    std::string clidTypeName;
+    StatusCode sc = clidSvc()->getTypeNameOfID(clid, clidTypeName);
+    if ( sc.isFailure() ) {
+      PyErr_Format( PyExc_TypeError, "actual type of CLID %lu unknown",
+		    (long unsigned int)clid );
+      return 0;
+    }
+    ROOT::Reflex::Type toType = ROOT::Reflex::Type::ByName( clidTypeName );
+    if ( !(bool)toType ) {
+      PyErr_Format( PyExc_TypeError, 
+		    "Reflex Type of %s is unknown", clidTypeName.c_str() );
+      return 0;
+    }
+
+    ROOT::Reflex::Object self( m_type, m_pyObj->GetObject() );
+    const ROOT::Reflex::Object& obj = m_type.CastObject( toType, self );
+    return (void*)obj.Address();
+  }
+
+  void* PyDataBucket::cast( const std::type_info& tinfo ) const
+  {
+    // if requested type is same than myself ==> no conversion needed
+    if ( tinfo == m_type.TypeInfo() ) {
+      return m_pyObj->GetObject();
+    }
+
+    ROOT::Reflex::Type toType = ROOT::Reflex::Type::ByTypeInfo( tinfo );
+    if ( !(bool)toType ) {
+      PyErr_Format( PyExc_TypeError, 
+		    "Reflex Type of %s is unknown", tinfo.name() );
+      return 0;
+    }
+
+    ROOT::Reflex::Object self( m_type, m_pyObj->GetObject() );
+    const ROOT::Reflex::Object& obj = m_type.CastObject( toType, self );
+    return (void*)obj.Address();
+  }
+
+  PyDataBucket* PyDataBucket::clone() const
+  { 
+    PyErr_SetString
+      ( PyExc_RuntimeError, 
+	"SG::PyDataBucket::clone() is not meant to be called (yet)" );
+    std::abort();
+    return 0;
+//     new SG::PyDataBucket( m_type, copy<>(m_pyObj), m_clid );
+   }
+
+} //< end namespace SG