ATLAS Offline Software
Loading...
Searching...
No Matches
ComponentAccumulator.py
Go to the documentation of this file.
2# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3
4import GaudiConfig2
5from GaudiKernel.DataHandle import DataHandle
6import GaudiKernel.GaudiHandles as GaudiHandles
7
8from AthenaCommon.Logging import logging
9from AthenaCommon.Debugging import DbgStage
10from AthenaCommon.CFElements import (isSequence, findSubSequence, findAlgorithm, iterSequences,
11 checkSequenceConsistency, findAllAlgorithmsByName)
12
13from AthenaConfiguration.AccumulatorCache import AccumulatorCachable
14from AthenaConfiguration.ComponentFactory import CompFactory, isComponentAccumulatorCfg
15from AthenaConfiguration.Deduplication import deduplicate, deduplicateOne, DeduplicationFailed
16from AthenaConfiguration.DebuggingContext import (Context, raiseWithCurrentContext, shortCallStack,
17 createContextForDeduplication)
18
19import atexit
20from collections.abc import Sequence
21import sys,os
22
23class ConfigurationError(RuntimeError):
24 pass
25
26# Always create these services in this order:
27_basicServicesToCreateOrder=("CoreDumpSvc/CoreDumpSvc",
28 "DBReplicaSvc/DBReplicaSvc",
29 "GeoModelSvc/GeoModelSvc",
30 "DetDescrCnvSvc/DetDescrCnvSvc")
31
32# Disable check of unmerged CA if we are exiting. This avoids an error on exceptions but
33# also the need to mark CAs as merged in top-level scripts (e.g. unit tests).
34def __exit():
35 ComponentAccumulator._checkUnmerged = False
36atexit.register(__exit)
37
38
39def printProperties(msg, c, nestLevel = 0, printDefaults=False):
40
41 # Dictionary of (default and) explicitly set values with latter taking precedence
42 props = {**c.getDefaultProperties(), **c._properties} if printDefaults else c._properties
43
44 for propname, propval in sorted(props.items()):
45
46 # Recursively expand these types:
47 if isinstance(propval, GaudiConfig2.Configurable):
48 msg.info("%s * %s: %s", " "*nestLevel, propname, propval.getFullJobOptName())
49 printProperties(msg, propval, nestLevel+3, printDefaults)
50
51 elif isinstance(propval, GaudiHandles.PrivateToolHandleArray):
52 msg.info( "%s * %s: PrivateToolHandleArray of size %s", " "*nestLevel, propname, len(propval))
53 for el in propval:
54 msg.info( "%s * %s/%s", " "*(nestLevel+3), el.__cpp_type__, el.getName())
55 printProperties(msg, el, nestLevel+6, printDefaults)
56
57 # Only print handle keys:
58 elif isinstance(propval, DataHandle):
59 propval = propval.Path
60
61 msg.info("%s * %s: %r", " "*nestLevel, propname, propval)
62
63
64def filterComponents (comps, onlyComponents = []):
65 ret = []
66 for c in comps:
67 if not onlyComponents or c.getName() in onlyComponents:
68 ret.append((c, True))
69 elif c.getName()+'-' in onlyComponents:
70 ret.append((c, False))
71 return ret
72
73
74class ComponentAccumulator(AccumulatorCachable):
75 # the debug mode is combination of the following strings:
76 # trackCA - to track CA creation,
77 # track[EventAlgo|CondAlgo|PublicTool|PrivateTool|Service|Sequence] - to track categories components addition
78 debugMode=""
79 _checkUnmerged = True
80
81 def __init__(self,sequence='AthAlgSeq'):
82 # Ensure that we are not operating in the legacy Athena Configurable mode
83 # where only a single global instance exists
84 if not isComponentAccumulatorCfg():
86 """
87 ComponentAccumulator initialised with legacy (global) Configurable behavior!
88 CA Deduplication is impossible in this mode.
89 Create the CA using the AthenaCommon.Configurable.ConfigurableCABehavior context manager.
90 """
91 )
92 self._msg=logging.getLogger('ComponentAccumulator')
93 if isinstance(sequence, str):
94 kwargs={'IgnoreFilterPassed' : True,
95 'StopOverride' : True }
96 if sequence == 'AthAlgSeq' :
97 kwargs.setdefault('ProcessDynamicDataDependencies',True)
98 kwargs.setdefault('ExtraDataForDynamicConsumers',[])
99
100 # (Nested) default sequence of event processing algorithms per sequence + their private tools
101 sequence = CompFactory.AthSequencer(sequence, **kwargs)
102
103 self._sequence = sequence
105 self._algorithms = {} #Dictionary of algorithm instances keyed by name
106 self._conditionsAlgs = [] #Unordered list of conditions algorithms + their private tools
107 self._services = [] #List of service, not yet sure if the order matters here in the MT age
109 self._auditors = [] #List of auditors
110 self._privateTools = None #A placeholder to carry a private tool(s) not yet attached to its parent
111 self._primaryComp = None #A placeholder to designate the primary service
112 self._currentDomain = None #Currently marked PerfMon domain
113 self._domainsRegistry = {} #PerfMon domains registry
114
115 self._theAppProps = dict() #Properties of the ApplicationMgr
116
117 #Backward compatibility hack: Allow also public tools:
118 self._publicTools = []
119
120 #To check if this accumulator was merged:
121 self._wasMerged = False
122 self._isMergable = True
123 self._lastAddedComponent = "Unknown"
124 self._creationCallStack = Context.hint if "trackCA" not in ComponentAccumulator.debugMode else shortCallStack()
125 self._componentsContext = dict()
126 self._debugStage = DbgStage()
127 self.interactive = ""
128
129 def setAsTopLevel(self):
130 self._isMergable = False
131
132 def _inspect(self): #Create a string some basic info about this CA, useful for debugging
133 summary = "This CA contains {0} service(s), {1} conditions algorithm(s), {2} event algorithm(s) and {3} public tool(s):\n"\
134 .format(len(self._services),len(self._conditionsAlgs),len(self._algorithms),len(self._publicTools))
135
136 if self._privateTools:
137 if isinstance(self._privateTools, list):
138 summary += " Private AlgTool: " + self._privateTools[-1].getFullJobOptName() + "\n"
139 else:
140 summary += " Private AlgTool: " + self._privateTools.getFullJobOptName() + "\n"
141
142 if self._primaryComp:
143 summary += " Primary Component: " + self._primaryComp.getFullJobOptName() + "\n"
144
145 summary += " Sequence(s): " + ", ".join([s.name+(" (main)" if s == self._sequence else "") for s in self._allSequences]) + "\n"
146 summary += " Last component added: " + self._lastAddedComponent+"\n"
147 summary += " Created by: " + self._creationCallStack
148 return summary
149
150 def _cleanup(self):
151 # Delete internal data structures, to be called after all properties are transferred to the C++ application
152 del self._sequence
153 del self._allSequences
154 del self._algorithms
155 del self._conditionsAlgs
156 del self._services
157 del self._publicTools
158 del self._auditors
159
160 # Clear all AccumulatorCaches
161 from AthenaConfiguration.AccumulatorCache import AccumulatorDecorator
162 AccumulatorDecorator.clearCache()
163
164 # Run garbage collector
165 import gc
166 gc.collect()
167
168 def empty(self):
169 return (len(self._sequence.Members)+len(self._conditionsAlgs)+len(self._services)+
170 len(self._publicTools)+len(self._theAppProps) == 0)
171
172 def __del__(self):
173 if self._checkUnmerged and not getattr(self,'_wasMerged',True) and not self.empty():
174 log = logging.getLogger("ComponentAccumulator")
175 log.error("ComponentAccumulator was never merged. %s\n", self._inspect())
176 import traceback
177 traceback.print_stack()
178 if getattr(self,'_privateTools',None) is not None:
179 log = logging.getLogger("ComponentAccumulator")
180 log.error("Deleting a ComponentAccumulator with dangling private tool(s): %s",
181 " ".join([t.name for t in self._privateTools]) if isinstance(self._privateTools, Sequence) else self._privateTools.name)
182
183 def _cacheEvict(self):
184 """Called by AccumulatorCache when deleting item from cache"""
185 self.popPrivateTools(quiet=True)
186 self.wasMerged()
187
188 def __getstate__(self):
189 state = self.__dict__.copy()
190 # Remove the unpicklable entries.
191 del state['_msg']
192 return state
193
194 def __setstate__(self,state):
195 self.__dict__.update(state)
196 #Re-enstate logger
197 self._msg=logging.getLogger('ComponentAccumulator')
198
199
200 def printCondAlgs(self, summariseProps=False, onlyComponents=[], printDefaults=False):
201 self._msg.info( "Condition Algorithms" )
202 for (c, flag) in filterComponents (self._conditionsAlgs, onlyComponents):
203 self._msg.info( " \\__ %s (cond alg)%s", c.name, self._componentsContext.get(c.name,""))
204 if summariseProps and flag:
205 printProperties(self._msg, c, 1, printDefaults)
206 return
207
208
209 # If onlyComponents is set, then only print components with names
210 # that appear in the onlyComponents list. If a name is present
211 # in the list with a trailing `-', then only the name of the component
212 # will be printed, not its properties.
213 def printConfig(self, withDetails=False, summariseProps=False,
214 onlyComponents = [], printDefaults=False, printSequenceTreeOnly=False, prefix=None):
215 msg = logging.getLogger(prefix) if prefix else self._msg
216
217 msg.info( "Event Algorithm Sequences" )
218
219 def printSeqAndAlgs(seq, nestLevel = 0,
220 onlyComponents = []):
221 def __prop(name):
222 if name in seq._properties:
223 return seq._properties[name]
224 return seq._descriptors[name].default
225 if withDetails:
226 msg.info( "%s\\__ %s (seq: %s %s)", " "*nestLevel, seq.name,
227 "SEQ" if __prop("Sequential") else "PAR",
228 "OR" if __prop("ModeOR") else "AND" + self._componentsContext.get(seq.name, "") )
229 else:
230 msg.info( "%s\\__ %s", " "*nestLevel, seq.name)
231
232 nestLevel += 3
233 for (c, flag) in filterComponents(seq.Members, onlyComponents):
234 if isSequence(c):
235 printSeqAndAlgs(c, nestLevel, onlyComponents = onlyComponents )
236 else:
237 if withDetails:
238 msg.info( "%s\\__ %s (alg) %s", " "*nestLevel, c.getFullJobOptName(), self._componentsContext.get(c.name, ""))
239 else:
240 msg.info( "%s\\__ %s", " "*nestLevel, c.name )
241 if summariseProps and flag:
242 printProperties(msg, c, nestLevel, printDefaults)
243
244
245 for n,s in enumerate(self._allSequences):
246 msg.info( "Top sequence %d", n )
247 printSeqAndAlgs(s, onlyComponents = onlyComponents)
248
249 if printSequenceTreeOnly:
250 return
251
252 self.printCondAlgs (summariseProps = summariseProps,
253 onlyComponents = onlyComponents)
254 msg.info( "Services" )
255 msg.info( [ s[0].name + (" (created) " if s[0].name in self._servicesToCreate else "")
256 for s in filterComponents (self._services, onlyComponents) ] )
257 msg.info( "Public Tools" )
258 msg.info( "[" )
259 for (t, flag) in filterComponents (self._publicTools, onlyComponents):
260 msg.info( " %s,", t.getFullJobOptName() + self._componentsContext.get(t.name,""))
261 # Not nested, for now
262 if summariseProps and flag:
263 printProperties(msg, t, printDefaults)
264 msg.info( "]" )
265 msg.info( "Private Tools")
266 msg.info( "[" )
267 if self._privateTools:
268 for tool in self._privateTools if isinstance(self._privateTools, Sequence) else [self._privateTools]:
269 msg.info( " %s,", tool.getFullJobOptName() + self._componentsContext.get(tool.name,""))
270 if summariseProps:
271 printProperties(msg, tool, printDefaults)
272 msg.info( "]" )
273 if self._auditors:
274 msg.info( "Auditors" )
275 msg.info( [ a[0].name for a in filterComponents(self._auditors, onlyComponents) ] )
276
277 msg.info( "theApp properties" )
278 for k, v in self._theAppProps.items():
279 msg.info(" %s : %s", k, v)
280
281 def getIO(self):
282 """
283 Returns information about inputs needed and outputs produced by this CA
284
285 It is a list of dictionaries containing the: type, key, R / W, the component and name of the property via which it is set
286 """
287 def __getHandles(comp):
288 io = []
289 for i in comp.ExtraInputs:
290 io.append({"type": i[0],
291 "key": i[1],
292 "comp": comp.getFullJobOptName(),
293 "mode": "R",
294 "prop": "ExtraInputs"})
295 for i in comp.ExtraOutputs:
296 io.append({"type": i[0],
297 "key": i[1],
298 "comp": comp.getFullJobOptName(),
299 "mode": "W",
300 "prop": "ExtraOutputs"})
301
302 for prop, descr in comp._descriptors.items():
303 if isinstance(descr.default, DataHandle):
304 io.append( {"type": descr.default.type(),
305 "key": comp._properties[prop] if prop in comp._properties else descr.default.path(),
306 "comp": comp.getFullJobOptName(),
307 "mode": descr.default.mode(),
308 "prop": prop })
309 # TODO we should consider instantiating c++ defaults and fetching corresponsing props
310 if "PrivateToolHandle" == descr.cpp_type and prop in comp._properties:
311 io.extend( __getHandles(comp._properties[prop]) )
312 if "PrivateToolHandleArray" == descr.cpp_type and prop in comp._properties:
313 for tool in getattr(comp, prop):
314 io.extend( __getHandles(tool))
315 return io
316
317 ret = []
318 for comp in self._allComponents():
319 ret.extend(__getHandles(comp))
320 return ret
321
322
323 def addSequence(self, newseq, primary=False, parentName = None ):
324 """ Adds new sequence. If second argument is present then it is added under another sequence """
325
326 if not isSequence(newseq):
327 raise TypeError('{} is not a sequence'.format(newseq.name))
328
329 if not isinstance(newseq, GaudiConfig2.Configurable):
330 raise ConfigurationError('{} is not the Conf2 Sequence, ComponentAccumulator handles only the former'.format(newseq.name))
331
332 algorithmsInside = findAllAlgorithmsByName(newseq)
333 if len(algorithmsInside) != 0:
334 raise ConfigurationError('{} contains algorithms (or sub-sequences contain them). That is not supported. Construct ComponentAccumulator and merge it instead'.format(newseq.name))
335
336
337 if parentName is None:
338 parent=self._sequence
339 else:
340 parent = findSubSequence(self._sequence, parentName )
341 if parent is None:
342 raise ConfigurationError("Missing sequence {} to add new sequence to".format(parentName))
343
344 parent.Members.append(newseq)
345 if "trackSequence" in ComponentAccumulator.debugMode:
346 self._componentsContext[newseq] = shortCallStack()
347
348 if primary:
349 if self._primaryComp:
350 self._msg.warning("addEventAlgo: Overwriting primary component of this CA. Was %s/%s, now %s/%s",
351 self._primaryComp.__cpp_type__, self._primaryComp.name,
352 newseq.__cpp_type__, newseq.name)
353 #keep a ref of the sequence as primary component
354 self._primaryComp = newseq
355 return newseq
356
357
358 def getSequence(self,sequenceName=None):
359 if sequenceName is None:
360 return self._sequence
361 else:
362 return findSubSequence(self._sequence,sequenceName)
363
364 def setPrivateTools(self,privTool):
365 """Use this method to carry private AlgTool(s) to the caller when returning this ComponentAccumulator.
366 The method accepts either a single private AlgTool or a list of private AlgTools (typically assigned to ToolHandleArray)
367 """
368 if self._privateTools is not None:
369 raise ConfigurationError("This ComponentAccumulator holds already a (list of) private tool(s). "
370 "Only one (list of) private tool(s) is allowed")
371
372 if isinstance(privTool, Sequence):
373 for t in privTool:
374 if t.__component_type__ != 'AlgTool':
375 raise ConfigurationError("ComponentAccumulator.setPrivateTools accepts only ConfigurableAlgTools "
376 f"or lists of ConfigurableAlgTools. Encountered {type(t)} in a list")
377 else:
378 if privTool.__component_type__ != "AlgTool":
379 raise ConfigurationError("ComponentAccumulator.setPrivateTools accepts only ConfigurableAlgTools "
380 f"or lists of ConfigurableAlgTools. Encountered {type(privTool)}")
381
382 self._privateTools=privTool
383 if "trackPrivateTool" in ComponentAccumulator.debugMode:
384 for tool in self._privateTools if isinstance(privTool, Sequence) else [self._privateTools]:
385 self._componentsContext[tool.name] = shortCallStack()
386
387 return
388
389 def popPrivateTools(self, quiet=False):
390 """Get the (list of) private AlgTools from this ComponentAccumulator.
391 The CA will not keep any reference to the AlgTool. Throw an exception if
392 no tools are available unless quiet=True.
393 """
394 tool = self._privateTools
395 if not quiet and tool is None:
396 raise ConfigurationError("Private tool(s) requested, but none are present")
397 self._privateTools=None
398 return tool
399
400 def popToolsAndMerge(self, other):
401 """ Merging in the other accumulator and getting the (list of) private AlgTools
402 from this ComponentAccumulator.
403 """
404 if other is None:
405 raise RuntimeError("popToolsAndMerge called on object of type None: "
406 "did you forget to return a CA from a config function?")
407 tool = other.popPrivateTools()
408 self.merge(other)
409 return tool
410
412 """ Get the current PerfMon domain. """
413 return self._currentDomain
414
415 def flagPerfmonDomain(self, name):
416 """ Mark the beginning of a new PerfMon domain. """
417 self._msg.debug(f"Toggling the current algorithm domain to {name}")
418 self._currentDomain = name
419
421 """ The actual registry keeps "alg":"domain".
422 This function inverts the registry to get "domain":["algs"].
423 """
424 result = {}
425 for i, v in self._domainsRegistry.items():
426 result[v] = [i] if v not in result.keys() else result[v] + [i]
427 return result
428
429 def getAlgPerfmonDomain(self, name):
430 """ Return the PerfMon domain of the given algorithm """
431 if name in self._domainsRegistry:
432 return self._domainsRegistry[name]
433 else:
434 self._msg.info(f"Algorithm {name} is not in PerfMon domains registry")
435 return None
436
437 def addAlgToPerfmonDomains(self, name, domain, overwrite=False):
438 """ Add the algorithm to the domains registry. """
439 if name not in self._domainsRegistry:
440 if domain:
441 self._domainsRegistry[name] = domain
442 self._msg.debug(f"Added algorithm {name} to the PerfMon domain {domain}")
443 else:
444 if overwrite and domain:
445 self._msg.info(f"Reassigned algorithm {name} "
446 f"from {self._domainsRegistry[name]} "
447 f"to {domain} PerfMon domain")
448 self._domainsRegistry[name] = domain
449 else:
450 self._msg.debug(f"Algorithm {name} is already in the PerfMon "
451 "domain, if you want to reassign do overwrite=True")
452
454 """ Print the PerfMon domains. """
455 invertedDomains = self.getInvertedPerfmonDomains()
456 self._msg.info(":: This CA contains the following PerfMon domains ::")
457 self._msg.info(f":: There are a total of {len(self._domainsRegistry)} "
458 f"registered algorithms in {len(invertedDomains)} domains ::")
459 for domain, algs in invertedDomains.items():
460 self._msg.info(f"+ Domain : {domain}")
461 for alg in algs:
462 self._msg.info("\\_ %s", alg)
463 self._msg.info(":: End of PerfMon domains ::")
464
465 def addEventAlgo(self, algorithms,sequenceName=None,primary=False,domain=None):
466 if not isinstance(algorithms, Sequence):
467 #Swallow both single algorithms as well as lists or tuples of algorithms
468 algorithms=[algorithms,]
469
470 if sequenceName is None:
471 # If there is an AthAlgSeq add the event algorithm there by default
472 # See ATEAM-825 for a more detailed discussion for this choice
473 seq = findSubSequence(self._sequence, 'AthAlgSeq')
474 if seq is None:
475 seq = self._sequence
476 else:
477 seq = findSubSequence(self._sequence, sequenceName)
478 if seq is None:
479 self.printConfig()
480 raise ConfigurationError("Can not find sequence {}".format(sequenceName))
481
482 for algo in algorithms:
483 if not isinstance(algo, GaudiConfig2.Configurable):
484 raise TypeError(f"Attempt to add wrong type: {type(algo).__name__} as event algorithm")
485
486 if algo.__component_type__ != "Algorithm":
487 raise TypeError(f"Attempt to add an {algo.__component_type__} as event algorithm")
488
489 if algo.name in self._algorithms:
490 context = createContextForDeduplication("Merging with existing Event Algorithm", algo.name, self._componentsContext) # noqa : F841
491 deduplicateOne(algo, self._algorithms[algo.name])
492 deduplicateOne(self._algorithms[algo.name], algo)
493 else:
494 self._algorithms[algo.name]=algo
495
496 existingAlgInDest = findAlgorithm(seq, algo.name)
497 if not existingAlgInDest:
498 seq.Members.append(self._algorithms[algo.name])
499 # Assign the algorithm to a domain
500 self.addAlgToPerfmonDomains(algo.name, self._currentDomain if not domain else domain)
501
502 if primary:
503 if len(algorithms)>1:
504 self._msg.warning("Called addEvenAlgo with a list of algorithms and primary==True. "
505 "Designating the first algorithm as primary component")
506 if self._primaryComp:
507 self._msg.warning("addEventAlgo: Overwriting primary component of this CA. Was %s/%s, now %s/%s",
508 self._primaryComp.__cpp_type__, self._primaryComp.name,
509 algorithms[0].__cpp_type__, algorithms[0].name)
510 #keep a ref of the algorithm as primary component
511 self._primaryComp = algorithms[0]
512 self._lastAddedComponent = algorithms[-1].name
513
514 if "trackEventAlgo" in ComponentAccumulator.debugMode:
515 for algo in algorithms:
516 self._componentsContext[algo.name] = shortCallStack()
517
518 return None
519
520 def getEventAlgo(self, name=None):
521 """Get algorithm with `name`"""
522 if name not in self._algorithms:
523 raise ConfigurationError("Can not find an algorithm of name {} ".format(name))
524 return self._algorithms[name]
525
526 def getEventAlgos(self, seqName=None):
527 """Get all algorithms within sequence"""
528 seq = self._sequence if seqName is None else findSubSequence(self._sequence, seqName )
529 return [s for s in iterSequences(seq) if not isSequence(s)]
530
531 def addCondAlgo(self,algo,primary=False,domain=None):
532 """Add Conditions algorithm"""
533 if not isinstance(algo, GaudiConfig2.Configurable):
534 raise TypeError(f"Attempt to add wrong type: {type(algo).__name__} as conditions algorithm")
535
536 if algo.__component_type__ != "Algorithm":
537 raise TypeError(f"Attempt to add wrong type: {algo.__component_type__} as conditions algorithm")
538
539 context = createContextForDeduplication("Merging with existing Conditions Algorithm", algo.name, self._componentsContext) # noqa : F841
540
541 deduplicate(algo, self._conditionsAlgs) #will raise on conflict
542 if primary:
543 if self._primaryComp:
544 self._msg.warning("addCondAlgo: Overwriting primary component of this CA. Was %s/%s, now %s/%s",
545 self._primaryComp.__cpp_type__, self._primaryComp.name,
546 algo.__cpp_type__, algo.name)
547 #keep a ref of the de-duplicated conditions algorithm as primary component
548 self._primaryComp = self.__getOne(self._conditionsAlgs, algo.name, "ConditionsAlgos")
549
550 self._lastAddedComponent=algo.name
551 if "trackCondAlgo" in ComponentAccumulator.debugMode:
552 self._componentsContext[algo.name] = shortCallStack()
553
554 # Assign the algorithm to a domain
555 self.addAlgToPerfmonDomains(algo.name, 'Conditions' if not domain else domain)
556
557 return algo
558
559 def getCondAlgos(self):
560 """Get all conditions algorithms"""
561 return self._conditionsAlgs
562
563 def getCondAlgo(self, name):
564 """Get conditions algorithm by name"""
565 return self.__getOne( self._conditionsAlgs, name, "conditions algorithms")
566
567 def addService(self, newSvc, primary=False, create=False):
568 """Add service and return the deduplicated instance"""
569 if not isinstance(newSvc, GaudiConfig2.Configurable):
570 raise TypeError(f"Attempt to add wrong type: {type(newSvc).__name__} as service")
571
572 if newSvc.__component_type__ != "Service":
573 raise TypeError(f"Attempt to add wrong type: {newSvc.__component_type__} as service")
574
575 context = createContextForDeduplication("Merging with existing Service", newSvc.name, self._componentsContext) # noqa : F841
576
577 deduplicate(newSvc, self._services) #may raise on conflict
578 if primary:
579 if self._primaryComp:
580 self._msg.warning("addService: Overwriting primary component of this CA. Was %s/%s, now %s/%s",
581 self._primaryComp.__cpp_type__, self._primaryComp.name,
582 newSvc.__cpp_type__, newSvc.name)
583 #keep a ref of the de-duplicated service as primary component
584 self._primaryComp=self.__getOne( self._services, newSvc.name, "Services")
585 self._lastAddedComponent=newSvc.name
586
587 if create:
588 sname = newSvc.getFullJobOptName()
589 if sname not in self._servicesToCreate:
590 self._servicesToCreate.append(sname)
591 if "trackService" in ComponentAccumulator.debugMode:
592 self._componentsContext[newSvc.name] = shortCallStack()
593 return self.__getOne( self._services, newSvc.name, "Services")
594
595
596 def addAuditor(self, auditor):
597 """Add Auditor to ComponentAccumulator and return the deduplicated instance.
598 This function will also create the required AuditorSvc."""
599 if not isinstance(auditor, GaudiConfig2.Configurable):
600 raise TypeError(f"Attempt to add wrong type: {type(auditor).__name__} as auditor")
601
602 if auditor.__component_type__ != "Auditor":
603 raise TypeError(f"Attempt to add wrong type: {auditor.__component_type__} as auditor")
604
605 context = createContextForDeduplication("Merging with existing auditors", auditor.name, self._componentsContext) # noqa : F841
606
607 deduplicate(auditor, self._auditors) #may raise on conflict
608 newAuditor = self.addService(CompFactory.AuditorSvc(Auditors=[auditor.getFullJobOptName()]))
609 self._lastAddedComponent = auditor.name
610 return newAuditor
611
612
613 def addPublicTool(self, newTool, primary=False):
614 """Add public tool and return the deduplicated instance."""
615 if not isinstance(newTool, GaudiConfig2.Configurable):
616 raise TypeError(f"Attempt to add wrong type: {type(newTool).__name__} as public AlgTool")
617
618 if newTool.__component_type__ != "AlgTool":
619 raise TypeError(f"Attempt to add wrong type: {newTool.__component_type__} as public AlgTool")
620
621 context = createContextForDeduplication("Merging with existing Public Tool", newTool.name, self._componentsContext) # noqa : F841
622
623 deduplicate(newTool,self._publicTools)
624 if primary:
625 if self._primaryComp:
626 self._msg.warning("addPublicTool: Overwriting primary component of this CA. Was %s/%s, now %s/%s",
627 self._primaryComp.__cpp_type__, self._primaryComp.name,
628 newTool.__cpp_type__, newTool.name)
629 #keep a ref of the de-duplicated tool as primary component
630 self._primaryComp=self.__getOne( self._publicTools, newTool.name, "Public Tool")
631 self._lastAddedComponent=newTool.name
632 if "trackPublicTool" in ComponentAccumulator.debugMode:
633 self._componentsContext[newTool.name] = shortCallStack()
634 # return the new public tool
635 return self.__getOne(self._publicTools, newTool.name, "Public Tool")
636
637
638 def getPrimary(self):
639 """Get designated primary component"""
640 if self._privateTools:
641 return self.popPrivateTools()
642 elif self._primaryComp:
643 return self._primaryComp
644 else:
645 raise ConfigurationError("Called getPrimary() but no primary component nor private AlgTool is known.\n{}".format(self._inspect()))
646
647 def getPrimaryAndMerge(self, other):
648 """ Merging in the other accumulator and getting the primary component"""
649 if other is None:
650 raise RuntimeError("merge called on object of type None: did you forget to return a CA from a config function?")
651 comp = other.getPrimary()
652 self.merge(other)
653 return comp
654
655 def __getOne(self, allcomps, name=None, typename="???"):
656 selcomps = allcomps if name is None else [ t for t in allcomps if t.name == name ]
657 if len( selcomps ) == 0:
658 raise ConfigurationError(f"Requested component of name {name} but is missing" )
659
660 if len( selcomps ) == 1:
661 return selcomps[0]
662 nmstr = f'with name {name} ' if name else ''
663 raise ConfigurationError("Number of {} available {}{} which is != 1 expected by this API".format(typename, nmstr, len(selcomps)) )
664
665 def getPublicTools(self):
666 return self._publicTools
667
668 def getPublicTool(self, name=None):
669 """Returns single public tool, exception if either not found or to many found"""
670 return self.__getOne( self._publicTools, name, "PublicTools")
671
672 def getServices(self):
673 return self._services
674
675 def getService(self, name=None):
676 """Returns single service, exception if either not found or to many found"""
677 if name is None:
678 return self._primarySvc
679 else:
680 return self.__getOne( self._services, name, "Services")
681
682 def getAuditor(self,name):
683 """Retuns a single auditor, exception if not found"""
684 return self.__getOne(self._auditors,name,"Auditors")
685
686 def dropEventAlgo(self,name,sequence="AthAlgSeq"):
687 s=self.getSequence(sequence)
688 lenBefore=len(s.Members)
689 s.Members = [a for a in s.Members if not a.getName()==name]
690 lenAfter=len(s.Members)
691 if lenAfter == lenBefore:
692 self._msg.warning("Algorithm %s not found in sequence %s",name,sequence)
693 else:
694 self._msg.info("Removed algorithm %s from sequence %s",name,sequence)
695 try:
696 del self._algorithms[name]
697 except KeyError:
698 self._msg.warning("Algorithm %s not found in self._sequence ???",name)
699 return
700
701 def popEventAlgo(self,name,sequence="AthAlgSeq"):
702 s=self.getSequence(sequence)
703 lenBefore=len(s.Members)
704 s.Members = [a for a in s.Members if not a.getName()==name]
705 lenAfter=len(s.Members)
706 if lenAfter == lenBefore:
707 self._msg.warning("Algorithm %s not found in sequence %s",name,sequence)
708 else:
709 self._msg.info("Removed algorithm %s from sequence %s",name,sequence)
710 try:
711 return self._algorithms.pop(name)
712 except KeyError:
713 self._msg.warning("Algorithm %s not found in self._sequence ??? Returning 'None'",name)
714 return None
715
716 def dropCondAlgo(self,name):
717 lenBefore=len(self._conditionsAlgs)
718 self._conditionsAlgs = [a for a in self._conditionsAlgs if a.getName()!=name]
719 lenAfter=len(self._conditionsAlgs)
720 if lenAfter == lenBefore:
721 self._msg.warning("Condition Algorithm %s not found",name)
722 else:
723 self._msg.info("Removed conditions Algorithm %s",name)
724 return
725
726 def dropService(self,name):
727 lenBefore=len(self._services)
728 self._services = [s for s in self._services if s.getName()!=name]
729 lenAfter=len(self._services)
730 if lenAfter == lenBefore:
731 self._msg.warning("Service %s not found",name)
732 else:
733 self._msg.info("Removed Service %s",name)
734 return
735
736 def dropPublicTool(self,name):
737 lenBefore=len(self._publicTools)
738 self._publicTools = [p for p in self._publicTools if p.getName()!=name]
739 lenAfter=len(self._publicTools)
740 if lenAfter == lenBefore:
741 self._msg.warning("Public tool %s not found",name)
742 else:
743 self._msg.info("Removed public tool %s",name)
744 return
745
746 def dropAuditor(self,name):
747 lenBefore=len(self._auditors)
748 self._auditors = [a for a in self._auditors if a.getName()!=name]
749 lenAfter=len(self._auditors)
750 if lenAfter == lenBefore:
751 self._msg.warning("Auditor %s not found",name)
752 else:
753 self._msg.info("Removed auditor %s",name)
754 return
755
756 def getAppProps(self):
757 return self._theAppProps
758
759 def setAppProperty(self,key,value,overwrite=False):
760 if (overwrite or key not in (self._theAppProps)):
761 self._theAppProps[key]=value
762 else:
763 if self._theAppProps[key] == value:
764 self._msg.debug("ApplicationMgr property '%s' already set to '%s'.", key, value)
765 elif isinstance(self._theAppProps[key], Sequence) and not isinstance(self._theAppProps[key],str):
766 value=self._theAppProps[key] + [el for el in value if el not in self._theAppProps[key]]
767 self._msg.info("ApplicationMgr property '%s' already set to '%s'. Overwriting with %s", key, self._theAppProps[key], value)
768 self._theAppProps[key]=value
769 else:
770 raise DeduplicationFailed("AppMgr property {} set twice: {} and {}".format(key, self._theAppProps[key], value))
771
772
773 def setDebugStage(self,stage):
774 if stage not in DbgStage.allowed_values:
775 raise RuntimeError("Allowed arguments for setDebugStage are [{}]".format(",".join(DbgStage.allowed_values)))
776 self._debugStage.value = stage
777
778
779 def merge(self,other, sequenceName=None):
780 """Merging in the other accumulator"""
781 if other is None:
782 raise RuntimeError("merge called on object of type None: "
783 "did you forget to return a CA from a config function?")
784
785 if not isinstance(other,ComponentAccumulator):
786 raise TypeError(f"Attempt to merge wrong type {type(other).__name__}. "
787 "Only instances of ComponentAccumulator can be added")
788
789 context = (Context.hint if not ComponentAccumulator.debugMode else # noqa: F841
790 Context("When merging the ComponentAccumulator:\n{} \nto:\n{}".format(other._inspect(), self._inspect())))
791
792 if other._privateTools is not None:
793 if isinstance(other._privateTools, Sequence):
794 raiseWithCurrentContext(RuntimeError(
795 "merge called on ComponentAccumulator with a dangling (array of) private tools\n"))
796 else:
797 raiseWithCurrentContext(RuntimeError(
798 "merge called on ComponentAccumulator with a dangling private tool "
799 f"{other._privateTools.__cpp_type__}/{other._privateTools.name}"))
800
801 if not other._isMergable:
802 raiseWithCurrentContext(ConfigurationError(
803 "Attempted to merge a top level ComponentAccumulator. Revert the order of merging\n"))
804
805 def mergeSequences( dest, src ):
806
807 # Ensure sequences of same name have same properties. This is called many times
808 # and is highly optimized. Make sure you profile before modifying this code.
809 if dest.name == src.name:
810 # Compare all set properties ignoring 'Members'
811 props = (dest._properties.keys() | src._properties.keys()) - {'Members'}
812 for seqProp in props:
813 try:
814 if dest._properties[seqProp] != src._properties[seqProp]:
815 raise RuntimeError(
816 f"Merging two sequences with name '{dest.name}' but property '{seqProp}' "
817 f"has different values: {getattr(dest, seqProp)} vs {getattr(src, seqProp)}")
818 except KeyError as e:
819 raise RuntimeError(
820 f"Merging two sequences with name '{dest.name}' but property '{e}' is not "
821 f"set in one of them")
822
823 for childIdx, c in enumerate(src.Members):
824 if isSequence( c ):
825 sub = findSubSequence( dest, c.name ) #depth=1 ???
826 if sub:
827 mergeSequences(sub, c )
828 else:
829 self._msg.debug(" Merging sequence %s to a destination sequence %s", c.name, dest.name )
830 algorithmsByName = findAllAlgorithmsByName(c) # dictionary: algName (alg, parentSeq, indexInParentSeq)
831 for name, existingAlgs in algorithmsByName.items():
832 # all algorithms from incoming CA are already deduplicated, so we can only handle the fist one
833 algInstance, _, _ = existingAlgs[0]
834 if name not in self._algorithms:
835 self._algorithms[name] = algInstance
836 else:
837 dedupContext1 = createContextForDeduplication("While merging sequences adding incoming algorithm", c.name, other._componentsContext) # noqa : F841
838 dedupContext2 = createContextForDeduplication("While merging sequences adding to existing algorithm", c.name, self._componentsContext) # noqa : F841
839 deduplicateOne(self._algorithms[name], algInstance)
840 deduplicateOne(algInstance, self._algorithms[name])
841 for _, parent, idx in existingAlgs: # put the deduplicated algo back into original sequences
842 parent.Members[idx] = self._algorithms[name]
843 # Add the algorithm to the PerfMon domains
844 self.addAlgToPerfmonDomains(name, other._domainsRegistry[name] if name in other._domainsRegistry else self._currentDomain)
845 dest.Members.append(c)
846
847 else: # an algorithm
848 if c.name in self._algorithms:
849 dedupContext1 = createContextForDeduplication("While merging sequences adding incoming algorithm", c.name, other._componentsContext) # noqa : F841
850 dedupContext2 = createContextForDeduplication("While merging sequences adding to existing algorithm", c.name, self._componentsContext) # noqa : F841
851
852 deduplicateOne(self._algorithms[c.name], c)
853 deduplicateOne(c, self._algorithms[c.name])
854 src.Members[childIdx] = self._algorithms[c.name]
855 else:
856 self._algorithms[c.name] = c
857
858 existingAlgInDest = findAlgorithm( dest, c.name, depth=1 )
859 if not existingAlgInDest:
860 self._msg.debug(" Adding algorithm %s to a sequence %s", c.name, dest.name )
861 dest.Members.append(c)
862
863 # Add the algorithm to the PerfMon domains
864 self.addAlgToPerfmonDomains(c.name, other._domainsRegistry[c.name] if c.name in other._domainsRegistry else self._currentDomain)
865
866 # Merge sequences:
867 # mergeSequences(destSeq, other._sequence)
868 # if sequenceName is provided it means we should be ignoring the actual MAIN seq name there and use the sequenceName
869 # that means the first search in the destination sequence needs to be cheated
870 # the sequenceName argument is only relevant for the MAIN sequence,
871 # secondary top sequences are treated as if the sequenceName argument would not be provided
872
873 for otherSeq in other._allSequences:
874 found=False
875 for ourSeq in self._allSequences:
876 destSeqName = otherSeq.name
877 if sequenceName and otherSeq == other._sequence: # if sequence moving is requested (sequenceName != None) it concerns only the main sequence
878 destSeqName = sequenceName
879 self._msg.verbose(" Will move sequence %s to %s", otherSeq.name, destSeqName )
880
881 ourSeq = findSubSequence(ourSeq, destSeqName) # try to add sequence to the main structure first, to each seq in parent?
882 if ourSeq:
883 mergeSequences(ourSeq, otherSeq)
884 found=True
885 self._msg.verbose(" Succeeded to merge sequence %s to %s", otherSeq.name, ourSeq.name )
886 else:
887 self._msg.verbose(" Failed to merge sequence %s to any existing one, destination CA will have several top/dangling sequences", otherSeq.name )
888 if not found: # just copy the sequence as a dangling one
889 self._allSequences.append( otherSeq )
890 mergeSequences( self._allSequences[-1], otherSeq )
891
892
893
894
895
896 # Additional checking and updating other accumulator's algorithms list
897 for name in other._algorithms:
898 if name not in self._algorithms:
899 raiseWithCurrentContext(ConfigurationError('Error in merging. Algorithm {} missing in destination accumulator\n'.format(name)))
900 other._algorithms[name] = self._algorithms[name]
901
902 #self._conditionsAlgs+=other._conditionsAlgs
903 for condAlg in other._conditionsAlgs:
904 addContext = createContextForDeduplication("Merging incoming Conditions Algorithm", condAlg.name, other._componentsContext) # noqa : F841
905 self.addCondAlgo(condAlg) #Profit from deduplicaton here
906
907 for svc in other._services:
908 addContext = createContextForDeduplication("Merging incoming Service", svc.name, other._componentsContext) # noqa : F841
909 self.addService(svc, create = svc.getFullJobOptName() in other._servicesToCreate) #Profit from deduplicaton here
910
911 for pt in other._publicTools:
912 addContext = createContextForDeduplication("Merging incoming Public Tool", pt.name, other._componentsContext) # noqa : F841
913 self.addPublicTool(pt) #Profit from deduplicaton here
914
915
916 for aud in other._auditors:
917 addContext = createContextForDeduplication("Merging incoming Auditor", aud.name, other._componentsContext) # noqa : F841
918 self.addAuditor(aud) #Profit from deduplicaton here
919
920 #Merge AppMgr properties:
921 for (k,v) in other._theAppProps.items():
922 self.setAppProperty(k,v) #Will warn about overrides
923 pass
924 other._wasMerged=True
925
926 self._lastAddedComponent = other._lastAddedComponent #+ ' (Merged)'
927 self._componentsContext.update(other._componentsContext) # update the context so it contains an information about the new components (and refreshed old components)
928
929 def __verifyFinalSequencesStructure(self):
930 if len(self._allSequences) != 1:
931 raiseWithCurrentContext(ConfigurationError('It is not allowed for the storable CA to have more than one top sequence, now it has: {}'
932 .format(','.join([ s.name for s in self._allSequences]))))
933
934
935 def wasMerged(self):
936 """ Declares CA as merged
937
938 This is temporarily needed by HLT and should not be used elsewhere
939 """
940 self._wasMerged=True
941
942 def _allComponents(self):
943 """ returns iterable over all components """
944 import itertools
945 return itertools.chain(self._publicTools,
946 self._privateTools if self._privateTools else [],
947 self._algorithms.values(),
948 self._conditionsAlgs)
949
950
951 def store(self,outfile, withDefaultHandles=False):
952 """
953 Saves CA in pickle form
954
955 when withDefaultHandles is True, also the handles that are not set are saved
956 """
957
958 checkSequenceConsistency(self._sequence)
959
960 self.wasMerged()
961 if withDefaultHandles:
962 from AthenaConfiguration.Utils import loadDefaultComps, exposeHandles
963 loadDefaultComps(self._allComponents())
964 exposeHandles(self._allComponents())
965 import pickle
966 pickle.dump(self,outfile)
967 return
968
969
970 def createApp(self):
971 # Set ROOT batch mode
972 from PyUtils.Helpers import ROOTSetup
973 ROOTSetup(batch = not self.interactive)
974
975 # Create the Gaudi object early.
976 # Without this here, pyroot can sometimes get confused
977 # and report spurious type mismatch errors about this object.
978 import ROOT
979 ROOT.Gaudi
980
981 appPropsToSet, mspPropsToSet, bshPropsToSet = self.gatherProps()
982
983 self._wasMerged = True
984 from Gaudi.Main import BootstrapHelper
985
986 bsh = BootstrapHelper()
987 app = bsh.createApplicationMgr()
988
989 for k, v in appPropsToSet.items():
990 self._msg.debug("Setting property %s : %s", k, v)
991 app.setProperty(k, v)
992
993 # An EventLoopMgr always needs to be explicitly configured as the default
994 # Gaudi one will certainly not work in athena.
995 if "EventLoop" not in appPropsToSet:
996 raise Exception("No EventLoopMgr has been configured. If you are using a custom "
997 "EventLoopMgr, make sure to set the 'EventLoop' App property.")
998
999 app.configure()
1000
1001 msp = app.getService("MessageSvc")
1002 for k, v in mspPropsToSet.items():
1003 self._msg.debug("Setting property %s : %s", k, v)
1004 bsh.setProperty(msp, k.encode(), v.encode())
1005
1006 # Feed the jobO service with the remaining options
1007 for comp, name, value in bshPropsToSet:
1008 self._msg.debug("Adding %s.%s = %s", comp, name, value)
1009 app.setOption(f"{comp}.{name}", value)
1010
1011 sys.stdout.flush()
1012 return app
1013
1014 def gatherProps(self):
1015 appPropsToSet = {k: str(v) for k, v in self._theAppProps.items()}
1016 mspPropsToSet = {}
1017 bshPropsToSet = []
1018 svcToCreate = []
1019 extSvc = []
1020 for svc in self._services:
1021 extSvc += [
1022 svc.getFullJobOptName(),
1023 ]
1024 if svc.getFullJobOptName() in self._servicesToCreate:
1025 svcToCreate.append(svc.getFullJobOptName())
1026
1027 # order basic services
1028 for bs in reversed(_basicServicesToCreateOrder):
1029 if bs in svcToCreate:
1030 svcToCreate.insert(0, svcToCreate.pop( svcToCreate.index(bs) ) )
1031
1032 extSvc.append("PyAthena::PyComponentMgr/PyComponentMgr")
1033
1034 appPropsToSet["ExtSvc"] = str(extSvc)
1035 appPropsToSet["CreateSvc"] = str(svcToCreate)
1036
1037 def getCompsToBeAdded(comp, namePrefix=""):
1038 name = namePrefix + comp.getName()
1039 for k, v in comp._properties.items():
1040 # Handle special cases of properties:
1041 # 1.PrivateToolHandles
1042 if isinstance(v, GaudiConfig2.Configurable):
1043 # Add the name of the tool as property to the parent
1044 bshPropsToSet.append((name, k, v.getFullJobOptName()))
1045 # Recursively add properties of this tool to the JobOptionSvc
1046 getCompsToBeAdded(v, namePrefix=name + ".")
1047 # 2. PrivateToolHandleArray
1048 elif isinstance(v, GaudiHandles.PrivateToolHandleArray):
1049 # Add names of tools as properties to the parent
1050 bshPropsToSet.append(
1051 (name, k, str([v1.getFullJobOptName() for v1 in v]),)
1052 )
1053 # Recursively add properties of tools to JobOptionsSvc
1054 for v1 in v:
1055 getCompsToBeAdded(v1, namePrefix=name + ".")
1056 else:
1057 # For a list of DataHandle, we need to stringify
1058 # each element individually. Otherwise, we get the repr
1059 # version of the elements, which Gaudi JO will choke on.
1060 if isinstance(v, list) and v and isinstance(v[0], DataHandle):
1061 v = [str(x) for x in v]
1062 # For sequences, need to convert the list of algs to names
1063 elif isSequence(comp) and k == "Members":
1064 v = [alg.getFullJobOptName() for alg in comp.Members]
1065 vstr = "" if v is None else str(v)
1066 bshPropsToSet.append((name, k, vstr))
1067
1068 try:
1069 from AthenaPython import PyAthenaComps
1070 PyAlg = PyAthenaComps.Alg
1071 PySvc = PyAthenaComps.Svc
1072 except ImportError:
1073 PyAlg = type(None)
1074 PySvc = type(None)
1075
1076 # Services
1077 for svc in self._services:
1078 if svc.getName() != "MessageSvc": # MessageSvc will exist already! Needs special treatment
1079 getCompsToBeAdded(svc)
1080 if isinstance(svc, PySvc):
1081 svc.setup()
1082 else:
1083 mspPropsToSet.update((k,str(v)) for k,v in svc._properties.items())
1084
1085 # Algorithms and Sequences
1086 for alg in iterSequences(self._sequence):
1087 getCompsToBeAdded(alg)
1088 if isinstance(alg, PyAlg):
1089 alg.setup()
1090
1091 # Cond Algs
1092 condalgseq = []
1093 for alg in self._conditionsAlgs:
1094 getCompsToBeAdded(alg)
1095 condalgseq.append(alg.getFullJobOptName())
1096 if isinstance(alg, PyAlg):
1097 alg.setup()
1098 bshPropsToSet.append(("AthCondSeq", "Members", str(condalgseq)))
1099
1100 # Public Tools
1101 for pt in self._publicTools:
1102 getCompsToBeAdded(pt, namePrefix="ToolSvc.")
1103
1104 # Auditors
1105 for aud in self._auditors:
1106 getCompsToBeAdded(aud)
1107
1108 return appPropsToSet, mspPropsToSet, bshPropsToSet
1109
1110 def run(self,maxEvents=None):
1111 from os import environ
1112 outpklfile = environ.get("PICKLECAFILE", None)
1113 if outpklfile is not None:
1114 if outpklfile: # non-empty string
1115 self._msg.info("Storing configuration in pickle file %s",outpklfile)
1116 with open(outpklfile, "wb") as f:
1117 self.store(f)
1118 else: # empty string, just exit
1119 self.wasMerged()
1120 self._msg.info("Exiting after configuration stage")
1121 from Gaudi.Main import BootstrapHelper
1122 return BootstrapHelper.StatusCode(True)
1123
1124 # Make sure python output is flushed before triggering output from Gaudi.
1125 # Otherwise, observed output ordering may differ between py2/py3.
1126 sys.stdout.flush()
1127
1128
1129 #Set TDAQ_ERS_NO_SIGNAL_HANDLERS to avoid interference with
1130 #TDAQ signal handling
1131 environ['TDAQ_ERS_NO_SIGNAL_HANDLERS']='1'
1132 from AthenaCommon.Debugging import allowPtrace, hookDebugger
1133 allowPtrace()
1134
1135 checkSequenceConsistency(self._sequence)
1136
1137 app = self.createApp()
1138 self.__verifyFinalSequencesStructure()
1139
1140 #Determine maxEvents
1141 if maxEvents is None:
1142 if "EvtMax" in self._theAppProps:
1143 maxEvents=self._theAppProps["EvtMax"]
1144 else:
1145 maxEvents=-1
1146
1147 if self.interactive == 'init':
1148 printInteractiveMsg_init()
1149 from sys import exit # noqa: F401
1150 startInteractive(locals())
1151
1152 #At this point, we don't need the internal structures of this CA any more, clean them up
1153 self._cleanup()
1154
1155 self._msg.info(f"Athena job with pid {os.getpid()}")
1156
1157 if (self._debugStage.value == "init"):
1158 hookDebugger()
1159 sc = app.initialize()
1160 if not sc.isSuccess():
1161 self._msg.error("Failed to initialize AppMgr")
1162 return sc
1163
1164 sc = app.start()
1165 if not sc.isSuccess():
1166 self._msg.error("Failed to start AppMgr")
1167 return sc
1168
1169 if (self._debugStage.value=="exec"):
1170 hookDebugger()
1171
1172
1173 if self.interactive == 'run':
1174 printInteractiveMsg_run()
1175 from AthenaPython.PyAthena import py_svc
1176 sg=py_svc("StoreGateSvc/StoreGateSvc")
1177 startInteractive(locals())
1178 else:
1179 sc = app.run(maxEvents)
1180 if not sc.isSuccess():
1181 self._msg.error("Failure running application")
1182 return sc
1183
1184 scStop=app.stop()
1185 if not scStop.isSuccess():
1186 self._msg.error("Failed to stop AppMgr")
1187 return scStop
1188
1189 if (self._debugStage.value == "fini"):
1190 hookDebugger()
1191
1192 scFin=app.finalize()
1193 if not scFin.isSuccess():
1194 self._msg.error("Failed to finalize AppMgr")
1195 return scFin
1196
1197 sc1 = app.terminate()
1198 return sc1
1199
1200 def foreach_component(self, path):
1201 """Utility to set properties of components using wildcards.
1202
1203 Example:
1204 ca.foreach_component("*/HLTTop/*/*Hypo*").OutputLevel = VERBOSE
1205
1206 The components name and location in the CF tree are translated into a UNIX-like path
1207 and are matched using the `fnmatch` library. If the property is set successfully
1208 an INFO message is printed else a WARNING.
1209
1210 The convention for paths of nested components is as follows:
1211 Sequence : only the name is used in the path
1212 Algorithm : "type/name" is used
1213 Private Tool : ToolHandle property name plus "type/name" is used
1214 Public Tool : located under "ToolSvc/" and "type/name" is used
1215 Service : located under "SvcMgr/" and "type/name" is used
1216 """
1217 from AthenaConfiguration.PropSetterProxy import PropSetterProxy
1218 return PropSetterProxy(self, path)
1219
1220
1221def startInteractive(localVarDic):
1222 """Setup and start a useful interactive session including auto-completion and history"""
1223 import code
1224
1225 # collect all global and local variables
1226 vars = sys.modules['__main__'].__dict__
1227 vars.update(globals())
1228 vars.update(localVarDic)
1229
1230 # configure the prompt
1231 from AthenaCommon.Interactive import configureInteractivePrompt
1232 configureInteractivePrompt(vars)
1233
1234 # start the interpreter
1235 code.interact(local=vars)
1236
1237
1238def printInteractiveMsg_init():
1239 print("Interactive mode")
1240 print("\tThe ComponentAccumulator is known as 'self', you can inspect it but changes are not taken into account.")
1241 print("\tThe application is known as 'app' but not yet initialized.")
1242 print("\t^D will exit the interactive mode and athena will continue.")
1243 print("\texit() will terminate the program now.")
1244 return
1245
1246
1247def printInteractiveMsg_run():
1248 print("Interactive mode")
1249 print("\tThe application is known as 'app' and initialized.")
1250 print("\tYou can process N events with 'app.run(N)'.")
1251 print("\tStoreGate is accessible as 'sg'.")
1252 print("\t^D will exit the interactive mode and athena will finalize.")
1253 return
1254
1255
1256# Make legacy support available in legacy jobs
1257if not isComponentAccumulatorCfg():
1258 from AthenaConfiguration.LegacySupport import (conf2toConfigurable, # noqa: F401 (for client use)
1259 CAtoGlobalWrapper,
1260 appendCAtoAthena)
1261# and the same names in CA (to support migration) but bomb on calling them
1262else:
1263 def conf2toConfigurable(*args, **kwargs):
1264 raise RuntimeError("conf2toConfigurable cannot be called in a CA job")
1265 def CAtoGlobalWrapper(*args, **kwargs):
1266 raise RuntimeError("CAtoGlobalWrapper cannot be called in a CA job")
1267 def appendCAtoAthena(*args, **kwargs):
1268 raise RuntimeError("appendCAtoAthena cannot be called in a CA job")
const bool debug
static const Attributes_t empty
addEventAlgo(self, algorithms, sequenceName=None, primary=False, domain=None)
printConfig(self, withDetails=False, summariseProps=False, onlyComponents=[], printDefaults=False, printSequenceTreeOnly=False, prefix=None)
__getOne(self, allcomps, name=None, typename="???")
setAppProperty(self, key, value, overwrite=False)
addSequence(self, newseq, primary=False, parentName=None)
printCondAlgs(self, summariseProps=False, onlyComponents=[], printDefaults=False)
addService(self, newSvc, primary=False, create=False)
addAlgToPerfmonDomains(self, name, domain, overwrite=False)
addCondAlgo(self, algo, primary=False, domain=None)
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:132
Definition merge.py:1
filterComponents(comps, onlyComponents=[])
printProperties(msg, c, nestLevel=0, printDefaults=False)