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",
52 self.setTypeAndName( typeAndName )
58 for key, value
in kwargs.items():
60 self.
_props[ key ] = copy.deepcopy( value )
63 """Get the instance name of the algorithm
65 This is for compatibility with the getName() function of Athena
72 """Get the type name of the algorithm
74 This is for compatibility with the getType() function of Athena
81 """add a copy of this config to the EventLoop job object
84 job -- The job object to add ourself to
89 """Get a previously set property value from the configuration
91 This function allows us to retrieve the value of a property that was
92 already set for the algorithm, to possibly use it in some configuration
93 decisions in the Python code itself.
96 name -- The name of the property
101 if name.startswith(
'_' ):
102 raise AttributeError( name )
105 if name
not in self.
_props:
106 raise AttributeError( f
"Property '{name}' was not set on "
107 f
"'{self.type()}/{self.name()}'" )
110 return self.
_props[ name ]
113 """Set an algorithm property on an existing configuration object
115 This function allows us to set/override properties on an algorithm
116 configuration object. Allowing for the following syntax:
120 alg.FloatProperty = 3.141592
121 alg.StringProperty = "Foo"
124 key -- The key/name of the property
125 value -- The value to set for the property
134 self.
_props[ key ] = copy.deepcopy( value )
137 """Check for equality with another object
139 The implementation of this is very simple. We only check that the type
140 and the name of the algorithms would match.
144 if not isinstance( other, PythonConfig ):
148 return ( ( self.type() == other.type() )
and
149 ( self.name() == other.name() ) )
152 """Print the algorithm configuration in a user friendly way
154 This is just to help with debugging configurations, allowing
155 the user to get a nice printout of their job configuration.
158 name = f
'PythonConfig {self.componentType()}/{self.type()}/{self.name()}'
159 result = PythonConfig._printHeader( name )
161 for key, value
in sorted( self.
_props.items() ):
162 if isinstance( value, str ):
163 printedValue = f
"'{value}'"
166 result += f
"|- {key}: {indentBy( printedValue, '| ' )}\n"
167 result += PythonConfig._printFooter( name )
171 """Create a private tool for the algorithm
173 This function is used in 'standalone' mode to declare a private tool
174 for the algorithm, or a private tool for an already declared private
178 config.addPrivateTool( 'tool1', 'ToolType1' )
179 config.addPrivateTool( 'tool1.tool2', 'ToolType2' )
182 name -- The full name of the private tool
183 type -- The C++ type of the private tool
191 toolNames = name.split(
'.' )
195 for tname
in toolNames[ 0 : -1 ]:
196 component = getattr( component, tname )
199 if hasattr( component, toolNames[ -1 ] ):
200 raise RuntimeError( f
"Tool with name '{name}' already exists" )
207 self.createPrivateTool( name, type ).ignore()
210 """Create a private tool in an array for the algorithm
212 This function is used in 'standalone' mode to declare a
213 private tool in a tool array for the algorithm, or a private
214 tool in a tool array for an already declared private tool.
217 tool = config.addPrivateToolInArray( 'tool1', 'ToolType1' )
218 tool = config.addPrivateToolInArray( 'tool1.tool2', 'ToolType2' )
221 name -- The full name of the private tool
222 type -- The C++ type of the private tool
230 toolNames = name.split(
'.' )
234 for tname
in toolNames[ 0 : -1 ]:
235 component = getattr( component, tname )
238 actualName = self.createPrivateToolInArray( name, type )
242 actualToolNames = actualName.split(
'.' )
246 component._props[ actualToolNames[ -1 ] ] = config
251 """Produce a nice header when printing the configuration
253 This function is used for printing the header of both algorithms
257 indentString -- String used as indentation
258 title -- The title of the algorithm/tool
261 preLength = PythonConfig.printHeaderPre
262 postLength = PythonConfig.printHeaderWidth - 3 - preLength - \
264 return f
"/{preLength * '*'} {title} {postLength * '*'}"
268 """Produce a nice footer when printing the configuration
270 This function is used for printing the footer of both algorithms
274 indentString -- String used as indentation
275 title -- The title of the algorithm/tool
278 preLength = PythonConfig.printHeaderPre
279 postLength = PythonConfig.printHeaderWidth - 12 - preLength - \
281 return f
"\\{preLength * '-'} (End of {title}) {postLength * '-'}"
285 """Standalone Private Tool Configuration
287 This class is used to mimic the behaviour of Athena tool configurable
288 classes. To be able to set the properties of private tools used by
289 dual-use algorithms in a way that's valid for both Athena and EventLoop.
293 """Constructor for an private tool configuration object
302 """Get a previously set property value from the configuration
304 This function allows us to retrieve the value of a tool property that
305 was already set for an algorithm's private tool, to possibly use it in
306 some configuration decisions in the Python code itself.
309 name -- The name of the property
314 if name.startswith(
'_' ):
315 raise AttributeError( name )
318 if name
not in self.
_props:
319 raise AttributeError(
320 f
'Property "{name}" was not set on '
321 f
'"{self._algorithm.type()}/{self._algorithm.name()}.{self._prefix}"' )
324 return self.
_props[ name ]
327 """Set a tool property on an existing configuration object
329 This function allows us to set/override properties on a private tool
330 of an algorithm configuration object. Allowing for the following syntax:
333 alg.Tool.IntProperty = 66
334 alg.Tool.FloatProperty = 3.141592
335 alg.Tool.StringProperty = "Foo"
338 key -- The key/name of the property
339 value -- The value to set for the property
347 fullName = self.
_prefix +
"." + key
350 self.
_algorithm.setPropertyFromString( fullName,
352 self.
_props[ key ] = copy.deepcopy( value )
355 """Print the private tool configuration in a user friendly way
357 This is just to help with debugging configurations, allowing
358 the user to get a nice printout of their job configuration.
361 name = f
'Private Tool {self._type}/{self._prefix}'
363 result += PythonConfig._printHeader( name )
365 for key, value
in sorted( self.
_props.items() ):
366 if isinstance( value, str ):
367 printedValue = f
"'{value}'"
370 result += f
"|- {key}: {indentBy( printedValue, '| ' )}\n"
371 result += PythonConfig._printFooter( name )
376 """Helper function producing a string property value"""
378 stringValue = str( value )
379 if isinstance( value, bool ):
380 stringValue = str( int( value ) )
385 """Helper function used in the configuration printout"""
387 stringValue = str( propValue )
389 for stringLine
in stringValue.split(
'\n' ):
391 result +=
"\n" + indent
407 self.assertEqual( config1.type(),
"TypeName" )
408 self.assertEqual( config1.name(),
"TypeName" )
410 self.assertEqual( config2.type(),
"NS::SomeType" )
411 self.assertEqual( config2.name(),
"NS::SomeType" )
417 self.assertEqual( config1.type(),
"TypeName" )
418 self.assertEqual( config1.name(),
"InstanceName" )
420 self.assertEqual( config2.type(),
"NS::SomeType" )
421 self.assertEqual( config2.name(),
"Instance" )
432 self.
config.Prop1 =
"Value1"
433 self.
config.Prop2 = [
"Value2" ]
434 self.assertEqual( self.
config.Prop1,
"Value1" )
435 self.assertEqual( self.
config.Prop2, [
"Value2" ] )
436 self.assertNotEqual( self.
config.Prop1,
"Foo" )
437 self.assertNotEqual( self.
config.Prop2,
"Value2" )
441 with self.assertRaises( AttributeError ):
453 self.
config.addPrivateTool(
"Tool1",
"ToolType1" )
454 self.
config.Tool1.Prop1 =
"Value1"
455 self.
config.Tool1.Prop2 = [ 1, 2, 3 ]
456 self.assertEqual( self.
config.Tool1.Prop1,
"Value1" )
457 self.assertEqual( self.
config.Tool1.Prop2, [ 1, 2, 3 ] )
461 tool = self.
config.addPrivateToolInArray(
"Tool1",
"ToolType1" )
462 tool.Prop1 =
"Value1"
463 tool.Prop2 = [ 1, 2, 3 ]
464 self.assertEqual( tool.Prop1,
"Value1" )
465 self.assertEqual( tool.Prop2, [ 1, 2, 3 ] )
469 self.
config.addPrivateTool(
"Tool1",
"ToolType1" )
470 self.
config.addPrivateTool(
"Tool1.Tool2",
"ToolType2" )
471 self.
config.Tool1.Tool2.Prop3 =
"Foo"
472 self.
config.Tool1.Tool2.Prop4 = [
"Bar" ]
473 self.assertEqual( self.
config.Tool1.Tool2.Prop3,
"Foo" )
474 self.assertEqual( self.
config.Tool1.Tool2.Prop4, [
"Bar" ] )
478 self.
config.addPrivateTool(
"Tool1",
"ToolType1" )
479 with self.assertRaises( AttributeError ):
480 value = self.
config.Tool1.BadProp
481 self.
config.addPrivateTool(
"Tool1.Tool2",
"ToolType2" )
482 with self.assertRaises( AttributeError ):
483 value = self.
config.Tool1.Tool2.BadProp
487 with self.assertRaises( AttributeError ):
488 self.
config.addPrivateTool(
"BadTool.Tool4",
"BadToolType" )
addPrivateToolInArray(self, name, type)
__setattr__(self, key, value)
__init__(self, typeAndName, **kwargs)
addPrivateTool(self, name, type)
Test case for the algorithm property handling.
test_propaccess(self)
Test that properties that got set, can be read back.
setUp(self)
Common setup for the tests.
test_nonexistentprop(self)
Test that an unset property can't be accessed.
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)