9     """Standalone Analysis Algorithm Configuration 
   11     This class is used to describe the configuration of an analysis algorithm 
   12     (a C++ class inheriting from EL::AnaAlgorithm) 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.AnaAlgorithmConfig import AnaAlgorithmConfig 
   21        alg = AnaAlgorithmConfig( "EL::UnitTestAlg2/TestAlg", 
   23        alg.string_property = "Foo" 
   26     Note that the python code doesn't know what properties can actually be set 
   27     on any given C++ algorithm. Any mistake made in the Python configuration 
   28     (apart from syntax errors) is only discovered while initialising the 
   37         """Constructor for an algorithm configuration object 
   40           typeAndName -- The type/instance name of the algorithm 
   42         Note that you can pass (initial) properties to the constructor like: 
   44            alg = AnaAlgorithmConfig( "EL::UnitTestAlg2/TestAlg", 
   50         super( AnaAlgorithmConfig, self ).
__init__()
 
   51         self.setTypeAndName( typeAndName )
 
   57         for key, value 
in kwargs.items():
 
   59             self.
_props[ key ] = copy.deepcopy( value )
 
   65         """Get the instance name of the algorithm 
   67         This is for compatibility with the getName() function of Athena 
   74         """Get the type name of the algorithm 
   76         This is for compatibility with the getType() function of Athena 
   83         """Get a previously set property value from the configuration 
   85         This function allows us to retrieve the value of a property that was 
   86         already set for the algorithm, to possibly use it in some configuration 
   87         decisions in the Python code itself. 
   90           name -- The name of the property 
   94         if not name 
in self.
_props:
 
   95             raise AttributeError( 
'Property \'%s\' was not set on \'%s/%s\'' %
 
   96                                   ( name, self.type(), self.name() ) )
 
  102         """Set an algorithm property on an existing configuration object 
  104         This function allows us to set/override properties on an algorithm 
  105         configuration object. Allowing for the following syntax: 
  109            alg.FloatProperty = 3.141592 
  110            alg.StringProperty = "Foo" 
  113           key   -- The key/name of the property 
  114           value -- The value to set for the property 
  119             return super( AnaAlgorithmConfig, self ).
__setattr__( key, value )
 
  122         super( AnaAlgorithmConfig,
 
  124         self.
_props[ key ] = copy.deepcopy( value )
 
  128         """Check for equality with another object 
  130         The implementation of this is very simple. We only check that the type 
  131         and the name of the algorithms would match. 
  135         if not isinstance( other, AnaAlgorithmConfig ):
 
  139         return ( ( self.type() == other.type() ) 
and 
  140                  ( self.name() == other.name() ) )
 
  143         """Check for an inequality with another object 
  145         This is just defined to make the '!=' operator of Python behave 
  146         consistently with the '==' operator for such objects. 
  148         return not self.
__eq__( other )
 
  151         """Print the algorithm configuration in a user friendly way 
  153         This is just to help with debugging configurations, allowing 
  154         the user to get a nice printout of their job configuration. 
  158         if self.isPublicTool():
 
  159             name = 
'Public Tool %s/%s' % ( self.type(), self.name() )
 
  161             name = 
'Algorithm %s/%s' % ( self.type(), self.name() )
 
  163         result = AnaAlgorithmConfig._printHeader( name )
 
  166             if isinstance( value, str ):
 
  167                 printedValue = 
"'%s'" % value
 
  171             result += 
"|- %s: %s\n" % ( key, 
indentBy( printedValue, 
"| " ) )
 
  173         result += AnaAlgorithmConfig._printFooter( name )
 
  177         """Create a private tool for the algorithm 
  179         This function is used in 'standalone' mode to declare a private tool 
  180         for the algorithm, or a private tool for an already declared private 
  184           config.addPrivateTool( 'tool1', 'ToolType1' ) 
  185           config.addPrivateTool( 'tool1.tool2', 'ToolType2' ) 
  188           name -- The full name of the private tool 
  189           type -- The C++ type of the private tool 
  197         toolNames = name.split( 
'.' )
 
  201         for tname 
in toolNames[ 0 : -1 ]:
 
  202             component = getattr( component, tname )
 
  206         if hasattr( component, toolNames[ -1 ] ):
 
  207             raise RuntimeError( 
"Tool with name '%s' already exists" % name )
 
  215         self.createPrivateTool( name, type ).
ignore()
 
  220         """Create a private tool in an array for the algorithm 
  222         This function is used in 'standalone' mode to declare a 
  223         private tool in a tool array for the algorithm, or a private 
  224         tool in a tool array for an already declared private tool. 
  227           tool = config.addPrivateToolInArray( 'tool1', 'ToolType1' ) 
  228           tool = config.addPrivateToolInArray( 'tool1.tool2', 'ToolType2' ) 
  231           name -- The full name of the private tool 
  232           type -- The C++ type of the private tool 
  240         toolNames = name.split( 
'.' )
 
  244         for tname 
in toolNames[ 0 : -1 ]:
 
  245             component = getattr( component, tname )
 
  249         actualName = self.createPrivateToolInArray( name, type )
 
  253         actualToolNames = actualName.split( 
'.' )
 
  257         component._props[ actualToolNames[ -1 ] ] = config
 
  262         """Produce a nice header when printing the configuration 
  264         This function is used for printing the header of both algorithms 
  268           indentString -- String used as indentation 
  269           title        -- The title of the algorithm/tool 
  272         preLength = AnaAlgorithmConfig.printHeaderPre
 
  273         postLength = AnaAlgorithmConfig.printHeaderWidth - 3 - preLength - \
 
  275         return '/%s %s %s' % ( preLength * 
'*', title, postLength * 
'*' )
 
  279         """Produce a nice footer when printing the configuration 
  281         This function is used for printing the footer of both algorithms 
  285           indentString -- String used as indentation 
  286           title        -- The title of the algorithm/tool 
  289         preLength = AnaAlgorithmConfig.printHeaderPre
 
  290         postLength = AnaAlgorithmConfig.printHeaderWidth - 12 - preLength - \
 
  292         return '\\%s (End of %s) %s' % ( preLength * 
'-', title,
 
  299     """Standalone Private Tool Configuration 
  301     This class is used to mimic the behaviour of Athena tool configurable 
  302     classes. To be able to set the properties of private tools used by 
  303     dual-use algorithms in a way that's valid for both Athena and EventLoop. 
  307         """Constructor for an private tool configuration object 
  318         """Get a previously set property value from the configuration 
  320         This function allows us to retrieve the value of a tool property that 
  321         was already set for an algorithm's private tool, to possibly use it in 
  322         some configuration decisions in the Python code itself. 
  325           name -- The name of the property 
  329         if not name 
in self.
_props:
 
  330             raise AttributeError( 
'Property "%s" was not set on "%s/%s.%s"' %
 
  335         return self.
_props[ name ]
 
  338         """Set a tool property on an existing configuration object 
  340         This function allows us to set/override properties on a private tool 
  341         of an algorithm configuration object. Allowing for the following syntax: 
  344            alg.Tool.IntProperty = 66 
  345            alg.Tool.FloatProperty = 3.141592 
  346            alg.Tool.StringProperty = "Foo" 
  349           key   -- The key/name of the property 
  350           value -- The value to set for the property 
  355             return super( PrivateToolConfig, self ).
__setattr__( key, value )
 
  358         fullName = self.
_prefix + 
"." + key
 
  361         self.
_algorithm.setPropertyFromString( fullName,
 
  363         self.
_props[ key ] = copy.deepcopy( value )
 
  367         """Print the private tool configuration in a user friendly way 
  369         This is just to help with debugging configurations, allowing 
  370         the user to get a nice printout of their job configuration. 
  373         name = 
'Private Tool %s/%s' % ( self.
_type, self.
_prefix )
 
  375         result += AnaAlgorithmConfig._printHeader( name )
 
  378             if isinstance( value, str ):
 
  379                 printedValue = 
"'%s'" % value
 
  383             result += 
"|- %s: %s\n" % ( key, 
indentBy( printedValue, 
"| " ) )
 
  385         result += AnaAlgorithmConfig._printFooter( name )
 
  392     """Helper function producing a string property value""" 
  394     stringValue = 
str( value )
 
  395     if isinstance( value, bool ):
 
  396         stringValue = 
str( 
int( value ) )
 
  402     """Helper function used in the configuration printout""" 
  404     stringValue = 
str( propValue )
 
  406     for stringLine 
in stringValue.split( 
'\n' ):
 
  408             result += 
"\n" + indent
 
  426         self.assertEqual( config1.type(), 
"TypeName" )
 
  427         self.assertEqual( config1.name(), 
"TypeName" )
 
  429         self.assertEqual( config2.type(), 
"NS::SomeType" )
 
  430         self.assertEqual( config2.name(), 
"NS::SomeType" )
 
  437         self.assertEqual( config1.type(), 
"TypeName" )
 
  438         self.assertEqual( config1.name(), 
"InstanceName" )
 
  440         self.assertEqual( config2.type(), 
"NS::SomeType" )
 
  441         self.assertEqual( config2.name(), 
"Instance" )
 
  454         self.
config.Prop1 = 
"Value1" 
  455         self.
config.Prop2 = [ 
"Value2" ]
 
  456         self.assertEqual( self.
config.Prop1, 
"Value1" )
 
  457         self.assertEqual( self.
config.Prop2, [ 
"Value2" ] )
 
  458         self.assertNotEqual( self.
config.Prop1, 
"Foo" )
 
  459         self.assertNotEqual( self.
config.Prop2, 
"Value2" )
 
  464         with self.assertRaises( AttributeError ):
 
  480         self.
config.Tool1.Prop1 = 
"Value1" 
  481         self.
config.Tool1.Prop2 = [ 1, 2, 3 ]
 
  482         self.assertEqual( self.
config.Tool1.Prop1, 
"Value1" )
 
  483         self.assertEqual( self.
config.Tool1.Prop2, [ 1, 2, 3 ] )
 
  489         tool.Prop1 = 
"Value1" 
  490         tool.Prop2 = [ 1, 2, 3 ]
 
  491         self.assertEqual( tool.Prop1, 
"Value1" )
 
  492         self.assertEqual( tool.Prop2, [ 1, 2, 3 ] )
 
  499         self.
config.Tool1.Tool2.Prop3 = 
"Foo" 
  500         self.
config.Tool1.Tool2.Prop4 = [ 
"Bar" ]
 
  501         self.assertEqual( self.
config.Tool1.Tool2.Prop3, 
"Foo" )
 
  502         self.assertEqual( self.
config.Tool1.Tool2.Prop4, [ 
"Bar" ] )
 
  508         with self.assertRaises( AttributeError ):
 
  509             value = self.
config.Tool1.BadProp
 
  512         with self.assertRaises( AttributeError ):
 
  513             value = self.
config.Tool1.Tool2.BadProp
 
  519         with self.assertRaises( AttributeError ):