ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
Control
AthToolSupport
AsgServices
python
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
261
class
PrivateToolConfig
( object ):
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
450
def
test_privatetoolofprivatetool
( self ):
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.AsgServiceConfig
Definition
AsgServiceConfig.py:17
python.AsgServiceConfig.AsgServiceConfig.__setattr__
__setattr__(self, key, value)
Definition
AsgServiceConfig.py:110
python.AsgServiceConfig.AsgServiceConfig.addPrivateTool
addPrivateTool(self, name, type)
Definition
AsgServiceConfig.py:180
python.AsgServiceConfig.AsgServiceConfig.__eq__
__eq__(self, other)
Definition
AsgServiceConfig.py:136
python.AsgServiceConfig.AsgServiceConfig._printFooter
_printFooter(title)
Definition
AsgServiceConfig.py:241
python.AsgServiceConfig.AsgServiceConfig.getType
getType(self)
Definition
AsgServiceConfig.py:82
python.AsgServiceConfig.AsgServiceConfig.__init__
__init__(self, typeAndName, **kwargs)
Definition
AsgServiceConfig.py:45
python.AsgServiceConfig.AsgServiceConfig._props
dict _props
Definition
AsgServiceConfig.py:63
python.AsgServiceConfig.AsgServiceConfig.__getattr__
__getattr__(self, name)
Definition
AsgServiceConfig.py:91
python.AsgServiceConfig.AsgServiceConfig._printHeader
_printHeader(title)
Definition
AsgServiceConfig.py:224
python.AsgServiceConfig.AsgServiceConfig.__ne__
__ne__(self, other)
Definition
AsgServiceConfig.py:151
python.AsgServiceConfig.AsgServiceConfig.__str__
__str__(self)
Definition
AsgServiceConfig.py:159
python.AsgServiceConfig.AsgServiceConfig.getName
getName(self)
Definition
AsgServiceConfig.py:73
python.AsgServiceConfig.PrivateToolConfig
Definition
AsgServiceConfig.py:261
python.AsgServiceConfig.PrivateToolConfig._prefix
_prefix
Definition
AsgServiceConfig.py:274
python.AsgServiceConfig.PrivateToolConfig._props
dict _props
Definition
AsgServiceConfig.py:276
python.AsgServiceConfig.PrivateToolConfig.__init__
__init__(self, service, prefix, type)
Definition
AsgServiceConfig.py:269
python.AsgServiceConfig.PrivateToolConfig.__str__
__str__(self)
Definition
AsgServiceConfig.py:329
python.AsgServiceConfig.PrivateToolConfig._service
_service
Definition
AsgServiceConfig.py:273
python.AsgServiceConfig.PrivateToolConfig.__setattr__
__setattr__(self, key, value)
Definition
AsgServiceConfig.py:300
python.AsgServiceConfig.PrivateToolConfig._type
_type
Definition
AsgServiceConfig.py:275
python.AsgServiceConfig.PrivateToolConfig.__getattr__
__getattr__(self, name)
Definition
AsgServiceConfig.py:280
python.AsgServiceConfig.TestServicePrivateTool
Test case for using private tools.
Definition
AsgServiceConfig.py:433
python.AsgServiceConfig.TestServicePrivateTool.config
config
Definition
AsgServiceConfig.py:437
python.AsgServiceConfig.TestServicePrivateTool.test_privatetoolofprivatetool
test_privatetoolofprivatetool(self)
Test setting up and using a private tool of a private tool.
Definition
AsgServiceConfig.py:450
python.AsgServiceConfig.TestServicePrivateTool.test_nonexistentprop
test_nonexistentprop(self)
Test that unset properties on the tools can't be used.
Definition
AsgServiceConfig.py:460
python.AsgServiceConfig.TestServicePrivateTool.test_nonexistenttool
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_privatetool
test_privatetool(self)
Test setting up and using one private tool.
Definition
AsgServiceConfig.py:441
python.AsgServiceConfig.TestServicePrivateTool.setUp
setUp(self)
Set up the main service object to test.
Definition
AsgServiceConfig.py:436
python.AsgServiceConfig.TestServiceProperties
Test case for the service property handling.
Definition
AsgServiceConfig.py:408
python.AsgServiceConfig.TestServiceProperties.test_propaccess
test_propaccess(self)
Test that properties that got set, can be read back.
Definition
AsgServiceConfig.py:416
python.AsgServiceConfig.TestServiceProperties.setUp
setUp(self)
Common setup for the tests.
Definition
AsgServiceConfig.py:411
python.AsgServiceConfig.TestServiceProperties.test_nonexistentprop
test_nonexistentprop(self)
Test that an unset property can't be accessed.
Definition
AsgServiceConfig.py:426
python.AsgServiceConfig.TestServiceProperties.config
config
Definition
AsgServiceConfig.py:412
python.AsgServiceConfig.TestServiceTypeAndName
Test case for the service type/name handling.
Definition
AsgServiceConfig.py:383
python.AsgServiceConfig.TestServiceTypeAndName.test_singletypename
test_singletypename(self)
Test that the type and name are set correctly when using a single argument.
Definition
AsgServiceConfig.py:387
python.AsgServiceConfig.TestServiceTypeAndName.test_typeandname
test_typeandname(self)
Test that specifying the type and name separately in the same string works as expected.
Definition
AsgServiceConfig.py:398
python.AsgServiceConfig.indentBy
indentBy(propValue, indent)
Definition
AsgServiceConfig.py:364
python.AsgServiceConfig.stringPropValue
stringPropValue(value)
Definition
AsgServiceConfig.py:354
type
Generated on
for ATLAS Offline Software by
1.17.0