ATLAS Offline Software
AthConfigFlags.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 from copy import copy, deepcopy
4 from difflib import get_close_matches
5 from enum import EnumMeta
6 import glob
7 import importlib
8 from AthenaCommon.Logging import logging
9 from PyUtils.moduleExists import moduleExists
10 from PyUtils.Decorators import deprecate
11 
12 _msg = logging.getLogger('AthConfigFlags')
13 
14 def isGaudiEnv():
15  """Return whether or not this is a gaudi-based (athena) environment"""
16 
17  return moduleExists('Gaudi')
18 
19 class CfgFlag(object):
20  """The base flag object.
21 
22  A flag can be set to either a fixed value or a callable, which computes
23  the value based on other flags.
24  """
25 
26  __slots__ = ['_value', '_setDef', '_type', '_help']
27 
28  _compatibleTypes = {
29  (int, float), # int can be assigned to float flag
30  }
31 
32  def __init__(self, default, type=None, help=None):
33  """Initialise the flag with the default value.
34 
35  Optionally set the type of the flag value and the help string.
36  """
37  if default is None:
38  raise ValueError("Default value of a flag must not be None")
39  self._type = type
40  self._help = help
41  self.set(default)
42  return
43 
44  def set(self, value):
45  """Set the value of the flag.
46 
47  Can be a constant value or a callable.
48  """
49  if callable(value):
50  self._value=None
51  self._setDef=value
52  else:
53  self._value=value
54  self._setDef=None
55  self._validateType(self._value)
56  return
57 
58  def get(self, flagdict=None):
59  """Get the value of the flag.
60 
61  If the currently set value is a callable, a dictionary of all available
62  flags needs to be provided.
63  """
64 
65  if self._value is not None:
66  return deepcopy(self._value)
67 
68  # For cases where the value is intended to be None
69  # i.e. _setDef applied this value, we should not progress
70  if self._setDef is None:
71  return None
72 
73  if not flagdict:
74  raise RuntimeError("Flag is using a callable but all flags are not available.")
75 
76  # Have to call the method to obtain the default value, and then reuse it in all next accesses
77  if flagdict.locked():
78  # optimise future reads, drop possibility to update this flag ever
79  self._value = self._setDef(flagdict)
80  self._setDef = None
81  value = self._value
82  else:
83  # use function for as long as the flags are not locked
84  value = self._setDef(flagdict)
85 
86  self._validateType(value)
87  return deepcopy(value)
88 
89  def __repr__(self):
90  if self._value is not None:
91  return repr(self._value)
92  else:
93  return "[function]"
94 
95  def _validateType(self, value):
96  if (self._type is None or value is None or
97  isinstance(value, self._type) or
98  (type(value), self._type) in self._compatibleTypes):
99  return
100  # Type mismatch
101  raise TypeError(f"Flag is of type '{self._type.__name__}', "
102  f"but value '{value}' of type '{type(value).__name__}' set.")
103 
104 
105 def _asdict(iterator):
106  """Flags to dict converter
107 
108  Used by both FlagAddress and AthConfigFlags. The input must be an
109  iterator over flags to be included in the dict.
110 
111  """
112  outdict = {}
113  for key, item in iterator:
114  x = outdict
115  subkeys = key.split('.')
116  for subkey in subkeys[:-1]:
117  x = x.setdefault(subkey,{})
118  x[subkeys[-1]] = item
119  return outdict
120 
122  def __init__(self, f, name):
123  if isinstance(f, AthConfigFlags):
124  self._flags = f
125  rname = self._flags._renames.get(name, name)
126  self._name = rname
127 
128  elif isinstance(f, FlagAddress):
129  self._flags = f._flags
130  name = f._name+"."+name
131  rname = self._flags._renames.get(name, name)
132  self._name = rname
133 
134  def __getattr__(self, name):
135  return getattr(self._flags, self._name + "." + name)
136 
137  def __setattr__( self, name, value ):
138  if name.startswith("_"):
139  return object.__setattr__(self, name, value)
140  merged = self._name + "." + name
141 
142  if not self._flags.hasFlag( merged ): # flag is misisng, try loading dynamic ones
143  self._flags._loadDynaFlags( merged )
144 
145  if not self._flags.hasFlag( merged ):
146  raise RuntimeError( "No such flag: {} The name is likely incomplete.".format(merged) )
147  return self._flags._set( merged, value )
148 
149  def __delattr__(self, name):
150  del self[name]
151 
152  def __cmp__(self, other):
153  raise RuntimeError( "No such flag: "+ self._name+". The name is likely incomplete." )
154  __eq__ = __cmp__
155  __ne__ = __cmp__
156  __lt__ = __cmp__
157  __le__ = __cmp__
158  __gt__ = __cmp__
159  __ge__ = __cmp__
160 
161  def __bool__(self):
162  raise RuntimeError( "No such flag: "+ self._name+". The name is likely incomplete." )
163 
164  def __getitem__(self, name):
165  return getattr(self, name)
166 
167  def __setitem__(self, name, value):
168  setattr(self, name, value)
169 
170  def __delitem__(self, name):
171  merged = self._name + "." + name
172  del self._flags[merged]
173 
174  def __iter__(self):
175  self._flags.loadAllDynamicFlags()
176  rmap = self._flags._renamed_map()
177  used = set()
178  for flag in self._flags._flagdict.keys():
179  if flag.startswith(self._name.rstrip('.') + '.'):
180  for newflag in rmap[flag]:
181  ntrim = len(self._name) + 1
182  n_dots_in = flag[:ntrim].count('.')
183  remaining = newflag.split('.')[n_dots_in]
184  if remaining not in used:
185  yield remaining
186  used.add(remaining)
187 
188  def _subflag_itr(self):
189  """Subflag iterator specialized for this address
190 
191  """
192  self._flags.loadAllDynamicFlags()
193  address = self._name
194  rename = self._flags._renamed_map()
195  for key in self._flags._flagdict.keys():
196  if key.startswith(address.rstrip('.') + '.'):
197  ntrim = len(address) + 1
198  remaining = key[ntrim:]
199  for r in rename[key]:
200  yield r, getattr(self, remaining)
201 
202  def asdict(self):
203  """Convert to a python dictionary
204 
205  Recursively convert this flag and all subflags into a
206  structure of nested dictionaries. All dynamic flags are
207  resolved in the process.
208 
209  The resulting data structure should be easy to serialize as
210  json or yaml.
211 
212  """
213  d = _asdict(self._subflag_itr())
214  for k in self._name.split('.'):
215  d = d[k]
216  return d
217 
218 
220 
221  def __init__(self):
222  self._flagdict=dict()
223  self._locked=False
224  self._dynaflags = dict()
225  self._loaded = set() # dynamic dlags that were loaded
226  self._categoryCache = set() # cache for already found categories
227  self._hash = None
228  self._parser = None
229  self._args = None # user args from parser
230  self._renames = {}
231 
232  def athHash(self):
233  if self._locked is False:
234  raise RuntimeError("Cannot calculate hash of unlocked flag container")
235  elif self._hash is None:
236  self._hash = self._calculateHash()
237  return self._hash
238 
239  def __hash__(self):
240  raise DeprecationWarning("__hash__ method in AthConfigFlags is deprecated. Probably called from function decorator, use AccumulatorCache decorator instead.")
241 
242  def _calculateHash(self):
243  return hash( (frozenset({k: v for k, v in self._renames.items() if k != v}), id(self._flagdict)) )
244 
245  def __getattr__(self, name):
246  # Avoid infinite recursion looking up our own attributes
247  _flagdict = object.__getattribute__(self, "_flagdict")
248 
249  # First try to get an already loaded flag or category
250  if name in _flagdict:
251  return self._get(name)
252 
253  if self.hasCategory(name):
254  return FlagAddress(self, name)
255 
256  # Reaching here means that we may need to load a dynamic flag
257  self._loadDynaFlags(name)
258 
259  # Try again
260  if name in _flagdict:
261  return self._get(name)
262 
263  if self.hasCategory(name):
264  return FlagAddress(self, name)
265 
266  # Reaching here means that it truly isn't something we know about
267  raise AttributeError(f"No such flag: {name}")
268 
269  def __setattr__(self, name, value):
270  if name.startswith("_"):
271  return object.__setattr__(self, name, value)
272 
273  if name in self._flagdict:
274  return self._set(name, value)
275  raise RuntimeError( "No such flag: "+ name+". The name is likely incomplete." )
276 
277  def __delattr__(self, name):
278  del self[name]
279 
280  def __getitem__(self, name):
281  return getattr(self, name)
282 
283  def __setitem__(self, name, value):
284  setattr(self, name, value)
285 
286  def __delitem__(self, name):
287  self._tryModify()
288  self.loadAllDynamicFlags()
289  for key in list(self._flagdict):
290  if key.startswith(name):
291  del self._flagdict[key]
292  self._categoryCache.clear()
293 
294  def __iter__(self):
295  self.loadAllDynamicFlags()
296  rmap = self._renamed_map()
297  used = set()
298  for flag in self._flagdict:
299  for r in rmap[flag]:
300  first = r.split('.',1)[0]
301  if first not in used:
302  yield first
303  used.add(first)
304 
305  def asdict(self):
306  """Convert to a python dictionary
307 
308  This is identical to the `asdict` in FlagAddress, but for all
309  the flags.
310 
311  """
312  return _asdict(self._subflag_itr())
313 
314 
315  def _renamed_map(self):
316  """mapping from the old names to the new names
317 
318  This is the inverse of _renamed, which maps new names to old
319  names
320 
321  Returns a list of the new names corresponding to the old names
322  (since cloneAndReplace may or may not disable access to the old name,
323  it is possible that an old name renames to multiple new names)
324  """
325  revmap = {}
326 
327  for new, old in self._renames.items():
328  if old not in revmap:
329  revmap[old] = [ new ]
330  else:
331  revmap[old] += [ new ]
332 
333  def rename(key):
334  for old, newlist in revmap.items():
335  if key.startswith(old + '.'):
336  stem = key.removeprefix(old)
337  return [ f'{new}{stem}' if new else '' for new in newlist ]
338  return [ key ]
339 
340  return {x:rename(x) for x in self._flagdict.keys()}
341 
342  def _subflag_itr(self):
343  """Subflag iterator for all flags
344 
345  This is used by the asdict() function.
346  """
347  self.loadAllDynamicFlags()
348 
349  for old, newlist in self._renamed_map().items():
350  for new in newlist:
351  # Lots of modules are missing in analysis releases. I
352  # tried to prevent imports using the _addFlagsCategory
353  # function which checks if some module exists, but this
354  # turned in to quite a rabbit hole. Catching and ignoring
355  # the missing module exception seems to work, even if it's
356  # not pretty.
357  try:
358  yield new, getattr(self, old)
359  except ModuleNotFoundError as err:
360  _msg.debug(f'missing module: {err}')
361  pass
362 
363  def addFlag(self, name, setDef, type=None, help=None):
364  self._tryModify()
365  if name in self._flagdict:
366  raise KeyError("Duplicated flag name: {}".format( name ))
367  self._flagdict[name]=CfgFlag(setDef, type, help)
368  return
369 
370  def addFlagsCategory(self, path, generator, prefix=False):
371  """
372  The path is the beginning of the flag name (e.g. "X" for flags generated with name "X.*").
373  The generator is a function that returns a flags container, the flags have to start with the same path.
374  When the prefix is True the flags created by the generator are prefixed by "path".
375 
376  Supported calls are then:
377  addFlagsCategory("A", g) - where g is function creating flags is f.addFlag("A.x", someValue)
378  addFlagsCategory("A", g, True) - when flags are defined in g like this: f.addFalg("x", somevalue),
379  The latter option allows to share one generator among flags that are later loaded in different paths.
380  """
381  self._tryModify()
382  _msg.debug("Adding flag category %s", path)
383  self._dynaflags[path] = (generator, prefix)
384 
385  def needFlagsCategory(self, name):
386  """ public interface for _loadDynaFlags """
387  self._loadDynaFlags( name )
388 
389  def _loadDynaFlags(self, name):
390  """
391  loads the flags of the form "A.B.C" first attempting the path "A" then "A.B" and then "A.B.C"
392  """
393 
394  def __load_impl( flagBaseName ):
395  if flagBaseName in self._loaded:
396  _msg.debug("Flags %s already loaded",flagBaseName )
397  return
398  if flagBaseName in self._dynaflags:
399  _msg.debug("Dynamically loading the flags under %s", flagBaseName )
400  # Retain locked status and hash
401  isLocked = self._locked
402  myHash = self._hash
403  self._locked = False
404  generator, prefix = self._dynaflags[flagBaseName]
405  self.join( generator(), flagBaseName if prefix else "" )
406  self._locked = isLocked
407  self._hash = myHash
408  del self._dynaflags[flagBaseName]
409  self._loaded.add(flagBaseName)
410 
411  pathfrags = name.split('.')
412  for maxf in range(1, len(pathfrags)+1):
413  __load_impl( '.'.join(pathfrags[:maxf]) )
414 
416  """Force load all the dynamic flags """
417  while len(self._dynaflags) != 0:
418  # Need to convert to a list since _loadDynaFlags may change the dict.
419  for prefix in list(self._dynaflags.keys()):
420  self._loadDynaFlags( prefix )
421 
422  def hasCategory(self, name):
423  # We cache successfully found categories
424  if name in self._categoryCache:
425  return True
426 
427  if name in self._renames:
428  re_name = self._renames[name]
429  if re_name != name:
430  return self.hasCategory(re_name)
431 
432  # If not found do search through all keys.
433  # TODO: could be improved by using a trie for _flagdict
434  for f in self._flagdict.keys():
435  if f.startswith(name+'.'):
436  self._categoryCache.add(name)
437  return True
438  for c in self._dynaflags.keys():
439  if c.startswith(name):
440  self._categoryCache.add(name)
441  return True
442 
443  return False
444 
445  def hasFlag(self, name):
446  return name in [y for x in self._renamed_map().values() for y in x]
447 
448  def _set(self,name,value):
449  self._tryModify()
450  try:
451  self._flagdict[name].set(value)
452  except KeyError:
453  closestMatch = get_close_matches(name,self._flagdict.keys(),1)
454  raise KeyError(f"No flag with name '{name}' found" +
455  (f". Did you mean '{closestMatch[0]}'?" if closestMatch else ""))
456 
457  def _get(self,name):
458  try:
459  return self._flagdict[name].get(self)
460  except KeyError:
461  closestMatch = get_close_matches(name,self._flagdict.keys(),1)
462  raise KeyError(f"No flag with name '{name}' found" +
463  (f". Did you mean '{closestMatch[0]}'?" if closestMatch else ""))
464 
465  @deprecate("Use '[...]' rather than '(...)' to access flags", print_context=True)
466  def __call__(self,name):
467  return self._get(name)
468 
469  def lock(self):
470  if not self._locked:
471  # before locking, parse args if a parser was defined
472  if self._args is None and self._parser is not None: self.fillFromArgs()
473  self._locked = True
474  return
475 
476  def locked(self):
477  return self._locked
478 
479  def _tryModify(self):
480  if self._locked:
481  raise RuntimeError("Attempt to modify locked flag container")
482  else:
483  # if unlocked then invalidate hash
484  self._hash = None
485 
486  def clone(self):
487  """Return an unlocked copy of self (dynamic flags are not loaded)"""
488  cln = AthConfigFlags()
489  cln._flagdict = deepcopy(self._flagdict)
490  cln._dynaflags = copy(self._dynaflags)
491  cln._renames = deepcopy(self._renames)
492  return cln
493 
494 
495  def cloneAndReplace(self,subsetToReplace,replacementSubset, keepOriginal=False):
496  """
497  This is to replace subsets of configuration flags like
498 
499  Example:
500  newflags = flags.cloneAndReplace('Muon', 'Trigger.Offline.Muon')
501  """
502 
503  _msg.debug("cloning flags and replacing %s by %s", subsetToReplace, replacementSubset)
504 
505  self._loadDynaFlags( subsetToReplace )
506  self._loadDynaFlags( replacementSubset )
507 
508  subsetToReplace = subsetToReplace.strip(".")
509  replacementSubset = replacementSubset.strip(".")
510 
511  #Sanity check: Don't replace a by a
512  if (subsetToReplace == replacementSubset):
513  raise RuntimeError(f'Can not replace flags {subsetToReplace} with themselves')
514 
515  # protect against subsequent remaps within remaps: clone = flags.cloneAndReplace('Y', 'X').cloneAndReplace('X.b', 'X.a')
516  for alias,src in self._renames.items():
517  if src == "": continue
518  if src+"." in subsetToReplace:
519  raise RuntimeError(f'Can not replace flags {subsetToReplace} by {replacementSubset} because of already present replacement of {alias} by {src}')
520 
521 
522  newFlags = copy(self) # shallow copy
523  newFlags._renames = deepcopy(self._renames) #maintains renames
524 
525  if replacementSubset in newFlags._renames: #and newFlags._renames[replacementSubset]:
526  newFlags._renames[subsetToReplace] = newFlags._renames[replacementSubset]
527  else:
528  newFlags._renames[subsetToReplace] = replacementSubset
529 
530  if not keepOriginal:
531  if replacementSubset not in newFlags._renames or newFlags._renames[replacementSubset] == replacementSubset:
532  newFlags._renames[replacementSubset] = "" # block access to original flags
533  else:
534  del newFlags._renames[replacementSubset]
535  #If replacementSubset was a "pure renaming" of another set of flags,
536  #the original set of flags gets propagated down to its potential further renamings:
537  #no need to worry about maintaining the intermediate steps in the renaming.
538  else:
539  if replacementSubset not in newFlags._renames:
540  newFlags._renames[replacementSubset] = replacementSubset
541  #For _renamed_map to know that these flags still work.
542  newFlags._hash = None
543  return newFlags
544 
545 
546  def join(self, other, prefix=''):
547  """
548  Merges two flag containers
549  When the prefix is passed each flag from the "other" is prefixed by "prefix."
550  """
551  self._tryModify()
552 
553  for (name,flag) in other._flagdict.items():
554  fullName = prefix+"."+name if prefix != "" else name
555  if fullName in self._flagdict:
556  raise KeyError("Duplicated flag name: {}".format( fullName ) )
557  self._flagdict[fullName]=flag
558 
559  for (name,loader) in other._dynaflags.items():
560  fullName = prefix+"."+name if prefix != "" else name
561  if fullName in self._dynaflags:
562  raise KeyError("Duplicated dynamic flags name: {}".format( fullName ) )
563  _msg.debug("Joining dynamic flags with %s", fullName)
564  self._dynaflags[fullName] = loader
565  return
566 
567  def dump(self, pattern=".*", evaluate=False, formatStr="{:40} : {}", maxLength=None):
568  import re
569  compiled = re.compile(pattern)
570  def truncate(s): return s[:maxLength] + ("..." if maxLength and len(s)>maxLength else "")
571  reverse_renames = {value: key for key, value in self._renames.items() if value != ''} # new name to old
572  for name in sorted(self._flagdict):
573  renamed = name
574  if any([name.startswith(r) for r in reverse_renames.keys()]):
575  for oldprefix, newprefix in reverse_renames.items():
576  if name.startswith(oldprefix):
577  renamed = name.replace(oldprefix, newprefix)
578  break
579  if compiled.match(renamed):
580  if evaluate:
581  try:
582  rep = repr(self._flagdict[name] )
583  val = repr(self._flagdict[name].get(self))
584  if val != rep:
585  print(formatStr.format(renamed,truncate("{} {}".format( val, rep )) ))
586  else:
587  print(formatStr.format(renamed, truncate("{}".format(val)) ) )
588  except Exception as e:
589  print(formatStr.format(renamed, truncate("Exception: {}".format( e )) ))
590  else:
591  print(formatStr.format( renamed, truncate("{}".format(repr(self._flagdict[name] ) )) ))
592 
593  if len(self._dynaflags) != 0 and any([compiled.match(x) for x in self._dynaflags.keys()]):
594  print("Flag categories that can be loaded dynamically")
595  print("{:25} : {:>30} : {}".format( "Category","Generator name", "Defined in" ) )
596  for name,gen_and_prefix in sorted(self._dynaflags.items()):
597  if compiled.match(name):
598  print("{:25} : {:>30} : {}".format( name, gen_and_prefix[0].__name__, '/'.join(gen_and_prefix[0].__code__.co_filename.split('/')[-2:]) ) )
599  if len(self._renames):
600  print("Flag categories that are redirected by the cloneAndReplace")
601  for alias,src in self._renames.items():
602  print("{:30} points to {:>30} ".format( alias, src if src else "nothing") )
603 
604 
605  def initAll(self):
606  """
607  Mostly a self-test method
608  """
609  for n,f in list(self._flagdict.items()):
610  f.get(self)
611  return
612 
613 
614  def getArgumentParser(self, **kwargs):
615  """
616  Scripts calling AthConfigFlags.fillFromArgs can extend this parser, and pass their version to fillFromArgs
617  """
618  import argparse
619  from AthenaCommon.AthOptionsParser import getArgumentParser
620  parser = getArgumentParser(**kwargs)
621  parser.add_argument("---",dest="terminator",action='store_true', help=argparse.SUPPRESS) # special hidden option required to convert option terminator -- for --help calls
622 
623  return parser
624 
625  def parser(self):
626  if self._parser is None: self._parser = self.getArgumentParser()
627  return self._parser
628 
629  def args(self):
630  return self._args
631 
632 
633  def fillFromString(self, flag_string):
634  """Fill the flags from a string of type key=value"""
635  import ast
636 
637  try:
638  key, value = flag_string.split("=")
639  except ValueError:
640  raise ValueError(f"Cannot interpret argument {flag_string}, expected a key=value format")
641 
642  key = key.strip()
643  value = value.strip()
644 
645  # also allow key+=value to append
646  oper = "="
647  if (key[-1]=="+"):
648  oper = "+="
649  key = key[:-1]
650 
651  if not self.hasFlag(key):
652  self._loadDynaFlags( '.'.join(key.split('.')[:-1]) ) # for a flag A.B.C dymanic flags from category A.B
653  if not self.hasFlag(key):
654  raise KeyError(f"{key} is not a known configuration flag")
655 
656  flag_type = self._flagdict[key]._type
657  if flag_type is None:
658  # Regular flag
659  try:
660  ast.literal_eval(value)
661  except Exception: # Can't determine type, assume we got an un-quoted string
662  value=f"\"{value}\""
663 
664  elif isinstance(flag_type, EnumMeta):
665  # Flag is an enum, so we need to import the module containing the enum
666  ENUM = importlib.import_module(flag_type.__module__) # noqa: F841 (used in exec)
667  value=f"ENUM.{value}"
668 
669  # Set the value (this also does the type checking if needed)
670  exec(f"self.{key}{oper}{value}")
671 
672 
673  # parser argument must be an ArgumentParser returned from getArgumentParser()
674  def fillFromArgs(self, listOfArgs=None, parser=None):
675  """
676  Used to set flags from command-line parameters, like flags.fillFromArgs(sys.argv[1:])
677  """
678  import sys
679 
680  self._tryModify()
681 
682  if parser is None:
683  parser = self.parser()
684  self._parser = parser # set our parser to given one
685  argList = listOfArgs or sys.argv[1:]
686  do_help = False
687  # We will now do a pre-parse of the command line arguments to propagate these to the flags
688  # the reason for this is so that we can use the help messaging to display the values of all
689  # flags as they would be *after* any parsing takes place. This is nice to see e.g. the value
690  # that any derived flag (functional flag) will take after, say, the filesInput are set
691  import argparse
692  unrequiredActions = []
693  if "-h" in argList or "--help" in argList:
694  do_help = True
695  if "-h" in argList: argList.remove("-h")
696  if "--help" in argList: argList.remove("--help")
697  # need to unrequire any required arguments in order to do a "pre parse"
698  for a in parser._actions:
699  if a.required:
700  unrequiredActions.append(a)
701  a.required = False
702  (args,leftover)=parser.parse_known_args(argList)
703  for a in unrequiredActions: a.required=True
704 
705  # remove the leftovers from the argList ... for later use in the do_help
706  argList = [a for a in argList if a not in leftover]
707 
708  # First, handle athena.py-like arguments (if available in parser):
709  def arg_set(dest):
710  """Check if dest is available in parser and has been set"""
711  return vars(args).get(dest, None) is not None
712 
713  if arg_set('debug'):
714  self.Exec.DebugStage=args.debug
715 
716  if arg_set('evtMax'):
717  self.Exec.MaxEvents=args.evtMax
718 
719  if arg_set('interactive'):
720  self.Exec.Interactive=args.interactive
721 
722  if arg_set('skipEvents'):
723  self.Exec.SkipEvents=args.skipEvents
724 
725  if arg_set('filesInput'):
726  self.Input.Files = [] # remove generic
727  for f in args.filesInput.split(","):
728  found = glob.glob(f)
729  # if not found, add string directly
730  self.Input.Files += found if found else [f]
731 
732  if arg_set('loglevel'):
733  from AthenaCommon import Constants
734  self.Exec.OutputLevel = getattr(Constants, args.loglevel)
735 
736  if arg_set('config_only') and args.config_only is not False:
737  from os import environ
738  environ["PICKLECAFILE"] = "" if args.config_only is True else args.config_only
739 
740  if arg_set('threads'):
741  self.Concurrency.NumThreads = args.threads
742  #Work-around a possible inconsistency of NumThreads and NumConcurrentEvents that may
743  #occur when these values are set by the transforms and overwritten by --athenaopts ..
744  #See also ATEAM-907
745  if args.concurrent_events is None and self.Concurrency.NumConcurrentEvents==0:
746  self.Concurrency.NumConcurrentEvents = args.threads
747 
748  if arg_set('concurrent_events'):
749  self.Concurrency.NumConcurrentEvents = args.concurrent_events
750 
751  if arg_set('nprocs'):
752  self.Concurrency.NumProcs = args.nprocs
753 
754  if arg_set('perfmon'):
755  from PerfMonComps.PerfMonConfigHelpers import setPerfmonFlagsFromRunArgs
756  setPerfmonFlagsFromRunArgs(self, args)
757 
758  if arg_set('mtes'):
759  self.Exec.MTEventService = args.mtes
760 
761  if arg_set('mtes_channel'):
762  self.Exec.MTEventServiceChannel = args.mtes_channel
763 
764  if arg_set('profile_python'):
765  from AthenaCommon.Debugging import dumpPythonProfile
766  import atexit, cProfile, functools
767  cProfile._athena_python_profiler = cProfile.Profile()
768  cProfile._athena_python_profiler.enable()
769 
770  # Save stats to file at exit
771  atexit.register(functools.partial(dumpPythonProfile, args.profile_python))
772 
773 
774  # All remaining arguments are assumed to be key=value pairs to set arbitrary flags:
775  for arg in leftover:
776  if arg=='--':
777  argList += ["---"]
778  continue # allows for multi-value arguments to be terminated by a " -- "
779  if do_help and '=' not in arg:
780  argList += arg.split(".") # put arg back back for help (but split by sub-categories)
781  continue
782 
783  self.fillFromString(arg)
784 
785  if do_help:
786  if parser.epilog is None: parser.epilog=""
787  parser.epilog += " Note: Specify additional flags in form <flagName>=<value>."
788  subparsers = {"":[parser,parser.add_subparsers(help=argparse.SUPPRESS)]} # first is category's parser, second is subparsers (effectively the category's subcategories)
789  # silence logging while evaluating flags
790  logging.root.setLevel(logging.ERROR)
791  def getParser(category): # get parser for a given category
792  if category not in subparsers.keys():
793  cat1,cat2 = category.rsplit(".",1) if "." in category else ("",category)
794  p,subp = getParser(cat1)
795  if subp.help==argparse.SUPPRESS:
796  subp.help = "Flag subcategories:"
797  newp = subp.add_parser(cat2,help="{} flags".format(category),
798  formatter_class = argparse.ArgumentDefaultsHelpFormatter,usage=argparse.SUPPRESS)
799  newp._positionals.title = "flags"
800  subparsers[category] = [newp,newp.add_subparsers(help=argparse.SUPPRESS)]
801  return subparsers[category]
802  self.loadAllDynamicFlags()
803  for name in sorted(self._flagdict):
804  category,flagName = name.rsplit(".",1) if "." in name else ("",name)
805  flag = self._flagdict[name]
806  try:
807  val = repr(flag.get(self))
808  except Exception:
809  val = None
810  if flag._help != argparse.SUPPRESS:
811  helptext = ""
812  if flag._help is not None:
813  helptext = f": {flag._help}"
814  if flag._type is not None:
815  helptext += f' [type: {flag._type.__name__}]'
816  if val is not None and helptext == "":
817  helptext = ": " # ensures default values are displayed even if there's no help text
818  getParser(category)[0].add_argument(name, nargs='?', default=val, help=helptext)
819 
820  parser._positionals.title = 'flags and positional arguments'
821  parser.parse_known_args(argList + ["--help"])
822 
823  self._args = args
824 
825  return args
826 
827 
828 
python.Decorators.deprecate
Callable[[TFunc], TFunc] deprecate(str reason, *bool warn_once_per_call=True, bool print_context=False)
Definition: Decorators.py:79
python.AthConfigFlags.AthConfigFlags.join
def join(self, other, prefix='')
Definition: AthConfigFlags.py:546
python.AthConfigFlags.AthConfigFlags.locked
def locked(self)
Definition: AthConfigFlags.py:476
python.AthConfigFlags.FlagAddress.__delattr__
def __delattr__(self, name)
Definition: AthConfigFlags.py:149
python.AthConfigFlags.AthConfigFlags._flagdict
_flagdict
Definition: AthConfigFlags.py:222
python.AthConfigFlags.AthConfigFlags.hasFlag
def hasFlag(self, name)
Definition: AthConfigFlags.py:445
python.AthConfigFlags.AthConfigFlags._locked
_locked
Definition: AthConfigFlags.py:223
python.AthConfigFlags.AthConfigFlags.initAll
def initAll(self)
Definition: AthConfigFlags.py:605
python.AthConfigFlags.AthConfigFlags._loadDynaFlags
def _loadDynaFlags(self, name)
Definition: AthConfigFlags.py:389
python.AthConfigFlags.FlagAddress._subflag_itr
def _subflag_itr(self)
Definition: AthConfigFlags.py:188
vtune_athena.format
format
Definition: vtune_athena.py:14
python.AthConfigFlags.CfgFlag.set
def set(self, value)
Definition: AthConfigFlags.py:44
python.AthConfigFlags.AthConfigFlags.addFlagsCategory
def addFlagsCategory(self, path, generator, prefix=False)
Definition: AthConfigFlags.py:370
python.AthConfigFlags.AthConfigFlags._loaded
_loaded
Definition: AthConfigFlags.py:225
python.AthConfigFlags.FlagAddress._flags
_flags
Definition: AthConfigFlags.py:124
python.AthConfigFlags.CfgFlag._compatibleTypes
dictionary _compatibleTypes
Definition: AthConfigFlags.py:28
python.AthConfigFlags.FlagAddress._name
_name
Definition: AthConfigFlags.py:126
python.AthConfigFlags.FlagAddress.asdict
def asdict(self)
Definition: AthConfigFlags.py:202
python.AthConfigFlags.AthConfigFlags.cloneAndReplace
def cloneAndReplace(self, subsetToReplace, replacementSubset, keepOriginal=False)
Definition: AthConfigFlags.py:495
python.AthConfigFlags.AthConfigFlags.parser
def parser(self)
Definition: AthConfigFlags.py:625
python.AthConfigFlags.AthConfigFlags.lock
def lock(self)
Definition: AthConfigFlags.py:469
python.AthConfigFlags.AthConfigFlags.loadAllDynamicFlags
def loadAllDynamicFlags(self)
Definition: AthConfigFlags.py:415
python.AthConfigFlags.AthConfigFlags._set
def _set(self, name, value)
Definition: AthConfigFlags.py:448
python.AthConfigFlags.AthConfigFlags.getArgumentParser
def getArgumentParser(self, **kwargs)
Definition: AthConfigFlags.py:614
python.AthConfigFlags.CfgFlag
Definition: AthConfigFlags.py:19
python.AthConfigFlags.CfgFlag.__init__
def __init__(self, default, type=None, help=None)
Definition: AthConfigFlags.py:32
python.AthConfigFlags.AthConfigFlags.__setattr__
def __setattr__(self, name, value)
Definition: AthConfigFlags.py:269
python.AthConfigFlags.AthConfigFlags.args
def args(self)
Definition: AthConfigFlags.py:629
XMLtoHeader.count
count
Definition: XMLtoHeader.py:85
python.Bindings.values
values
Definition: Control/AthenaPython/python/Bindings.py:797
python.AthConfigFlags.FlagAddress.__setattr__
def __setattr__(self, name, value)
Definition: AthConfigFlags.py:137
python.AthConfigFlags.AthConfigFlags._get
def _get(self, name)
Definition: AthConfigFlags.py:457
python.AthConfigFlags.AthConfigFlags._dynaflags
_dynaflags
Definition: AthConfigFlags.py:224
python.AthConfigFlags.AthConfigFlags._renames
_renames
Definition: AthConfigFlags.py:230
python.AthConfigFlags.AthConfigFlags._categoryCache
_categoryCache
Definition: AthConfigFlags.py:226
python.AthConfigFlags.FlagAddress.__iter__
def __iter__(self)
Definition: AthConfigFlags.py:174
python.AthConfigFlags.AthConfigFlags
Definition: AthConfigFlags.py:219
python.AthConfigFlags.CfgFlag._setDef
_setDef
Definition: AthConfigFlags.py:51
python.AthConfigFlags.CfgFlag._validateType
def _validateType(self, value)
Definition: AthConfigFlags.py:95
python.AthConfigFlags._asdict
def _asdict(iterator)
Definition: AthConfigFlags.py:105
python.AthConfigFlags.AthConfigFlags.addFlag
def addFlag(self, name, setDef, type=None, help=None)
Definition: AthConfigFlags.py:363
LArG4FSStartPointFilter.exec
exec
Definition: LArG4FSStartPointFilter.py:103
python.PerfMonConfigHelpers.setPerfmonFlagsFromRunArgs
def setPerfmonFlagsFromRunArgs(flags, runArgs)
Definition: PerfMonConfigHelpers.py:3
python.AthConfigFlags.AthConfigFlags.fillFromString
def fillFromString(self, flag_string)
Definition: AthConfigFlags.py:633
python.AthConfigFlags.FlagAddress
Definition: AthConfigFlags.py:121
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
PyAthena::repr
std::string repr(PyObject *o)
returns the string representation of a python object equivalent of calling repr(o) in python
Definition: PyAthenaUtils.cxx:106
add
bool add(const std::string &hname, TKey *tobj)
Definition: fastadd.cxx:55
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.AthConfigFlags.isGaudiEnv
def isGaudiEnv()
Definition: AthConfigFlags.py:14
python.AthConfigFlags.AthConfigFlags._renamed_map
def _renamed_map(self)
Definition: AthConfigFlags.py:315
python.AthConfigFlags.AthConfigFlags._parser
_parser
Definition: AthConfigFlags.py:228
python.AthConfigFlags.AthConfigFlags.hasCategory
def hasCategory(self, name)
Definition: AthConfigFlags.py:422
python.AthConfigFlags.AthConfigFlags.__setitem__
def __setitem__(self, name, value)
Definition: AthConfigFlags.py:283
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.
CxxUtils::set
constexpr std::enable_if_t< is_bitmask_v< E >, E & > set(E &lhs, E rhs)
Convenience function to set bits in a class enum bitmask.
Definition: bitmask.h:224
python.AthConfigFlags.AthConfigFlags.__iter__
def __iter__(self)
Definition: AthConfigFlags.py:294
python.AthConfigFlags.AthConfigFlags.__hash__
def __hash__(self)
Definition: AthConfigFlags.py:239
id
SG::auxid_t id
Definition: Control/AthContainers/Root/debug.cxx:194
python.AthConfigFlags.FlagAddress.__getattr__
def __getattr__(self, name)
Definition: AthConfigFlags.py:134
python.AthConfigFlags.AthConfigFlags.dump
def dump(self, pattern=".*", evaluate=False, formatStr="{:40} : {}", maxLength=None)
Definition: AthConfigFlags.py:567
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:79
python.AthConfigFlags.AthConfigFlags.athHash
def athHash(self)
Definition: AthConfigFlags.py:232
python.AthConfigFlags.AthConfigFlags.clone
def clone(self)
Definition: AthConfigFlags.py:486
python.AthConfigFlags.AthConfigFlags.__getitem__
def __getitem__(self, name)
Definition: AthConfigFlags.py:280
python.AthConfigFlags.AthConfigFlags.__delattr__
def __delattr__(self, name)
Definition: AthConfigFlags.py:277
python.AthConfigFlags.AthConfigFlags._args
_args
Definition: AthConfigFlags.py:229
python.AthConfigFlags.CfgFlag.__repr__
def __repr__(self)
Definition: AthConfigFlags.py:89
python.AthConfigFlags.AthConfigFlags._hash
_hash
Definition: AthConfigFlags.py:227
python.AthConfigFlags.AthConfigFlags._calculateHash
def _calculateHash(self)
Definition: AthConfigFlags.py:242
mc.generator
generator
Configure Herwig7 These are the commands corresponding to what would go into the regular Herwig infil...
Definition: mc.MGH7_FxFx_H71-DEFAULT_test.py:18
python.AthConfigFlags.AthConfigFlags.needFlagsCategory
def needFlagsCategory(self, name)
Definition: AthConfigFlags.py:385
VKalVrtAthena::varHolder_detail::clear
void clear(T &var)
Definition: NtupleVars.h:48
CaloCondBlobAlgs_fillNoiseFromASCII.hash
dictionary hash
Definition: CaloCondBlobAlgs_fillNoiseFromASCII.py:109
python.AthConfigFlags.FlagAddress.__setitem__
def __setitem__(self, name, value)
Definition: AthConfigFlags.py:167
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
python.AthConfigFlags.FlagAddress.__bool__
def __bool__(self)
Definition: AthConfigFlags.py:161
Muon::print
std::string print(const MuPatSegment &)
Definition: MuonTrackSteering.cxx:28
get
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition: hcg.cxx:127
python.AthConfigFlags.AthConfigFlags.asdict
def asdict(self)
Definition: AthConfigFlags.py:305
python.AthConfigFlags.AthConfigFlags._tryModify
def _tryModify(self)
Definition: AthConfigFlags.py:479
python.AthConfigFlags.CfgFlag._type
_type
Definition: AthConfigFlags.py:39
python.AthConfigFlags.AthConfigFlags.__call__
def __call__(self, name)
Definition: AthConfigFlags.py:466
pickleTool.object
object
Definition: pickleTool.py:30
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:790
calibdata.copy
bool copy
Definition: calibdata.py:27
python.AthConfigFlags.AthConfigFlags._subflag_itr
def _subflag_itr(self)
Definition: AthConfigFlags.py:342
python.AthConfigFlags.FlagAddress.__cmp__
def __cmp__(self, other)
Definition: AthConfigFlags.py:152
python.AthConfigFlags.FlagAddress.__init__
def __init__(self, f, name)
Definition: AthConfigFlags.py:122
python.AthConfigFlags.AthConfigFlags.__getattr__
def __getattr__(self, name)
Definition: AthConfigFlags.py:245
python.AthConfigFlags.AthConfigFlags.__init__
def __init__(self)
Definition: AthConfigFlags.py:221
python.AthConfigFlags.FlagAddress.__getitem__
def __getitem__(self, name)
Definition: AthConfigFlags.py:164
python.AthConfigFlags.AthConfigFlags.__delitem__
def __delitem__(self, name)
Definition: AthConfigFlags.py:286
python.AthConfigFlags.FlagAddress.__delitem__
def __delitem__(self, name)
Definition: AthConfigFlags.py:170
Trk::split
@ split
Definition: LayerMaterialProperties.h:38
python.AthConfigFlags.CfgFlag._value
_value
Definition: AthConfigFlags.py:50
python.AthConfigFlags.AthConfigFlags.fillFromArgs
def fillFromArgs(self, listOfArgs=None, parser=None)
Definition: AthConfigFlags.py:674
python.AthConfigFlags.CfgFlag._help
_help
Definition: AthConfigFlags.py:40
python.moduleExists.moduleExists
def moduleExists(modName)
Definition: moduleExists.py:13
python.AthConfigFlags.CfgFlag.get
def get(self, flagdict=None)
Definition: AthConfigFlags.py:58
LArG4ShowerLibProcessing.truncate
truncate
Definition: LArG4ShowerLibProcessing.py:39