Commits

Anonymous committed 60f8f94

almost working PyAthena + added tests

  • Participants
  • Parent commits 291efd7

Comments (0)

Files changed (9)

+2007-12-18  Sebastien Binet  <binet@lblbox>
+
+	* tagging AthenaPython-00-00-03
+	* almost working PyAthena
+	* added tests
+	* TODO: fix AlgTools' bloodline (private/public tools, again!)
+	* M cmt/requirements
+	* M python/Configurables.py
+	* M python/PyAthena.py
+	* M src/PyComponentMgr.cxx
+	* M src/PyComponentMgr.h
+	* A python/tests/PyTestsLib.py
+	* A python/tests/__init__.py
+	* A share/tests/test_pyathena.py
+
 2007-12-14  Sebastien Binet  <binet@lblbox>
 
 	* tagging AthenaPython-00-00-02
 
 ## Put here your package dependencies...
 use AtlasPython 	AtlasPython-*  		External
+use AtlasROOT		AtlasROOT-*		External -no_auto_imports
 
 use AthenaKernel 	AthenaKernel-*		Control
-
-use CLIDSvc        	CLIDSvc-*           	Control
-use CLIDComps		CLIDComps-*		Control 
-
 use SGTools 	   	SGTools-*           	Control
 use StoreGate		StoreGate-*		Control
 ##
 
 branches AthenaPython src src/components python
 
+## make the library private so nobody can link against
+private
 library AthenaPython *.cxx components/*.cxx
 apply_pattern component_library
-apply_pattern declare_python_modules files="*.py"
+apply_pattern declare_python_modules files="*.py tests"
+apply_pattern declare_joboptions files="*.py tests/*.py"
 
+macro AthenaPython_PyROOT_linkopts " -L$(ROOT_home)/lib -lPyROOT"
+macro_append AthenaPython_linkopts " $(AthenaPython_PyROOT_linkopts)"
+end_private
 
 #
 # dictionary creation (patterns from Gaudi/SEAL) for bindings

python/Configurables.py

 # @author: Sebastien Binet <binet@cern.ch>
 
 import AthenaCommon.Constants as Lvl
-from AthenaCommon.Configurable import (ConfigurablePyAlgorithm,
-                                       ConfigurableAlgTool,
-                                       ConfigurableService)
+from AthenaCommon.Configurable import *
+from AthenaCommon import CfgMgr
+
 ### 
 class PyComponents(object):
     """@c PyComponents is a placeholder where all factories for the python
     components will be collected and stored for easy look-up and call from
     the C++ side.
-    The @a PyComponents.instances dictionary will store for each instance name:
-      - the package name where the class of the component to be instantiated is
-        defined
-      - the class name of the component to be associated
-      - the arguments (and values) to pass to the __init__ method to properly
-        create the python component.
+    The @a PyComponents.instances dictionary will store the instance
     e.g.:
-     PyComponents.instances[ 'alg1' ] = {
-        'package' : 'MyAnaPkg',
-        'class'   : 'MyAna.MyPyAlg',
-        'kwargs'  : { 'OutputLevel' : 1,
-                      'EtaCut'      : 2.5,
-                      'PtCut'       : 20*GeV,
-                      }
-     }
+     PyComponents.instances[ 'alg1' ] = <PyAthena::Alg/alg1 instance>
     All this boilerplate code will of course be done automatically for the user
     as soon as she uses the (python) @PyConfigurable classes.
     """
     instances = {}
     pass
 
-from AthenaCommon import CfgMgr
+### Configurable base class for PyAlgorithms ----------------------------------
+class CfgPyAlgorithm( ConfigurableAlgorithm ):
+    def __init__( self, name, **kw ):
+        super( CfgPyAlgorithm, self ).__init__(name)
+        for n,v in kw.items():
+            setattr(self,n,v)
+    def getGaudiType( self ): return 'Algorithm'
+    def getType(self):        return 'PyAthena::Alg'
+    def getDlls(self):        return 'AthenaPython'
+    def getHandle(self):      return None
+    def setup(self):
+        from AthenaCommon import CfgMgr
+        from AthenaCommon.AppMgr import ServiceMgr as svcMgr
+        if not hasattr( svcMgr, 'PyComponentMgr' ):
+            svcMgr += CfgMgr.PyAthena__PyComponentMgr('PyComponentMgr')
+        
+        ## populate the PyComponents instances repository
+        name = self.getName()
+        if name in PyComponents.instances:
+            raise RuntimeError(
+                "A python component of name [%s] has already been registered "\
+                "with the PyComponents registry !" % name
+                )
+        PyComponents.instances[name] = self
+        return super(CfgPyAlgorithm, self).setup()
 
-### Configurable base class for PyAlgorithms ---------------------------------
-_PyAlg = CfgMgr.PyAthena__Alg
-class CfgPyAlgorithm(_PyAlg):
-    __slots__ = ( '_pyPkg', '_pyKlass', )
-
-    def __init__(self, name, pkg, klass, **kw):
-        # init base class
-        kw['name'] = name
-        super(CfgPyAlgorithm, self).__init__( **kw )
-
-        self._pyPkg   = pkg
-        self._pyKlass = klass
-
-    def setDefaults(cls, handle):
-
-        if not isinstance(handle, CfgPyAlgorithm):
-            return
-
-        print "."*80
-        import PyAthena
-        name = handle.getName()
-        if name in PyComponents.instances:
-           #print "???????????"
-           return
-        #PyComponents.instances[name] = "%s/%s" % ( pyKlass, name )
-        exec( 'from %s import %s' % (handle._pyPkg,
-                                     handle._pyKlass.split('.')[0]) )
-        exec( 'PyComponents.instances[name] = %s( "%s" )' %
-              (handle._pyKlass, name) )
-        print "."*80
-
-        ## schedule the PyComponentMgr service
-        from AthenaCommon.AppMgr import theApp, ServiceMgr as svcMgr
-        if not hasattr(svcMgr, 'PyComponentMgr'):
-            svcMgr += CfgMgr.PyAthena__PyComponentMgr(
-                'PyComponentMgr',
-                OutputLevel = Lvl.VERBOSE )
-            
-        return
-    pass 
-del _PyAlg
-CfgMgr.PyAthena__Alg = CfgPyAlgorithm
+    pass # class CfgPyAlgorithm
 
 ### Configurable base class for PyServices ------------------------------------
-_PySvc = CfgMgr.PyAthena__Svc
-class CfgPyService(_PySvc):
-    __slots__ = ( '_pyPkg', '_pyKlass', )
+class CfgPyService( ConfigurableService ):
+    def __init__( self, name, **kw ):
+        super( CfgPyService, self ).__init__(name)
+        for n,v in kw.items():
+            setattr(self,n,v)
+    def getGaudiType( self ): return 'Service'
+    def getType(self):        return 'PyAthena::Svc'
+    def getDlls(self):        return 'AthenaPython'
+    def getHandle(self):      return None
+    def setup(self):
+        from AthenaCommon import CfgMgr
+        from AthenaCommon.AppMgr import ServiceMgr as svcMgr
+        if not hasattr( svcMgr, 'PyComponentMgr' ):
+            svcMgr += CfgMgr.PyAthena__PyComponentMgr('PyComponentMgr')
+        
+        ## populate the PyComponents instances repository
+        name = self.getName()
+        if name in PyComponents.instances:
+            raise RuntimeError(
+                "A python component of name [%s] has already been registered "\
+                "with the PyComponents registry !" % name
+                )
+        PyComponents.instances[name] = self
+        return super(CfgPyService, self).setup()
 
-    def __init__(self, name, pkg, klass, **kw):
-        # init base class
-        kw['name'] = name
-        super(CfgPyService, self).__init__( **kw )
+    pass # class CfgPyService
 
-        self._pyPkg   = pkg
-        self._pyKlass = klass
+### Configurable base class for PyAlgTools ------------------------------------
+class CfgPyAlgTool( ConfigurableAlgTool ):
+    def __init__( self, name, **kw ):
+        super( CfgPyAlgTool, self ).__init__(name)
+        for n,v in kw.items():
+            setattr(self,n,v)
+    def getGaudiType( self ): return 'AlgTool'
+    def getType(self):        return 'PyAthena::Tool'
+    def getDlls(self):        return 'AthenaPython'
+    def getHandle(self):      return None
+    def setup(self):
+        from AthenaCommon import CfgMgr
+        from AthenaCommon.AppMgr import ServiceMgr as svcMgr
+        if not hasattr( svcMgr, 'PyComponentMgr' ):
+            svcMgr += CfgMgr.PyAthena__PyComponentMgr('PyComponentMgr')
+        
+        ## populate the PyComponents instances repository
+        name = self.getName()
+        if name in PyComponents.instances:
+            raise RuntimeError(
+                "A python component of name [%s] has already been registered "\
+                "with the PyComponents registry !" % name
+                )
+        PyComponents.instances[name] = self
+        return super(CfgPyAlgTool, self).setup()
 
-    def setDefaults(cls, handle):
+    pass # class CfgPyAlgTool
 
-        if not isinstance(handle, CfgPyService):
-            return
 
-        print "."*80
-        import PyAthena
-        name = handle.getName()
-        if name in PyComponents.instances:
-           #print "???????????"
-           return
-        #PyComponents.instances[name] = "%s/%s" % ( pyKlass, name )
-        exec( 'from %s import %s' % (handle._pyPkg,
-                                     handle._pyKlass.split('.')[0]) )
-        exec( 'PyComponents.instances[name] = %s( "%s" )' %
-              (handle._pyKlass, name) )
-        print "."*80
 
-        ## schedule the PyComponentMgr service
-        from AthenaCommon.AppMgr import theApp, ServiceMgr as svcMgr
-        if not hasattr(svcMgr, 'PyComponentMgr'):
-            svcMgr += CfgMgr.PyAthena__PyComponentMgr(
-                'PyComponentMgr',
-                OutputLevel = Lvl.VERBOSE )
-            
-        return
-    pass 
-del _PySvc
-CfgMgr.PyAthena__Svc = CfgPyService
-
-

python/PyAthena.py

 # @file: PyAthena.py
-# @purpose: a set of Python bindings for pyathena
+# @purpose: a set of Python classes for PyAthena
 # @author: Sebastien Binet <binet@cern.ch>
 
-import PyCintex
+__all__ = [ 'StatusCode',
+            'Alg',
+            'Svc',
+            'AlgTool',
+            'Aud',
+            ]
+from AthenaCommon.Logging import logging
+from AthenaCommon.Configurable  import *
+from AthenaPython.Configurables import (CfgPyAlgorithm,
+                                        CfgPyService,
+                                        CfgPyAlgTool)
 
+### PyAthena.StatusCode -------------------------------------------------------
 class StatusCode:
     Success = 1
     Failure = 0
 
-#import numpy
-import array
 ### PyAthena.Alg --------------------------------------------------------------
-_Alg = PyCintex.makeClass( "PyAthena::Alg" )
-class Alg( _Alg ):
+class Alg( CfgPyAlgorithm ):
     """
+    Base class from which all concrete algorithm classes should
+    be derived.
+
+    In order for a concrete algorithm class to do anything
+    useful the methods initialize(), execute() and finalize()
+    should be overridden.
     """
-    def __init__(self, name):
-
-        from AthenaCommon.Logging import logging
-        self.msg = logging.getLogger( name )
-        self.msg.error( "__init__ !!")
-
-        from gaudimodule import gbl, Interface
-        svcLoc = gbl.Gaudi.svcLocator()
-        algMgr = Interface(gbl.IAlgManager).cast(svcLoc)
-
+    def __init__(self, name = None, **kw):
+        if name is None: name = kw['name']
         ## init base class
-        super(Alg, self).__init__(name, svcLoc)
-
-        sc = algMgr.addAlgorithm(self)
-        if sc.isFailure():
-            raise RuntimeError, "Unable to add PyAthena::Alg/%s" % name
+        super(Alg, self).__init__( name, **kw)
+        self.__dict__['msg']  = logging.getLogger( self.name() )
 
         return
 
-    def __del__(self):
-        sc = self._algMgr.removeAlgorithm(self)
-        if sc.isFailure() : pass
+    def sysInitialize(self):
+        sc = StatusCode.Failure
+        try: sc = self.initialize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
+    def initialize(self):
+        raise NotImplementedError(
+            "You have to implement PyAthena.Alg.initialize() !"
+            )
 
-    def initialize(self):
-        self.msg.info( "==> initialize..." )
-        self.data = array.array( 'd' )
-        return StatusCode.Success
+    def sysReinitialize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.reinitialize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
     
     def reinitialize(self):
         self.msg.info( "==> re-initialize..." )
         return StatusCode.Success
+
+    def sysExecute(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.execute()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
     
     def execute(self):
-        self.msg.info( "==> execute..." )
-        self.data.append( 42 )
+        raise NotImplementedError(
+            "You have to implement PyAthena.Alg.execute() !"
+            )
+
+    def sysFinalize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.finalize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
+    def finalize(self):
+        raise NotImplementedError(
+            "You have to implement PyAthena.Alg.finalize() !"
+            )
+
+    def sysBeginRun(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.beginRun()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
+    def beginRun(self):
+        self.msg.debug( "==> beginRun..." )
         return StatusCode.Success
 
-    def finalize(self):
-        self.msg.info( "==> finalize..." )
-        self.msg.info( "data: %r", self.data )
-        return StatusCode.Success
-
-    def beginRun(self):
-        self.msg.info( "==> beginRun..." )
-        return StatusCode.Success
-
+    def sysEndRun(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.endRun()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
     def endRun(self):
-        self.msg.info( "==> endRun..." )
+        self.msg.debug( "==> endRun..." )
         return StatusCode.Success
     
     pass # PyAthena.Alg
 
 ### PyAthena.Svc --------------------------------------------------------------
-_Svc = PyCintex.makeClass( "PyAthena::Svc" )
-class Svc( _Svc ):
+class Svc( CfgPyService ):
+    """Base class for all services
     """
-    """
-    def __init__(self, name):
+    def __init__(self, name = None, **kw):
 
-        from AthenaCommon.Logging import logging
-        self.msg = logging.getLogger( name )
-        self.msg.error( "__init__ !!")
-
-        from gaudimodule import gbl, Interface
-        svcLoc = gbl.Gaudi.svcLocator()
-        svcMgr = Interface(gbl.ISvcManager).cast(svcLoc)
-
+        if name is None: name = kw['name']
         ## init base class
-        super(Svc, self).__init__(name, svcLoc)
-
-##         priority = 99999
-##         sc = self._svcMgr.addService(self, priority)
-##         if sc.isFailure():
-##             raise RuntimeError, "Unable to add PyAthena::Svc/%s" % name
+        super(Svc, self).__init__(name, **kw)
+        self.__dict__['msg']  = logging.getLogger( self.name() )
         
         return
 
-##     def __del__(self):
-##         sc = self._svcMgr.removeService(self)
-##         if sc.isFailure() : pass
-
-##     def queryInterface(self, riid, ppvInterface):
-##         PyCintex.loadDictionary( "libAthenaPythonDict" )
-##         ptr = PyCintex.libPyROOT.MakeNullPointer(self.__class__)
-##         queryInterface = PyCintex.gbl.AthenaInternal.queryInterface
-##         return queryInterface(self, riid, ppvInterface, ptr)
-
-##     def __getVoidPtr(self):
-##         klassName = self.__class__
-##         print "--->",klassName
-##         return PyCintex.libPyROOT.MakeNullPointer(klassName)
+    def sysInitialize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.initialize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
     
     def initialize(self):
-        self.msg.info( "==> initialize..." )
-        self.msg.info( "classes: %s", self.__class__.mro() )
-        for i in self.__class__.__bases__:
-            self.msg.info( "[%s]: %r", i.__name__,
-                           i.interfaceID() )
-        return StatusCode.Success
+        raise NotImplementedError(
+            "You have to implement PyAthena.Svc.initialize() !"
+            )
+
+    def sysReinitialize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.reinitialize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
     
     def reinitialize(self):
         self.msg.info( "==> re-initialize..." )
         return StatusCode.Success
+
+    def sysFinalize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.finalize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
     
     def finalize(self):
         self.msg.info( "==> finalize..." )
         return StatusCode.Success
 
+    def sysBeginRun(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.beginRun()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
     def beginRun(self):
         self.msg.info( "==> beginRun..." )
         return StatusCode.Success
 
+    def sysEndRun(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.endRun()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
     def endRun(self):
         self.msg.info( "==> endRun..." )
         return StatusCode.Success
     
     pass # PyAthena.Svc
 
+### PyAthena.AlgTool ----------------------------------------------------------
+class AlgTool( CfgPyAlgTool ):
+    """
+    Base class from which all concrete algtool classes should be derived.
+    """
+    def __init__(self, name=None, parent=None, **kw):
+        if name is None: name = kw['name']
+        if not (parent is None):
+            if isinstance(parent, str): name = "%s.%s" % (parent,name)
+            else:                       name = "%s.%s" % (parent.name(),name)
+        ## init base class
+        super(AlgTool, self).__init__( name, **kw)
+        self.__dict__['msg']  = logging.getLogger( self.name() )
+        return
+
+    def sysInitialize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.initialize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
+    def initialize(self):
+        raise NotImplementedError(
+            "You have to implement PyAthena.AlgTool.initialize() !"
+            )
+
+    def sysReinitialize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.reinitialize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
+    def reinitialize(self):
+        self.msg.info( "==> re-initialize..." )
+        return StatusCode.Success
+
+    def sysFinalize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.finalize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
+    def finalize(self):
+        raise NotImplementedError(
+            "You have to implement PyAthena.AlgTool.finalize() !"
+            )
+
+    pass # PyAthena.AlgTool
+
+### PyAthena.Aud --------------------------------------------------------------
+class Aud( object ):
+    """
+    Base class from which all concrete auditor classes should be derived.
+    """
+    def __init__(self, name = None, **kw):
+
+        if name is None: name = kw['name']
+        from AthenaCommon.Logging import logging
+        self.msg = logging.getLogger( name )
+
+        ## init base class
+        super(Aud, self).__init__(**kw)
+
+        return
+
+    def sysInitialize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.initialize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
+    def initialize(self):
+        raise NotImplementedError(
+            "You have to implement PyAthena.Aud.initialize() !"
+            )
+
+    def sysFinalize(self):
+        sc = StatusCode.Failure
+        try:
+            sc = self.finalize()
+        except Exception, err:
+            self.msg.error( err )
+            sc = StatusCode.Failure
+        return sc
+    
+    def finalize(self):
+        raise NotImplementedError(
+            "You have to implement PyAthena.Aud.finalize() !"
+            )
+    ## TODO: boiler-plate code for all before/after methods
+##     def sysBeforeInitialize(INamedInterface* );
+##     def sysAfterInitialize(INamedInterface* );
+##     def sysBeforeReinitialize(INamedInterface* );
+##     def sysAfterReinitialize(INamedInterface* );
+##     def sysBeforeExecute(INamedInterface* );
+##     def sysAfterExecute(INamedInterface*, const StatusCode& );
+##     def sysBeforeBeginRun(INamedInterface* );
+##     def sysAfterBeginRun(INamedInterface* );
+##     def sysBeforeEndRun(INamedInterface* );
+##     def sysAfterEndRun(INamedInterface* );
+##     def sysBeforeFinalize(INamedInterface* );
+##     def sysAfterFinalize(INamedInterface* );
+
+    pass # PyAthena.Aud
+

python/tests/PyTestsLib.py

+##
+
+import AthenaCommon.SystemOfUnits as Units
+import AthenaPython.PyAthena as PyAthena
+from AthenaPython.PyAthena import StatusCode
+
+class MyAlg( PyAthena.Alg ):
+    """Simple test of a py-algorithm
+    """
+    def __init__(self, name = "MyAlg", **kw):
+        ## init base class
+        kw['name'] = name
+        super(MyAlg,self).__init__(**kw)
+
+        if not kw.has_key('px' ): self.px  = 10.*Units.GeV
+        if not kw.has_key('eta'): self.eta = 2.5
+        if not kw.has_key('pt' ): self.pt  = 40.*Units.GeV
+        if not kw.has_key('mytool'):
+            self.mytool = MyTool("%s_mytool"%self.name())
+            
+    def initialize(self):
+        from StoreGateBindings.Bindings import StoreGate
+        self.sg = StoreGate.pointer("StoreGateSvc")
+        self.msg.info( "==> initializing [%s]...", self.name() )
+        self.msg.info( "eta: %r",self.eta )
+        self.msg.info( "pt:  %r",self.pt  )
+        self.msg.info( "px:  %r",self.px  )
+        self.mytool.counter += 1
+        self.msg.info( "tool:%r %r",self.mytool.counter, self.mytool.name() )
+        return StatusCode.Success
+
+    def execute(self):
+        self.msg.info( "==> execute..." )
+##         self.msg.info( "content of StoreGate..." )
+##         self.sg.dump()
+        return StatusCode.Success
+
+    def finalize(self):
+        self.msg.info( "==> finalize..." )
+        return StatusCode.Success
+
+class MySvc( PyAthena.Svc ):
+    """Simple test of a py-service
+    """
+    def __init__(self, name = "MySvc", **kw):
+        ## init base class
+        kw['name'] = name
+        super(MySvc,self).__init__(**kw)
+
+        if not kw.has_key('counter' ): self.counter = 0
+        
+    def initialize(self):
+        from StoreGateBindings.Bindings import StoreGate
+        self.sg = StoreGate.pointer("StoreGateSvc")
+        self.msg.info( "==> initializing [%s]...", self.name() )
+        self.msg.info( "cnt: %r",self.counter )
+        return StatusCode.Success
+
+    def finalize(self):
+        self.msg.info( "==> finalize..." )
+        self.msg.info( "cnt: %r",self.counter )
+        return StatusCode.Success
+
+class MyTool( PyAthena.AlgTool ):
+    """Simple test of a py-tool
+    """
+    def __init__(self, name = "MyTool", **kw):
+        ## init base class
+        kw['name'] = name
+        super(MyTool,self).__init__(**kw)
+
+        if not kw.has_key('counter' ): self.counter = 0
+        
+    def initialize(self):
+        from StoreGateBindings.Bindings import StoreGate
+        self.sg = StoreGate.pointer("StoreGateSvc")
+        self.msg.info( "==> initializing [%s]...", self.name() )
+        self.msg.info( "cnt: %r",self.counter )
+        return StatusCode.Success
+
+    def finalize(self):
+        self.msg.info( "==> finalize..." )
+        self.msg.info( "cnt: %r",self.counter )
+        return StatusCode.Success

python/tests/__init__.py

Empty file added.

share/tests/test_pyathena.py

+##
+import AthenaCommon.AtlasUnixStandardJob
+import AthenaCommon.SystemOfUnits as Units
+import AthenaCommon.Constants as Lvl
+
+from AthenaCommon.AlgSequence import AlgSequence
+job = AlgSequence()
+
+from AthenaPython.tests.PyTestsLib import MyAlg, MySvc, MyTool
+job += MyAlg( "alg1", eta = 2.5 )
+job.alg1.pt = 42.
+
+job += MyAlg( name="alg2", eta = 5.1, pt = 20.*Units.GeV )
+job += MyAlg( "alg3", eta = 5.1, pt = 20.*Units.GeV )
+job.alg3 += MyTool( "mytool", counter = 50, parent="alg3" )
+
+job += MyAlg( eta = 5.2,
+              px  = 20.*Units.GeV,
+              mytool = MyTool( "mytool", counter = 30,
+                               parent="MyAlg",
+                               OutputLevel = Lvl.VERBOSE ),
+              )
+
+from AthenaCommon.AppMgr import ServiceMgr as svcMgr
+svcMgr += MySvc()
+
+from AthenaCommon.AppMgr import theApp
+theApp.EvtMax = 2
+
+theApp.CreateSvc += [ svcMgr.MySvc.getFullName() ]

src/PyComponentMgr.cxx

 #include "GaudiKernel/Property.h"
 #include "GaudiKernel/SvcFactory.h"
 
+// PyROOT includes
+#include "TPyException.h"
+
 using namespace PyAthena;
 
 /////////////////////////////////////////////////////////////////// 
 				ISvcLocator* pSvcLocator ) : 
   Service ( name,     pSvcLocator ),
   m_msg   ( msgSvc(),        name ),
-  m_module( 0 ),
-  m_dict  ( 0 )
+  m_dict      ( 0 ),
+  m_components(   )
 {
   //
   // Property declaration
 { 
   m_msg << MSG::DEBUG << "Calling destructor" << endreq;
 
-  // we own the module
-  Py_XDECREF( m_module );
+  // we own the repository of instances' description
+  Py_XDECREF( m_dict );
+
+  // as well as the one of corresponding instances
+  for ( PyComponents_t::iterator 
+	  i    = m_components.begin(), 
+	  iEnd = m_components.end();
+	i != iEnd;
+	++i ) {
+    m_msg << MSG::VERBOSE << "__del__(" << i->first << ")..." << endreq;
+    Py_XDECREF( i->second );
+  }
+
 }
 
 // Athena Algorithm's Hooks
   // import the module holding the dictionary of component instances
   m_msg << MSG::DEBUG << "Importing module [" << pyModuleName << "]..."
 	<< endreq;
-  m_module = PyImport_ImportModule( const_cast<char*>(pyModuleName.c_str()) );
-  if ( !m_module || !PyModule_Check( m_module ) ) {
-    PyErr_Clear();
+  PyObject* module = PyImport_ImportModule( const_cast<char*>(pyModuleName.c_str()) );
+  if ( !module || !PyModule_Check( module ) ) {
     m_msg << MSG::ERROR
 	  << "Could not import [" << pyModuleName << "] !!"
 	  << endreq;
-    return StatusCode::FAILURE;
+    throw PyROOT::TPyException();
   }
 
   const std::string pyClassName = "PyComponents";
   PyObject* pyClass = 0;
-  pyClass = PyDict_GetItemString( PyModule_GetDict( m_module ),
+  pyClass = PyDict_GetItemString( PyModule_GetDict( module ),
 				  const_cast<char*>( pyClassName.c_str() ) );
   
   // borrowed ref. so ->increment
   Py_XINCREF( pyClass );
 
   if ( !pyClass ) {
-    PyErr_Clear();
     m_msg << MSG::ERROR
 	  << "Could not retrieve class [" << pyClassName << "] from module ["
 	  << pyModuleName << "] !!"
 	  << endreq;
-    return StatusCode::FAILURE;
+    Py_XDECREF(pyClass);
+    throw PyROOT::TPyException();
   }
 
   m_dict = PyObject_GetAttrString( pyClass,
 PyObject*
 PyComponentMgr::pyObject( const std::string& name )
 {
+  // Check if we already have instantiated that component
+  PyComponents_t::iterator comp = m_components.find( name );
+  if ( comp != m_components.end() && comp->second ) {
+    return comp->second;
+  }
+  m_components[name] = static_cast<PyObject*>( NULL );
+
   // Check we have a valid dict.
   if ( !m_dict || !PyDict_Check(m_dict) ) {
     m_msg << MSG::ERROR
   // borrowed ref -> incr
   Py_XINCREF( o );
 
+  // Check we have a valid dict.
   if ( !o ) {
-    PyErr_Clear();
     m_msg << MSG::ERROR
-	  << "No such python component [" << name << "] !!"
+	  << "No such python component [" << name << "] or invalid item !!"
 	  << endreq;
-    Py_XDECREF( o ); // <============ ??? FIXME: do we do that ???
-    Py_INCREF( Py_None );
-    return Py_None;
+    Py_XDECREF( o );
+    throw PyROOT::TPyException();
   }
+  m_components[name] = o;
+
+  /// Hum... remove ? or not ?
+  /// leaving the objects on the python side may allow easier retrieval from
+  /// py-components...
+//   // remove the object from the python dict
+//   if ( PyDict_DelItemString( m_dict, const_cast<char*>(name.c_str()) ) ) {
+//     m_msg << MSG::ERROR
+// 	  << "Could not remove [" << name << "] from PyComponents.instances !!"
+// 	  << endreq;
+//     throw PyROOT::TPyException();
+//   }
 
   return o;
 }

src/PyComponentMgr.h

 
 // STL includes
 #include <string>
+#include "SGTools/unordered_map.h"
 
 // FrameWork includes
 #include "GaudiKernel/Service.h"
 // Forward declaration
 class ISvcLocator;
 template <class TYPE> class SvcFactory;
+struct _object;
+typedef _object PyObject;
 
 namespace PyAthena {
 
   /// MsgStream for talking with the outside world
   MsgStream m_msg;
 
-  /// The module holding all python component instances
-  PyObject* m_module;
+  /** @brief The dictionary of python components' description
+   *         It should be of the form:
+   *            { 'name' : { 'package' : "MyAnaPkg",
+   *                         'class'   : "MyAlg",
+   *                         'pyprops' : { 'OutputLevel' : 1,
+   *                                       ..., }
+   *                       },
+   *            }
+   */
+  PyObject* m_dict;
 
-  /// The dictionary of python objects (created from the Python prompt)
-  PyObject* m_dict;
+  typedef SG::unordered_map<std::string, PyObject*> PyComponents_t;
+  /** @brief A fast look-up hash-map for python components
+   *         { 'name' : PyObject* }
+   *         PyObject* is NULL if not yet instantiated
+   *  Note that we own the PyObjects.
+   */
+  PyComponents_t m_components;
 }; 
 
 /// I/O operators