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",
51 self.setTypeAndName( typeAndName )
57 for key, value
in kwargs.items():
59 self.
_props[ key ] = copy.deepcopy( value )
62 """Get the instance name of the algorithm
64 This is for compatibility with the getName() function of Athena
71 """Get the type name of the algorithm
73 This is for compatibility with the getType() function of Athena
80 """Get a previously set property value from the configuration
82 This function allows us to retrieve the value of a property that was
83 already set for the algorithm, to possibly use it in some configuration
84 decisions in the Python code itself.
87 name -- The name of the property
92 if name.startswith(
'_' ):
93 raise AttributeError( name )
96 if name
not in self.
_props:
97 raise AttributeError( f
"Property '{name}' was not set on "
98 f
"'{self.type()}/{self.name()}'" )
101 return self.
_props[ name ]
104 """Set an algorithm property on an existing configuration object
106 This function allows us to set/override properties on an algorithm
107 configuration object. Allowing for the following syntax:
111 alg.FloatProperty = 3.141592
112 alg.StringProperty = "Foo"
115 key -- The key/name of the property
116 value -- The value to set for the property
125 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 """Print the algorithm configuration in a user friendly way
145 This is just to help with debugging configurations, allowing
146 the user to get a nice printout of their job configuration.
149 if self.isPublicTool():
150 name = f
'Public Tool {self.type()}/{self.name()}'
152 name = f
'Algorithm {self.type()}/{self.name()}'
153 result = AnaAlgorithmConfig._printHeader( name )
155 for key, value
in sorted( self.
_props.items() ):
156 if isinstance( value, str ):
157 printedValue = f
"'{value}'"
160 result += f
"|- {key}: {indentBy( printedValue, '| ' )}\n"
161 result += AnaAlgorithmConfig._printFooter( name )
165 """Create a private tool for the algorithm
167 This function is used in 'standalone' mode to declare a private tool
168 for the algorithm, or a private tool for an already declared private
172 config.addPrivateTool( 'tool1', 'ToolType1' )
173 config.addPrivateTool( 'tool1.tool2', 'ToolType2' )
176 name -- The full name of the private tool
177 type -- The C++ type of the private tool
185 toolNames = name.split(
'.' )
189 for tname
in toolNames[ 0 : -1 ]:
190 component = getattr( component, tname )
193 if hasattr( component, toolNames[ -1 ] ):
194 raise RuntimeError( f
"Tool with name '{name}' already exists" )
201 self.createPrivateTool( name, type ).ignore()
204 """Create a private tool in an array for the algorithm
206 This function is used in 'standalone' mode to declare a
207 private tool in a tool array for the algorithm, or a private
208 tool in a tool array for an already declared private tool.
211 tool = config.addPrivateToolInArray( 'tool1', 'ToolType1' )
212 tool = config.addPrivateToolInArray( 'tool1.tool2', 'ToolType2' )
215 name -- The full name of the private tool
216 type -- The C++ type of the private tool
224 toolNames = name.split(
'.' )
228 for tname
in toolNames[ 0 : -1 ]:
229 component = getattr( component, tname )
232 actualName = self.createPrivateToolInArray( name, type )
236 actualToolNames = actualName.split(
'.' )
240 component._props[ actualToolNames[ -1 ] ] = config
245 """Produce a nice header when printing the configuration
247 This function is used for printing the header of both algorithms
251 indentString -- String used as indentation
252 title -- The title of the algorithm/tool
255 preLength = AnaAlgorithmConfig.printHeaderPre
256 postLength = AnaAlgorithmConfig.printHeaderWidth - 3 - preLength - \
258 return f
"/{preLength * '*'} {title} {postLength * '*'}"
262 """Produce a nice footer when printing the configuration
264 This function is used for printing the footer 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 - 12 - preLength - \
275 return f
"\\{preLength * '-'} (End of {title}) {postLength * '-'}"
279 """Standalone Private Tool Configuration
281 This class is used to mimic the behaviour of Athena tool configurable
282 classes. To be able to set the properties of private tools used by
283 dual-use algorithms in a way that's valid for both Athena and EventLoop.
287 """Constructor for an private tool configuration object
296 """Get a previously set property value from the configuration
298 This function allows us to retrieve the value of a tool property that
299 was already set for an algorithm's private tool, to possibly use it in
300 some configuration decisions in the Python code itself.
303 name -- The name of the property
308 if name.startswith(
'_' ):
309 raise AttributeError( name )
312 if name
not in self.
_props:
313 raise AttributeError(
314 f
'Property "{name}" was not set on '
315 f
'"{self._algorithm.type()}/{self._algorithm.name()}.{self._prefix}"' )
318 return self.
_props[ name ]
321 """Set a tool property on an existing configuration object
323 This function allows us to set/override properties on a private tool
324 of an algorithm configuration object. Allowing for the following syntax:
327 alg.Tool.IntProperty = 66
328 alg.Tool.FloatProperty = 3.141592
329 alg.Tool.StringProperty = "Foo"
332 key -- The key/name of the property
333 value -- The value to set for the property
341 fullName = self.
_prefix +
"." + key
344 self.
_algorithm.setPropertyFromString( fullName,
346 self.
_props[ key ] = copy.deepcopy( value )
349 """Print the private tool configuration in a user friendly way
351 This is just to help with debugging configurations, allowing
352 the user to get a nice printout of their job configuration.
355 name = f
'Private Tool {self._type}/{self._prefix}'
357 result += AnaAlgorithmConfig._printHeader( name )
359 for key, value
in sorted( self.
_props.items() ):
360 if isinstance( value, str ):
361 printedValue = f
"'{value}'"
364 result += f
"|- {key}: {indentBy( printedValue, '| ' )}\n"
365 result += AnaAlgorithmConfig._printFooter( name )
370 """Helper function producing a string property value"""
372 stringValue = str( value )
373 if isinstance( value, bool ):
374 stringValue = str( int( value ) )
379 """Helper function used in the configuration printout"""
381 stringValue = str( propValue )
383 for stringLine
in stringValue.split(
'\n' ):
385 result +=
"\n" + indent
401 self.assertEqual( config1.type(),
"TypeName" )
402 self.assertEqual( config1.name(),
"TypeName" )
404 self.assertEqual( config2.type(),
"NS::SomeType" )
405 self.assertEqual( config2.name(),
"NS::SomeType" )
411 self.assertEqual( config1.type(),
"TypeName" )
412 self.assertEqual( config1.name(),
"InstanceName" )
414 self.assertEqual( config2.type(),
"NS::SomeType" )
415 self.assertEqual( config2.name(),
"Instance" )
426 self.
config.Prop1 =
"Value1"
427 self.
config.Prop2 = [
"Value2" ]
428 self.assertEqual( self.
config.Prop1,
"Value1" )
429 self.assertEqual( self.
config.Prop2, [
"Value2" ] )
430 self.assertNotEqual( self.
config.Prop1,
"Foo" )
431 self.assertNotEqual( self.
config.Prop2,
"Value2" )
435 with self.assertRaises( AttributeError ):
447 self.
config.addPrivateTool(
"Tool1",
"ToolType1" )
448 self.
config.Tool1.Prop1 =
"Value1"
449 self.
config.Tool1.Prop2 = [ 1, 2, 3 ]
450 self.assertEqual( self.
config.Tool1.Prop1,
"Value1" )
451 self.assertEqual( self.
config.Tool1.Prop2, [ 1, 2, 3 ] )
455 tool = self.
config.addPrivateToolInArray(
"Tool1",
"ToolType1" )
456 tool.Prop1 =
"Value1"
457 tool.Prop2 = [ 1, 2, 3 ]
458 self.assertEqual( tool.Prop1,
"Value1" )
459 self.assertEqual( tool.Prop2, [ 1, 2, 3 ] )
463 self.
config.addPrivateTool(
"Tool1",
"ToolType1" )
464 self.
config.addPrivateTool(
"Tool1.Tool2",
"ToolType2" )
465 self.
config.Tool1.Tool2.Prop3 =
"Foo"
466 self.
config.Tool1.Tool2.Prop4 = [
"Bar" ]
467 self.assertEqual( self.
config.Tool1.Tool2.Prop3,
"Foo" )
468 self.assertEqual( self.
config.Tool1.Tool2.Prop4, [
"Bar" ] )
472 self.
config.addPrivateTool(
"Tool1",
"ToolType1" )
473 with self.assertRaises( AttributeError ):
474 value = self.
config.Tool1.BadProp
475 self.
config.addPrivateTool(
"Tool1.Tool2",
"ToolType2" )
476 with self.assertRaises( AttributeError ):
477 value = self.
config.Tool1.Tool2.BadProp
481 with self.assertRaises( AttributeError ):
482 self.
config.addPrivateTool(
"BadTool.Tool4",
"BadToolType" )
addPrivateTool(self, name, type)
addPrivateToolInArray(self, name, type)
__init__(self, typeAndName, **kwargs)
__setattr__(self, key, value)
Test case for the algorithm property handling.
setUp(self)
Common setup for the tests.
test_nonexistentprop(self)
Test that an unset property can't be accessed.
test_propaccess(self)
Test that properties that got set, can be read back.
Test case for the algorithm type/name handling.
test_typeandname(self)
Test that specifying the type and name separately in the same string works as expected.
test_singletypename(self)
Test that the type and name are set correctly when using a single argument.
indentBy(propValue, indent)