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 ):