ATLAS Offline Software
AsgServiceConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
2 
3 # Import(s):
4 import ROOT
5 import unittest
6 import copy
7 
8 # This file is (mostly) a copy of AnaAlgorithmConfig.py, with
9 # algorithms replaced with services. Ideally those two classes should
10 # be merged at some point, and the public tools be added via
11 # AsgToolConfig. However, given that this class inherits from a
12 # type-specific Config class that is (probably) a non-trivial change
13 # to the class, and postponed to a future update. Nils Krumnack 19
14 # Mar 21
15 
16 
17 class AsgServiceConfig( ROOT.asg.AsgServiceConfig ):
18  """Standalone Analysis Service Configuration
19 
20  This class is used to describe the configuration of an analysis service
21  (a C++ class inheriting from asg::AsgService) in Python. It behaves
22  similar to an Athena configurable, but is implemented in a much simpler
23  way.
24 
25  An example of using it in configuring an EventLoop job could look like:
26 
27  job = ROOT.asg.Job()
28  ...
29  from AsgServices.AsgServiceConfig import AsgServiceConfig
30  service = AsgServiceConfig( "asg::UnitTestService1/TestService",
31  property = 1.23 )
32  service.string_property = "Foo"
33  job.servicesAdd( service )
34 
35  Note that the python code doesn't know what properties can actually be set
36  on any given C++ service. Any mistake made in the Python configuration
37  (apart from syntax errors) is only discovered while initialising the
38  analysis job.
39  """
40 
41  # Class/static variable(s):
42  printHeaderWidth = 80
43  printHeaderPre = 3
44 
45  def __init__( self, typeAndName, **kwargs ):
46  """Constructor for an service configuration object
47 
48  Keyword arguments:
49  typeAndName -- The type/instance name of the service
50 
51  Note that you can pass (initial) properties to the constructor like:
52 
53  service = AsgServiceConfig( "asg::UnitTestService1/TestService",
54  property = 1.23 )
55  """
56 
57  # Call the base class's constructor. Use the default constructor instead
58  # of the one receiving the type and name, to avoid ROOT-10872.
59  super( AsgServiceConfig, self ).__init__()
60  self.setTypeAndName( typeAndName )
61 
62  # Initialise the properties of the service:
63  self._props = {}
64 
65  # Set the properties on the object:
66  for key, value in kwargs.items():
67  self.setPropertyFromString( key, stringPropValue( value ) )
68  self._props[ key ] = copy.deepcopy( value )
69  pass
70 
71  pass
72 
73  def getName( self ):
74  """Get the instance name of the service
75 
76  This is for compatibility with the getName() function of Athena
77  configurables.
78  """
79 
80  return self.name()
81 
82  def getType( self ):
83  """Get the type name of the service
84 
85  This is for compatibility with the getType() function of Athena
86  configurables.
87  """
88 
89  return self.type()
90 
91  def __getattr__( self, name ):
92  """Get a previously set property value from the configuration
93 
94  This function allows us to retrieve the value of a property that was
95  already set for the service, to possibly use it in some configuration
96  decisions in the Python code itself.
97 
98  Keyword arguments:
99  name -- The name of the property
100  """
101 
102  # Fail if the property was not (yet) set:
103  if not name in self._props:
104  raise AttributeError( 'Property \'%s\' was not set on \'%s/%s\'' %
105  ( name, self.type(), self.name() ) )
106 
107  # Return the property value:
108  return self._props[ name ]
109 
110  def __setattr__( self, key, value ):
111  """Set an service property on an existing configuration object
112 
113  This function allows us to set/override properties on an service
114  configuration object. Allowing for the following syntax:
115 
116  service = ...
117  service.IntProperty = 66
118  service.FloatProperty = 3.141592
119  service.StringProperty = "Foo"
120 
121  Keyword arguments:
122  key -- The key/name of the property
123  value -- The value to set for the property
124  """
125 
126  # Private variables should be set directly:
127  if key[ 0 ] == '_':
128  return super( AsgServiceConfig, self ).__setattr__( key, value )
129 
130  # Set the property, and remember its value:
131  super( AsgServiceConfig,
132  self ).setPropertyFromString( key, stringPropValue( value ) )
133  self._props[ key ] = copy.deepcopy( value )
134  pass
135 
136  def __eq__( self, other ):
137  """Check for equality with another object
138 
139  The implementation of this is very simple. We only check that the type
140  and the name of the services would match.
141  """
142 
143  # First check that the other object is also an AsgServiceConfig one:
144  if not isinstance( other, AsgServiceConfig ):
145  return False
146 
147  # Now check whether the type and the name of the services agree:
148  return ( ( self.type() == other.type() ) and
149  ( self.name() == other.name() ) )
150 
151  def __ne__( self, other ):
152  """Check for an inequality with another object
153 
154  This is just defined to make the '!=' operator of Python behave
155  consistently with the '==' operator for such objects.
156  """
157  return not self.__eq__( other )
158 
159  def __str__( self ):
160  """Print the service configuration in a user friendly way
161 
162  This is just to help with debugging configurations, allowing
163  the user to get a nice printout of their job configuration.
164  """
165 
166  name = 'Service %s/%s' % ( self.type(), self.name() )
167  result = AsgServiceConfig._printHeader( name )
168  result += '\n'
169  for key, value in sorted( self._props.items() ):
170  if isinstance( value, str ):
171  printedValue = "'%s'" % value
172  else:
173  printedValue = value
174  pass
175  result += "|- %s: %s\n" % ( key, indentBy( printedValue, "| " ) )
176  pass
177  result += AsgServiceConfig._printFooter( name )
178  return result
179 
180  def addPrivateTool( self, name, type ):
181  """Create a private tool for the service
182 
183  This function is used in 'standalone' mode to declare a private tool
184  for the service, or a private tool for an already declared private
185  tool.
186 
187  Can be used like:
188  config.addPrivateTool( 'tool1', 'ToolType1' )
189  config.addPrivateTool( 'tool1.tool2', 'ToolType2' )
190 
191  Keyword arguments:
192  name -- The full name of the private tool
193  type -- The C++ type of the private tool
194  """
195 
196  # And now set up the Python object that will take care of setting
197  # properties on this tool.
198 
199  # Tokenize the tool's name. In case it is a subtool of a tool, or
200  # something possibly even deeper.
201  toolNames = name.split( '.' )
202 
203  # Look up the component that we need to set up the private tool on.
204  component = self
205  for tname in toolNames[ 0 : -1 ]:
206  component = getattr( component, tname )
207  pass
208 
209  # Check that the component doesn't have such a (tool) property yet.
210  if hasattr( component, toolNames[ -1 ] ):
211  raise RuntimeError( "Tool with name '%s' already exists" % name )
212  pass
213 
214  # Now set up a smart object as a property on that component.
215  component._props[ toolNames[ -1 ] ] = PrivateToolConfig( self, name,
216  type )
217 
218  # Finally, tell the C++ code what to do.
219  self.createPrivateTool( name, type ).ignore()
220 
221  pass
222 
223  @staticmethod
224  def _printHeader( title ):
225  """Produce a nice header when printing the configuration
226 
227  This function is used for printing the header of both services
228  and tools.
229 
230  Keyword arguments:
231  indentString -- String used as indentation
232  title -- The title of the service/tool
233  """
234 
235  preLength = AsgServiceConfig.printHeaderPre
236  postLength = AsgServiceConfig.printHeaderWidth - 3 - preLength - \
237  len( title )
238  return '/%s %s %s' % ( preLength * '*', title, postLength * '*' )
239 
240  @staticmethod
241  def _printFooter( title ):
242  """Produce a nice footer when printing the configuration
243 
244  This function is used for printing the footer of both services
245  and tools.
246 
247  Keyword arguments:
248  indentString -- String used as indentation
249  title -- The title of the service/tool
250  """
251 
252  preLength = AsgServiceConfig.printHeaderPre
253  postLength = AsgServiceConfig.printHeaderWidth - 12 - preLength - \
254  len( title )
255  return '\\%s (End of %s) %s' % ( preLength * '-', title,
256  postLength * '-' )
257 
258  pass
259 
260 
262  """Standalone Private Tool Configuration
263 
264  This class is used to mimic the behaviour of Athena tool configurable
265  classes. To be able to set the properties of private tools used by
266  dual-use services in a way that's valid for both Athena and EventLoop.
267  """
268 
269  def __init__( self, service, prefix, type ):
270  """Constructor for an private tool configuration object
271  """
272 
273  self._service = service
274  self._prefix = prefix
275  self._type = type
276  self._props = {}
277 
278  pass
279 
280  def __getattr__( self, name ):
281  """Get a previously set property value from the configuration
282 
283  This function allows us to retrieve the value of a tool property that
284  was already set for an service's private tool, to possibly use it in
285  some configuration decisions in the Python code itself.
286 
287  Keyword arguments:
288  name -- The name of the property
289  """
290 
291  # Fail if the property was not (yet) set:
292  if not name in self._props:
293  raise AttributeError( 'Property "%s" was not set on "%s/%s.%s"' %
294  ( name, self._service.type(),
295  self._service.name(), self._prefix ) )
296 
297  # Return the property value:
298  return self._props[ name ]
299 
300  def __setattr__( self, key, value ):
301  """Set a tool property on an existing configuration object
302 
303  This function allows us to set/override properties on a private tool
304  of an service configuration object. Allowing for the following syntax:
305 
306  service = ...
307  service.Tool.IntProperty = 66
308  service.Tool.FloatProperty = 3.141592
309  service.Tool.StringProperty = "Foo"
310 
311  Keyword arguments:
312  key -- The key/name of the property
313  value -- The value to set for the property
314  """
315 
316  # Private variables should be set directly:
317  if key[ 0 ] == '_':
318  return super( PrivateToolConfig, self ).__setattr__( key, value )
319 
320  # Construct the full name, used in the C++ code:
321  fullName = self._prefix + "." + key
322 
323  # Set the property, and remember its value:
324  self._service.setPropertyFromString( fullName,
325  stringPropValue( value ) )
326  self._props[ key ] = copy.deepcopy( value )
327  pass
328 
329  def __str__( self ):
330  """Print the private tool configuration in a user friendly way
331 
332  This is just to help with debugging configurations, allowing
333  the user to get a nice printout of their job configuration.
334  """
335 
336  name = 'Private Tool %s/%s' % ( self._type, self._prefix )
337  result = ' \n'
338  result += AsgServiceConfig._printHeader( name )
339  result += '\n'
340  for key, value in sorted( self._props.items() ):
341  if isinstance( value, str ):
342  printedValue = "'%s'" % value
343  else:
344  printedValue = value
345  pass
346  result += "|- %s: %s\n" % ( key, indentBy( printedValue, "| " ) )
347  pass
348  result += AsgServiceConfig._printFooter( name )
349  return result
350 
351  pass
352 
353 
354 def stringPropValue( value ):
355  """Helper function producing a string property value"""
356 
357  stringValue = str( value )
358  if isinstance( value, bool ):
359  stringValue = str( int( value ) )
360  pass
361  return stringValue
362 
363 
364 def indentBy( propValue, indent ):
365  """Helper function used in the configuration printout"""
366 
367  stringValue = str( propValue )
368  result = ""
369  for stringLine in stringValue.split( '\n' ):
370  if len( result ):
371  result += "\n" + indent
372  pass
373  result += stringLine
374  pass
375  return result
376 
377 
378 #
379 # Declare some unit tests for the code
380 #
381 
382 
383 class TestServiceTypeAndName( unittest.TestCase ):
384 
385 
387  def test_singletypename( self ):
388  config1 = AsgServiceConfig( "TypeName" )
389  self.assertEqual( config1.type(), "TypeName" )
390  self.assertEqual( config1.name(), "TypeName" )
391  config2 = AsgServiceConfig( "NS::SomeType" )
392  self.assertEqual( config2.type(), "NS::SomeType" )
393  self.assertEqual( config2.name(), "NS::SomeType" )
394  pass
395 
396 
398  def test_typeandname( self ):
399  config1 = AsgServiceConfig( "TypeName/InstanceName" )
400  self.assertEqual( config1.type(), "TypeName" )
401  self.assertEqual( config1.name(), "InstanceName" )
402  config2 = AsgServiceConfig( "NS::SomeType/Instance" )
403  self.assertEqual( config2.type(), "NS::SomeType" )
404  self.assertEqual( config2.name(), "Instance" )
405  pass
406 
407 
408 class TestServiceProperties( unittest.TestCase ):
409 
410 
411  def setUp( self ):
412  self.config = AsgServiceConfig( "Type/Name" )
413  pass
414 
415 
416  def test_propaccess( self ):
417  self.config.Prop1 = "Value1"
418  self.config.Prop2 = [ "Value2" ]
419  self.assertEqual( self.config.Prop1, "Value1" )
420  self.assertEqual( self.config.Prop2, [ "Value2" ] )
421  self.assertNotEqual( self.config.Prop1, "Foo" )
422  self.assertNotEqual( self.config.Prop2, "Value2" )
423  pass
424 
425 
426  def test_nonexistentprop( self ):
427  with self.assertRaises( AttributeError ):
428  value = self.config.Prop3
429  pass
430  pass
431 
432 
433 class TestServicePrivateTool( unittest.TestCase ):
434 
435 
436  def setUp( self ):
437  self.config = AsgServiceConfig( "ServiceType/ServiceName" )
438  pass
439 
440 
441  def test_privatetool( self ):
442  self.config.addPrivateTool( "Tool1", "ToolType1" )
443  self.config.Tool1.Prop1 = "Value1"
444  self.config.Tool1.Prop2 = [ 1, 2, 3 ]
445  self.assertEqual( self.config.Tool1.Prop1, "Value1" )
446  self.assertEqual( self.config.Tool1.Prop2, [ 1, 2, 3 ] )
447  pass
448 
449 
451  self.config.addPrivateTool( "Tool1", "ToolType1" )
452  self.config.addPrivateTool( "Tool1.Tool2", "ToolType2" )
453  self.config.Tool1.Tool2.Prop3 = "Foo"
454  self.config.Tool1.Tool2.Prop4 = [ "Bar" ]
455  self.assertEqual( self.config.Tool1.Tool2.Prop3, "Foo" )
456  self.assertEqual( self.config.Tool1.Tool2.Prop4, [ "Bar" ] )
457  pass
458 
459 
460  def test_nonexistentprop( self ):
461  self.config.addPrivateTool( "Tool1", "ToolType1" )
462  with self.assertRaises( AttributeError ):
463  value = self.config.Tool1.BadProp
464  pass
465  self.config.addPrivateTool( "Tool1.Tool2", "ToolType2" )
466  with self.assertRaises( AttributeError ):
467  value = self.config.Tool1.Tool2.BadProp
468  pass
469  pass
470 
471 
472  def test_nonexistenttool( self ):
473  with self.assertRaises( AttributeError ):
474  self.config.addPrivateTool( "BadTool.Tool4", "BadToolType" )
475  pass
476  pass
python.AsgServiceConfig.PrivateToolConfig._type
_type
Definition: AsgServiceConfig.py:275
python.AsgServiceConfig.TestServiceProperties
Test case for the service property handling.
Definition: AsgServiceConfig.py:408
python.AsgServiceConfig.PrivateToolConfig.__getattr__
def __getattr__(self, name)
Definition: AsgServiceConfig.py:280
python.AsgServiceConfig.TestServicePrivateTool.test_privatetoolofprivatetool
def test_privatetoolofprivatetool(self)
Test setting up and using a private tool of a private tool.
Definition: AsgServiceConfig.py:450
python.AsgServiceConfig.stringPropValue
def stringPropValue(value)
Definition: AsgServiceConfig.py:354
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
python.DualUseConfig.addPrivateTool
def addPrivateTool(alg, toolName, typeName)
Definition: DualUseConfig.py:180
python.AsgServiceConfig.TestServiceTypeAndName.test_singletypename
def test_singletypename(self)
Test that the type and name are set correctly when using a single argument.
Definition: AsgServiceConfig.py:387
python.AsgServiceConfig.TestServiceProperties.setUp
def setUp(self)
Common setup for the tests.
Definition: AsgServiceConfig.py:411
python.AsgServiceConfig.PrivateToolConfig
Definition: AsgServiceConfig.py:261
python.AsgServiceConfig.TestServicePrivateTool.test_privatetool
def test_privatetool(self)
Test setting up and using one private tool.
Definition: AsgServiceConfig.py:441
python.AsgServiceConfig.PrivateToolConfig._props
_props
Definition: AsgServiceConfig.py:276
python.AsgServiceConfig.AsgServiceConfig.addPrivateTool
def addPrivateTool(self, name, type)
Definition: AsgServiceConfig.py:180
python.AsgServiceConfig.PrivateToolConfig.__setattr__
def __setattr__(self, key, value)
Definition: AsgServiceConfig.py:300
python.AsgServiceConfig.AsgServiceConfig._printHeader
def _printHeader(title)
Definition: AsgServiceConfig.py:224
python.AsgServiceConfig.AsgServiceConfig.__eq__
def __eq__(self, other)
Definition: AsgServiceConfig.py:136
DiTauMassTools::ignore
void ignore(T &&)
Definition: PhysicsAnalysis/TauID/DiTauMassTools/DiTauMassTools/HelperFunctions.h:54
python.AsgServiceConfig.indentBy
def indentBy(propValue, indent)
Definition: AsgServiceConfig.py:364
python.AsgServiceConfig.AsgServiceConfig.getName
def getName(self)
Definition: AsgServiceConfig.py:73
python.AsgServiceConfig.TestServicePrivateTool.test_nonexistenttool
def test_nonexistenttool(self)
Test that private tools can't be set up on not-yet-declared tools.
Definition: AsgServiceConfig.py:472
python.AsgServiceConfig.TestServicePrivateTool
Test case for using private tools.
Definition: AsgServiceConfig.py:433
python.AsgServiceConfig.TestServicePrivateTool.test_nonexistentprop
def test_nonexistentprop(self)
Test that unset properties on the tools can't be used.
Definition: AsgServiceConfig.py:460
python.AsgServiceConfig.TestServiceProperties.test_propaccess
def test_propaccess(self)
Test that properties that got set, can be read back.
Definition: AsgServiceConfig.py:416
python.AsgServiceConfig.PrivateToolConfig._service
_service
Definition: AsgServiceConfig.py:273
python.AsgServiceConfig.TestServicePrivateTool.config
config
Definition: AsgServiceConfig.py:437
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename T::value_type > sorted(T begin, T end)
Helper function to create a sorted vector from an unsorted one.
python.AsgServiceConfig.TestServiceProperties.test_nonexistentprop
def test_nonexistentprop(self)
Test that an unset property can't be accessed.
Definition: AsgServiceConfig.py:426
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:195
python.AsgServiceConfig.AsgServiceConfig.__str__
def __str__(self)
Definition: AsgServiceConfig.py:159
python.AsgServiceConfig.TestServiceTypeAndName.test_typeandname
def test_typeandname(self)
Test that specifying the type and name separately in the same string works as expected.
Definition: AsgServiceConfig.py:398
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:79
python.AsgServiceConfig.TestServicePrivateTool.setUp
def setUp(self)
Set up the main service object to test.
Definition: AsgServiceConfig.py:436
python.AsgServiceConfig.AsgServiceConfig.__init__
def __init__(self, typeAndName, **kwargs)
Definition: AsgServiceConfig.py:45
python.AsgServiceConfig.TestServiceTypeAndName
Test case for the service type/name handling.
Definition: AsgServiceConfig.py:383
python.AsgServiceConfig.AsgServiceConfig.__ne__
def __ne__(self, other)
Definition: AsgServiceConfig.py:151
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
python.AsgServiceConfig.TestServiceProperties.config
config
Definition: AsgServiceConfig.py:412
python.AsgServiceConfig.AsgServiceConfig.__getattr__
def __getattr__(self, name)
Definition: AsgServiceConfig.py:91
pickleTool.object
object
Definition: pickleTool.py:30
str
Definition: BTagTrackIpAccessor.cxx:11
python.AsgServiceConfig.AsgServiceConfig.__setattr__
def __setattr__(self, key, value)
Definition: AsgServiceConfig.py:110
python.AsgServiceConfig.AsgServiceConfig.getType
def getType(self)
Definition: AsgServiceConfig.py:82
python.AsgServiceConfig.PrivateToolConfig._prefix
_prefix
Definition: AsgServiceConfig.py:274
python.AsgServiceConfig.PrivateToolConfig.__str__
def __str__(self)
Definition: AsgServiceConfig.py:329
python.AsgServiceConfig.AsgServiceConfig._printFooter
def _printFooter(title)
Definition: AsgServiceConfig.py:241
python.AsgServiceConfig.AsgServiceConfig
Definition: AsgServiceConfig.py:17
python.AsgServiceConfig.AsgServiceConfig._props
_props
Definition: AsgServiceConfig.py:63
python.AsgServiceConfig.PrivateToolConfig.__init__
def __init__(self, service, prefix, type)
Definition: AsgServiceConfig.py:269