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