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