9     """Standalone Analysis Component Configuration 
   11     This class is used to describe the configuration of an analysis component 
   12     (a C++ class inheriting from asg::AsgComponentConfig) in Python. It behaves 
   13     similar to an Athena configurable, but is implemented in a much simpler 
   16     An example of using it in configuring an EventLoop job could look like: 
   20        from AnaAlgorithm.PythonConfig import PythonConfig 
   21        alg = PythonConfig( "EL::UnitTestAlg2/TestAlg", 
   23        alg.setComponentType( "AnaAlgorithm" ) 
   24        alg.string_property = "Foo" 
   27     Note that the python code doesn't know what properties can actually be set 
   28     on any given C++ algorithm. Any mistake made in the Python configuration 
   29     (apart from syntax errors) is only discovered while initialising the 
   38         """Constructor for an algorithm configuration object 
   41           typeAndName -- The type/instance name of the algorithm 
   43         Note that you can pass (initial) properties to the constructor like: 
   45            alg = PythonConfig( "EL::UnitTestAlg2/TestAlg", 
   51         super( PythonConfig, self ).
__init__()
 
   52         self.setTypeAndName( typeAndName )
 
   58         for key, value 
in kwargs.items():
 
   60             self.
_props[ key ] = copy.deepcopy( value )
 
   66         """Get the instance name of the algorithm 
   68         This is for compatibility with the getName() function of Athena 
   75         """Get the type name of the algorithm 
   77         This is for compatibility with the getType() function of Athena 
   84         """add a copy of this config to the EventLoop job object 
   87           job      -- The job object to add ourself to 
   93         """Get a previously set property value from the configuration 
   95         This function allows us to retrieve the value of a property that was 
   96         already set for the algorithm, to possibly use it in some configuration 
   97         decisions in the Python code itself. 
  100           name -- The name of the property 
  104         if not name 
in self.
_props:
 
  105             raise AttributeError( 
'Property \'%s\' was not set on \'%s/%s\'' %
 
  106                                   ( name, self.type(), self.name() ) )
 
  109         return self.
_props[ name ]
 
  112         """Set an algorithm property on an existing configuration object 
  114         This function allows us to set/override properties on an algorithm 
  115         configuration object. Allowing for the following syntax: 
  119            alg.FloatProperty = 3.141592 
  120            alg.StringProperty = "Foo" 
  123           key   -- The key/name of the property 
  124           value -- The value to set for the property 
  129             return super( PythonConfig, self ).
__setattr__( key, value )
 
  134         self.
_props[ key ] = copy.deepcopy( value )
 
  138         """Check for equality with another object 
  140         The implementation of this is very simple. We only check that the type 
  141         and the name of the algorithms would match. 
  145         if not isinstance( other, PythonConfig ):
 
  149         return ( ( self.type() == other.type() ) 
and 
  150                  ( self.name() == other.name() ) )
 
  153         """Check for an inequality with another object 
  155         This is just defined to make the '!=' operator of Python behave 
  156         consistently with the '==' operator for such objects. 
  158         return not self.
__eq__( other )
 
  161         """Print the algorithm configuration in a user friendly way 
  163         This is just to help with debugging configurations, allowing 
  164         the user to get a nice printout of their job configuration. 
  167         name = 
'PythonConfig %s/%s/%s' % ( self.componentType(),self.type(), self.name() )
 
  168         result = PythonConfig._printHeader( name )
 
  171             if isinstance( value, str ):
 
  172                 printedValue = 
"'%s'" % value
 
  176             result += 
"|- %s: %s\n" % ( key, 
indentBy( printedValue, 
"| " ) )
 
  178         result += PythonConfig._printFooter( name )
 
  182         """Create a private tool for the algorithm 
  184         This function is used in 'standalone' mode to declare a private tool 
  185         for the algorithm, or a private tool for an already declared private 
  189           config.addPrivateTool( 'tool1', 'ToolType1' ) 
  190           config.addPrivateTool( 'tool1.tool2', 'ToolType2' ) 
  193           name -- The full name of the private tool 
  194           type -- The C++ type of the private tool 
  202         toolNames = name.split( 
'.' )
 
  206         for tname 
in toolNames[ 0 : -1 ]:
 
  207             component = getattr( component, tname )
 
  211         if hasattr( component, toolNames[ -1 ] ):
 
  212             raise RuntimeError( 
"Tool with name '%s' already exists" % name )
 
  220         self.createPrivateTool( name, type ).
ignore()
 
  225         """Create a private tool in an array for the algorithm 
  227         This function is used in 'standalone' mode to declare a 
  228         private tool in a tool array for the algorithm, or a private 
  229         tool in a tool array for an already declared private tool. 
  232           tool = config.addPrivateToolInArray( 'tool1', 'ToolType1' ) 
  233           tool = config.addPrivateToolInArray( 'tool1.tool2', 'ToolType2' ) 
  236           name -- The full name of the private tool 
  237           type -- The C++ type of the private tool 
  245         toolNames = name.split( 
'.' )
 
  249         for tname 
in toolNames[ 0 : -1 ]:
 
  250             component = getattr( component, tname )
 
  254         actualName = self.createPrivateToolInArray( name, type )
 
  258         actualToolNames = actualName.split( 
'.' )
 
  262         component._props[ actualToolNames[ -1 ] ] = config
 
  267         """Produce a nice header when printing the configuration 
  269         This function is used for printing the header of both algorithms 
  273           indentString -- String used as indentation 
  274           title        -- The title of the algorithm/tool 
  277         preLength = PythonConfig.printHeaderPre
 
  278         postLength = PythonConfig.printHeaderWidth - 3 - preLength - \
 
  280         return '/%s %s %s' % ( preLength * 
'*', title, postLength * 
'*' )
 
  284         """Produce a nice footer when printing the configuration 
  286         This function is used for printing the footer of both algorithms 
  290           indentString -- String used as indentation 
  291           title        -- The title of the algorithm/tool 
  294         preLength = PythonConfig.printHeaderPre
 
  295         postLength = PythonConfig.printHeaderWidth - 12 - preLength - \
 
  297         return '\\%s (End of %s) %s' % ( preLength * 
'-', title,
 
  304     """Standalone Private Tool Configuration 
  306     This class is used to mimic the behaviour of Athena tool configurable 
  307     classes. To be able to set the properties of private tools used by 
  308     dual-use algorithms in a way that's valid for both Athena and EventLoop. 
  312         """Constructor for an private tool configuration object 
  323         """Get a previously set property value from the configuration 
  325         This function allows us to retrieve the value of a tool property that 
  326         was already set for an algorithm's private tool, to possibly use it in 
  327         some configuration decisions in the Python code itself. 
  330           name -- The name of the property 
  334         if not name 
in self.
_props:
 
  335             raise AttributeError( 
'Property "%s" was not set on "%s/%s.%s"' %
 
  340         return self.
_props[ name ]
 
  343         """Set a tool property on an existing configuration object 
  345         This function allows us to set/override properties on a private tool 
  346         of an algorithm configuration object. Allowing for the following syntax: 
  349            alg.Tool.IntProperty = 66 
  350            alg.Tool.FloatProperty = 3.141592 
  351            alg.Tool.StringProperty = "Foo" 
  354           key   -- The key/name of the property 
  355           value -- The value to set for the property 
  360             return super( PrivateToolConfig, self ).
__setattr__( key, value )
 
  363         fullName = self.
_prefix + 
"." + key
 
  366         self.
_algorithm.setPropertyFromString( fullName,
 
  368         self.
_props[ key ] = copy.deepcopy( value )
 
  372         """Print the private tool configuration in a user friendly way 
  374         This is just to help with debugging configurations, allowing 
  375         the user to get a nice printout of their job configuration. 
  378         name = 
'Private Tool %s/%s' % ( self.
_type, self.
_prefix )
 
  380         result += PythonConfig._printHeader( name )
 
  383             if isinstance( value, str ):
 
  384                 printedValue = 
"'%s'" % value
 
  388             result += 
"|- %s: %s\n" % ( key, 
indentBy( printedValue, 
"| " ) )
 
  390         result += PythonConfig._printFooter( name )
 
  397     """Helper function producing a string property value""" 
  399     stringValue = 
str( value )
 
  400     if isinstance( value, bool ):
 
  401         stringValue = 
str( 
int( value ) )
 
  407     """Helper function used in the configuration printout""" 
  409     stringValue = 
str( propValue )
 
  411     for stringLine 
in stringValue.split( 
'\n' ):
 
  413             result += 
"\n" + indent
 
  431         self.assertEqual( config1.type(), 
"TypeName" )
 
  432         self.assertEqual( config1.name(), 
"TypeName" )
 
  434         self.assertEqual( config2.type(), 
"NS::SomeType" )
 
  435         self.assertEqual( config2.name(), 
"NS::SomeType" )
 
  442         self.assertEqual( config1.type(), 
"TypeName" )
 
  443         self.assertEqual( config1.name(), 
"InstanceName" )
 
  445         self.assertEqual( config2.type(), 
"NS::SomeType" )
 
  446         self.assertEqual( config2.name(), 
"Instance" )
 
  459         self.
config.Prop1 = 
"Value1" 
  460         self.
config.Prop2 = [ 
"Value2" ]
 
  461         self.assertEqual( self.
config.Prop1, 
"Value1" )
 
  462         self.assertEqual( self.
config.Prop2, [ 
"Value2" ] )
 
  463         self.assertNotEqual( self.
config.Prop1, 
"Foo" )
 
  464         self.assertNotEqual( self.
config.Prop2, 
"Value2" )
 
  469         with self.assertRaises( AttributeError ):
 
  485         self.
config.Tool1.Prop1 = 
"Value1" 
  486         self.
config.Tool1.Prop2 = [ 1, 2, 3 ]
 
  487         self.assertEqual( self.
config.Tool1.Prop1, 
"Value1" )
 
  488         self.assertEqual( self.
config.Tool1.Prop2, [ 1, 2, 3 ] )
 
  494         tool.Prop1 = 
"Value1" 
  495         tool.Prop2 = [ 1, 2, 3 ]
 
  496         self.assertEqual( tool.Prop1, 
"Value1" )
 
  497         self.assertEqual( tool.Prop2, [ 1, 2, 3 ] )
 
  504         self.
config.Tool1.Tool2.Prop3 = 
"Foo" 
  505         self.
config.Tool1.Tool2.Prop4 = [ 
"Bar" ]
 
  506         self.assertEqual( self.
config.Tool1.Tool2.Prop3, 
"Foo" )
 
  507         self.assertEqual( self.
config.Tool1.Tool2.Prop4, [ 
"Bar" ] )
 
  513         with self.assertRaises( AttributeError ):
 
  514             value = self.
config.Tool1.BadProp
 
  517         with self.assertRaises( AttributeError ):
 
  518             value = self.
config.Tool1.Tool2.BadProp
 
  524         with self.assertRaises( AttributeError ):