Loading [MathJax]/extensions/tex2jax.js
ATLAS Offline Software
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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  # A list of all flags instances for which we've returned a hash.
222  # We can't allow them to be deleted; otherwise, we might get new
223  # flags object with the same hash.
224  _hashedFlags = []
225 
226  def __init__(self):
227  self._flagdict=dict()
228  self._locked=False
229  self._dynaflags = dict()
230  self._loaded = set() # dynamic dlags that were loaded
231  self._categoryCache = set() # cache for already found categories
232  self._hash = None
233  self._parser = None
234  self._args = None # user args from parser
235  self._renames = {}
236 
237  def athHash(self):
238  if self._locked is False:
239  raise RuntimeError("Cannot calculate hash of unlocked flag container")
240  elif self._hash is None:
241  self._hash = self._calculateHash()
242  return self._hash
243 
244  def __hash__(self):
245  raise DeprecationWarning("__hash__ method in AthConfigFlags is deprecated. Probably called from function decorator, use AccumulatorCache decorator instead.")
246 
247  def _calculateHash(self):
248  # Once we've hashed a flags instance, we need to be sure that
249  # it never goes away. Otherwise, since we base the hash
250  # on just the id of the dictionary, if a flags object is deleted
251  # and a new one created, the hash of the new one could match the
252  # hash of the old, even if contents are different.
253  # See ATLASRECTS-8070.
254  AthConfigFlags._hashedFlags.append (self)
255  return hash( (frozenset({k: v for k, v in self._renames.items() if k != v}), id(self._flagdict)) )
256 
257  def __getattr__(self, name):
258  # Avoid infinite recursion looking up our own attributes
259  _flagdict = object.__getattribute__(self, "_flagdict")
260 
261  # First try to get an already loaded flag or category
262  if name in _flagdict:
263  return self._get(name)
264 
265  if self.hasCategory(name):
266  return FlagAddress(self, name)
267 
268  # Reaching here means that we may need to load a dynamic flag
269  self._loadDynaFlags(name)
270 
271  # Try again
272  if name in _flagdict:
273  return self._get(name)
274 
275  if self.hasCategory(name):
276  return FlagAddress(self, name)
277 
278  # Reaching here means that it truly isn't something we know about
279  raise AttributeError(f"No such flag: {name}")
280 
281  def __setattr__(self, name, value):
282  if name.startswith("_"):
283  return object.__setattr__(self, name, value)
284 
285  if name in self._flagdict:
286  return self._set(name, value)
287  raise RuntimeError( "No such flag: "+ name+". The name is likely incomplete." )
288 
289  def __delattr__(self, name):
290  del self[name]
291 
292  def __getitem__(self, name):
293  return getattr(self, name)
294 
295  def __setitem__(self, name, value):
296  setattr(self, name, value)
297 
298  def __delitem__(self, name):
299  self._tryModify()
300  self.loadAllDynamicFlags()
301  for key in list(self._flagdict):
302  if key.startswith(name):
303  del self._flagdict[key]
304  self._categoryCache.clear()
305 
306  def __iter__(self):
307  self.loadAllDynamicFlags()
308  rmap = self._renamed_map()
309  used = set()
310  for flag in self._flagdict:
311  for r in rmap[flag]:
312  first = r.split('.',1)[0]
313  if first not in used:
314  yield first
315  used.add(first)
316 
317  def asdict(self):
318  """Convert to a python dictionary
319 
320  This is identical to the `asdict` in FlagAddress, but for all
321  the flags.
322 
323  """
324  return _asdict(self._subflag_itr())
325 
326 
327  def _renamed_map(self):
328  """mapping from the old names to the new names
329 
330  This is the inverse of _renamed, which maps new names to old
331  names
332 
333  Returns a list of the new names corresponding to the old names
334  (since cloneAndReplace may or may not disable access to the old name,
335  it is possible that an old name renames to multiple new names)
336  """
337  revmap = {}
338 
339  for new, old in self._renames.items():
340  if old not in revmap:
341  revmap[old] = [ new ]
342  else:
343  revmap[old] += [ new ]
344 
345  def rename(key):
346  for old, newlist in revmap.items():
347  if key.startswith(old + '.'):
348  stem = key.removeprefix(old)
349  return [ f'{new}{stem}' if new else '' for new in newlist ]
350  return [ key ]
351 
352  return {x:rename(x) for x in self._flagdict.keys()}
353 
354  def _subflag_itr(self):
355  """Subflag iterator for all flags
356 
357  This is used by the asdict() function.
358  """
359  self.loadAllDynamicFlags()
360 
361  for old, newlist in self._renamed_map().items():
362  for new in newlist:
363  # Lots of modules are missing in analysis releases. I
364  # tried to prevent imports using the _addFlagsCategory
365  # function which checks if some module exists, but this
366  # turned in to quite a rabbit hole. Catching and ignoring
367  # the missing module exception seems to work, even if it's
368  # not pretty.
369  try:
370  yield new, getattr(self, old)
371  except ModuleNotFoundError as err:
372  _msg.debug(f'missing module: {err}')
373  pass
374 
375  def addFlag(self, name, setDef, type=None, help=None):
376  self._tryModify()
377  if name in self._flagdict:
378  raise KeyError("Duplicated flag name: {}".format( name ))
379  self._flagdict[name]=CfgFlag(setDef, type, help)
380  return
381 
382  def addFlagsCategory(self, path, generator, prefix=False):
383  """
384  The path is the beginning of the flag name (e.g. "X" for flags generated with name "X.*").
385  The generator is a function that returns a flags container, the flags have to start with the same path.
386  When the prefix is True the flags created by the generator are prefixed by "path".
387 
388  Supported calls are then:
389  addFlagsCategory("A", g) - where g is function creating flags is f.addFlag("A.x", someValue)
390  addFlagsCategory("A", g, True) - when flags are defined in g like this: f.addFalg("x", somevalue),
391  The latter option allows to share one generator among flags that are later loaded in different paths.
392  """
393  self._tryModify()
394  _msg.debug("Adding flag category %s", path)
395  self._dynaflags[path] = (generator, prefix)
396 
397  def needFlagsCategory(self, name):
398  """ public interface for _loadDynaFlags """
399  self._loadDynaFlags( name )
400 
401  def _loadDynaFlags(self, name):
402  """
403  loads the flags of the form "A.B.C" first attempting the path "A" then "A.B" and then "A.B.C"
404  """
405 
406  def __load_impl( flagBaseName ):
407  if flagBaseName in self._loaded:
408  _msg.debug("Flags %s already loaded",flagBaseName )
409  return
410  if flagBaseName in self._dynaflags:
411  _msg.debug("Dynamically loading the flags under %s", flagBaseName )
412  # Retain locked status and hash
413  isLocked = self._locked
414  myHash = self._hash
415  self._locked = False
416  generator, prefix = self._dynaflags[flagBaseName]
417  self.join( generator(), flagBaseName if prefix else "" )
418  self._locked = isLocked
419  self._hash = myHash
420  del self._dynaflags[flagBaseName]
421  self._loaded.add(flagBaseName)
422 
423  pathfrags = name.split('.')
424  for maxf in range(1, len(pathfrags)+1):
425  __load_impl( '.'.join(pathfrags[:maxf]) )
426 
428  """Force load all the dynamic flags """
429  while len(self._dynaflags) != 0:
430  # Need to convert to a list since _loadDynaFlags may change the dict.
431  for prefix in list(self._dynaflags.keys()):
432  self._loadDynaFlags( prefix )
433 
434  def hasCategory(self, name):
435  # We cache successfully found categories
436  if name in self._categoryCache:
437  return True
438 
439  if name in self._renames:
440  re_name = self._renames[name]
441  if re_name != name:
442  return self.hasCategory(re_name)
443 
444  # If not found do search through all keys.
445  # TODO: could be improved by using a trie for _flagdict
446  for f in self._flagdict.keys():
447  if f.startswith(name+'.'):
448  self._categoryCache.add(name)
449  return True
450  for c in self._dynaflags.keys():
451  if c.startswith(name):
452  self._categoryCache.add(name)
453  return True
454 
455  return False
456 
457  def hasFlag(self, name):
458  return name in [y for x in self._renamed_map().values() for y in x]
459 
460  def _set(self,name,value):
461  self._tryModify()
462  try:
463  self._flagdict[name].set(value)
464  except KeyError:
465  closestMatch = get_close_matches(name,self._flagdict.keys(),1)
466  raise KeyError(f"No flag with name '{name}' found" +
467  (f". Did you mean '{closestMatch[0]}'?" if closestMatch else ""))
468 
469  def _get(self,name):
470  try:
471  return self._flagdict[name].get(self)
472  except KeyError:
473  closestMatch = get_close_matches(name,self._flagdict.keys(),1)
474  raise KeyError(f"No flag with name '{name}' found" +
475  (f". Did you mean '{closestMatch[0]}'?" if closestMatch else ""))
476 
477  @deprecate("Use '[...]' rather than '(...)' to access flags", print_context=True)
478  def __call__(self,name):
479  return self._get(name)
480 
481  def lock(self):
482  if not self._locked:
483  # before locking, parse args if a parser was defined
484  if self._args is None and self._parser is not None: self.fillFromArgs()
485  self._locked = True
486  return
487 
488  def locked(self):
489  return self._locked
490 
491  def _tryModify(self):
492  if self._locked:
493  raise RuntimeError("Attempt to modify locked flag container")
494  else:
495  # if unlocked then invalidate hash
496  self._hash = None
497 
498  def clone(self):
499  """Return an unlocked copy of self (dynamic flags are not loaded)"""
500  cln = AthConfigFlags()
501  cln._flagdict = deepcopy(self._flagdict)
502  cln._dynaflags = copy(self._dynaflags)
503  cln._renames = deepcopy(self._renames)
504  return cln
505 
506 
507  def cloneAndReplace(self,subsetToReplace,replacementSubset, keepOriginal=False):
508  """
509  This is to replace subsets of configuration flags like
510 
511  Example:
512  newflags = flags.cloneAndReplace('Muon', 'Trigger.Offline.Muon')
513  """
514 
515  _msg.debug("cloning flags and replacing %s by %s", subsetToReplace, replacementSubset)
516 
517  self._loadDynaFlags( subsetToReplace )
518  self._loadDynaFlags( replacementSubset )
519 
520  subsetToReplace = subsetToReplace.strip(".")
521  replacementSubset = replacementSubset.strip(".")
522 
523  #Sanity check: Don't replace a by a
524  if (subsetToReplace == replacementSubset):
525  raise RuntimeError(f'Can not replace flags {subsetToReplace} with themselves')
526 
527  # protect against subsequent remaps within remaps: clone = flags.cloneAndReplace('Y', 'X').cloneAndReplace('X.b', 'X.a')
528  for alias,src in self._renames.items():
529  if src == "": continue
530  if src+"." in subsetToReplace:
531  raise RuntimeError(f'Can not replace flags {subsetToReplace} by {replacementSubset} because of already present replacement of {alias} by {src}')
532 
533 
534  newFlags = copy(self) # shallow copy
535  newFlags._renames = deepcopy(self._renames) #maintains renames
536 
537  if replacementSubset in newFlags._renames: #and newFlags._renames[replacementSubset]:
538  newFlags._renames[subsetToReplace] = newFlags._renames[replacementSubset]
539  else:
540  newFlags._renames[subsetToReplace] = replacementSubset
541 
542  if not keepOriginal:
543  if replacementSubset not in newFlags._renames or newFlags._renames[replacementSubset] == replacementSubset:
544  newFlags._renames[replacementSubset] = "" # block access to original flags
545  else:
546  del newFlags._renames[replacementSubset]
547  #If replacementSubset was a "pure renaming" of another set of flags,
548  #the original set of flags gets propagated down to its potential further renamings:
549  #no need to worry about maintaining the intermediate steps in the renaming.
550  else:
551  if replacementSubset not in newFlags._renames:
552  newFlags._renames[replacementSubset] = replacementSubset
553  #For _renamed_map to know that these flags still work.
554  newFlags._hash = None
555  return newFlags
556 
557 
558  def join(self, other, prefix=''):
559  """
560  Merges two flag containers
561  When the prefix is passed each flag from the "other" is prefixed by "prefix."
562  """
563  self._tryModify()
564 
565  for (name,flag) in other._flagdict.items():
566  fullName = prefix+"."+name if prefix != "" else name
567  if fullName in self._flagdict:
568  raise KeyError("Duplicated flag name: {}".format( fullName ) )
569  self._flagdict[fullName]=flag
570 
571  for (name,loader) in other._dynaflags.items():
572  fullName = prefix+"."+name if prefix != "" else name
573  if fullName in self._dynaflags:
574  raise KeyError("Duplicated dynamic flags name: {}".format( fullName ) )
575  _msg.debug("Joining dynamic flags with %s", fullName)
576  self._dynaflags[fullName] = loader
577  return
578 
579  def dump(self, pattern=".*", evaluate=False, formatStr="{:40} : {}", maxLength=None):
580  import re
581  compiled = re.compile(pattern)
582  def truncate(s): return s[:maxLength] + ("..." if maxLength and len(s)>maxLength else "")
583  reverse_renames = {value: key for key, value in self._renames.items() if value != ''} # new name to old
584  for name in sorted(self._flagdict):
585  renamed = name
586  if any([name.startswith(r) for r in reverse_renames.keys()]):
587  for oldprefix, newprefix in reverse_renames.items():
588  if name.startswith(oldprefix):
589  renamed = name.replace(oldprefix, newprefix)
590  break
591  if compiled.match(renamed):
592  if evaluate:
593  try:
594  rep = repr(self._flagdict[name] )
595  val = repr(self._flagdict[name].get(self))
596  if val != rep:
597  print(formatStr.format(renamed,truncate("{} {}".format( val, rep )) ))
598  else:
599  print(formatStr.format(renamed, truncate("{}".format(val)) ) )
600  except Exception as e:
601  print(formatStr.format(renamed, truncate("Exception: {}".format( e )) ))
602  else:
603  print(formatStr.format( renamed, truncate("{}".format(repr(self._flagdict[name] ) )) ))
604 
605  if len(self._dynaflags) != 0 and any([compiled.match(x) for x in self._dynaflags.keys()]):
606  print("Flag categories that can be loaded dynamically")
607  print("{:25} : {:>30} : {}".format( "Category","Generator name", "Defined in" ) )
608  for name,gen_and_prefix in sorted(self._dynaflags.items()):
609  if compiled.match(name):
610  print("{:25} : {:>30} : {}".format( name, gen_and_prefix[0].__name__, '/'.join(gen_and_prefix[0].__code__.co_filename.split('/')[-2:]) ) )
611  if len(self._renames):
612  print("Flag categories that are redirected by the cloneAndReplace")
613  for alias,src in self._renames.items():
614  print("{:30} points to {:>30} ".format( alias, src if src else "nothing") )
615 
616 
617  def initAll(self):
618  """
619  Mostly a self-test method
620  """
621  for n,f in list(self._flagdict.items()):
622  f.get(self)
623  return
624 
625 
626  def getArgumentParser(self, **kwargs):
627  """
628  Scripts calling AthConfigFlags.fillFromArgs can extend this parser, and pass their version to fillFromArgs
629  """
630  import argparse
631  from AthenaCommon.AthOptionsParser import getArgumentParser
632  parser = getArgumentParser(**kwargs)
633  parser.add_argument("---",dest="terminator",action='store_true', help=argparse.SUPPRESS) # special hidden option required to convert option terminator -- for --help calls
634 
635  return parser
636 
637  def parser(self):
638  if self._parser is None: self._parser = self.getArgumentParser()
639  return self._parser
640 
641  def args(self):
642  return self._args
643 
644 
645  def fillFromString(self, flag_string):
646  """Fill the flags from a string of type key=value"""
647  import ast
648 
649  try:
650  key, value = flag_string.split("=")
651  except ValueError:
652  raise ValueError(f"Cannot interpret argument {flag_string}, expected a key=value format")
653 
654  key = key.strip()
655  value = value.strip()
656 
657  # also allow key+=value to append
658  oper = "="
659  if (key[-1]=="+"):
660  oper = "+="
661  key = key[:-1]
662 
663  if not self.hasFlag(key):
664  self._loadDynaFlags( '.'.join(key.split('.')[:-1]) ) # for a flag A.B.C dymanic flags from category A.B
665  if not self.hasFlag(key):
666  raise KeyError(f"{key} is not a known configuration flag")
667 
668  flag_type = self._flagdict[key]._type
669  if flag_type is None:
670  # Regular flag
671  try:
672  ast.literal_eval(value)
673  except Exception: # Can't determine type, assume we got an un-quoted string
674  value=f"\"{value}\""
675 
676  elif isinstance(flag_type, EnumMeta):
677  # Flag is an enum, so we need to import the module containing the enum
678  ENUM = importlib.import_module(flag_type.__module__) # noqa: F841 (used in exec)
679  value=f"ENUM.{value}"
680 
681  # Set the value (this also does the type checking if needed)
682  exec(f"self.{key}{oper}{value}")
683 
684 
685  # parser argument must be an ArgumentParser returned from getArgumentParser()
686  def fillFromArgs(self, listOfArgs=None, parser=None):
687  """
688  Used to set flags from command-line parameters, like flags.fillFromArgs(sys.argv[1:])
689  """
690  import sys
691 
692  self._tryModify()
693 
694  if parser is None:
695  parser = self.parser()
696  self._parser = parser # set our parser to given one
697  argList = listOfArgs or sys.argv[1:]
698  do_help = False
699  # We will now do a pre-parse of the command line arguments to propagate these to the flags
700  # the reason for this is so that we can use the help messaging to display the values of all
701  # flags as they would be *after* any parsing takes place. This is nice to see e.g. the value
702  # that any derived flag (functional flag) will take after, say, the filesInput are set
703  import argparse
704  unrequiredActions = []
705  if "-h" in argList or "--help" in argList:
706  do_help = True
707  if "-h" in argList: argList.remove("-h")
708  if "--help" in argList: argList.remove("--help")
709  # need to unrequire any required arguments in order to do a "pre parse"
710  for a in parser._actions:
711  if a.required:
712  unrequiredActions.append(a)
713  a.required = False
714  (args,leftover)=parser.parse_known_args(argList)
715  for a in unrequiredActions: a.required=True
716 
717  # remove the leftovers from the argList ... for later use in the do_help
718  argList = [a for a in argList if a not in leftover]
719 
720  # First, handle athena.py-like arguments (if available in parser):
721  def arg_set(dest):
722  """Check if dest is available in parser and has been set"""
723  return vars(args).get(dest, None) is not None
724 
725  if arg_set('debug'):
726  self.Exec.DebugStage=args.debug
727 
728  if arg_set('evtMax'):
729  self.Exec.MaxEvents=args.evtMax
730 
731  if arg_set('interactive'):
732  self.Exec.Interactive=args.interactive
733 
734  if arg_set('skipEvents'):
735  self.Exec.SkipEvents=args.skipEvents
736 
737  if arg_set('filesInput'):
738  self.Input.Files = [] # remove generic
739  for f in args.filesInput.split(","):
740  found = glob.glob(f)
741  # if not found, add string directly
742  self.Input.Files += found if found else [f]
743 
744  if "-l" in argList or "--loglevel" in argList: # different check b.c. has a default value so will always be in args
745  from AthenaCommon import Constants
746  self.Exec.OutputLevel = getattr(Constants, args.loglevel)
747 
748  if arg_set('config_only') and args.config_only is not False:
749  from os import environ
750  environ["PICKLECAFILE"] = "" if args.config_only is True else args.config_only
751 
752  if arg_set('threads'):
753  self.Concurrency.NumThreads = args.threads
754  #Work-around a possible inconsistency of NumThreads and NumConcurrentEvents that may
755  #occur when these values are set by the transforms and overwritten by --athenaopts ..
756  #See also ATEAM-907
757  if args.concurrent_events is None and self.Concurrency.NumConcurrentEvents==0:
758  self.Concurrency.NumConcurrentEvents = args.threads
759 
760  if arg_set('concurrent_events'):
761  self.Concurrency.NumConcurrentEvents = args.concurrent_events
762 
763  if arg_set('nprocs'):
764  self.Concurrency.NumProcs = args.nprocs
765 
766  if arg_set('perfmon'):
767  from PerfMonComps.PerfMonConfigHelpers import setPerfmonFlagsFromRunArgs
768  setPerfmonFlagsFromRunArgs(self, args)
769 
770  if arg_set('mtes'):
771  self.Exec.MTEventService = args.mtes
772 
773  if arg_set('mtes_channel'):
774  self.Exec.MTEventServiceChannel = args.mtes_channel
775 
776  if arg_set('profile_python'):
777  from AthenaCommon.Debugging import dumpPythonProfile
778  import atexit, cProfile, functools
779  cProfile._athena_python_profiler = cProfile.Profile()
780  cProfile._athena_python_profiler.enable()
781 
782  # Save stats to file at exit
783  atexit.register(functools.partial(dumpPythonProfile, args.profile_python))
784 
785 
786  # All remaining arguments are assumed to be key=value pairs to set arbitrary flags:
787  for arg in leftover:
788  if arg=='--':
789  argList += ["---"]
790  continue # allows for multi-value arguments to be terminated by a " -- "
791  if do_help and '=' not in arg:
792  argList += arg.split(".") # put arg back back for help (but split by sub-categories)
793  continue
794 
795  self.fillFromString(arg)
796 
797  if do_help:
798  if parser.epilog is None: parser.epilog=""
799  parser.epilog += " Note: Specify additional flags in form <flagName>=<value>."
800  subparsers = {"":[parser,parser.add_subparsers(help=argparse.SUPPRESS)]} # first is category's parser, second is subparsers (effectively the category's subcategories)
801  # silence logging while evaluating flags
802  logging.root.setLevel(logging.ERROR)
803  def getParser(category): # get parser for a given category
804  if category not in subparsers.keys():
805  cat1,cat2 = category.rsplit(".",1) if "." in category else ("",category)
806  p,subp = getParser(cat1)
807  if subp.help==argparse.SUPPRESS:
808  subp.help = "Flag subcategories:"
809  newp = subp.add_parser(cat2,help="{} flags".format(category),
810  formatter_class = argparse.ArgumentDefaultsHelpFormatter,usage=argparse.SUPPRESS)
811  newp._positionals.title = "flags"
812  subparsers[category] = [newp,newp.add_subparsers(help=argparse.SUPPRESS)]
813  return subparsers[category]
814  self.loadAllDynamicFlags()
815  for name in sorted(self._flagdict):
816  category,flagName = name.rsplit(".",1) if "." in name else ("",name)
817  flag = self._flagdict[name]
818  try:
819  val = repr(flag.get(self))
820  except Exception:
821  val = None
822  if flag._help != argparse.SUPPRESS:
823  helptext = ""
824  if flag._help is not None:
825  helptext = f": {flag._help}"
826  if flag._type is not None:
827  helptext += f' [type: {flag._type.__name__}]'
828  if val is not None and helptext == "":
829  helptext = ": " # ensures default values are displayed even if there's no help text
830  getParser(category)[0].add_argument(name, nargs='?', default=val, help=helptext)
831 
832  parser._positionals.title = 'flags and positional arguments'
833  parser.parse_known_args(argList + ["--help"])
834 
835  self._args = args
836 
837  return args
838 
839 
840 
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:558
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:488
python.AthConfigFlags.FlagAddress.__delattr__
def __delattr__(self, name)
Definition: AthConfigFlags.py:149
python.AthConfigFlags.AthConfigFlags._flagdict
_flagdict
Definition: AthConfigFlags.py:227
python.AthConfigFlags.AthConfigFlags.hasFlag
def hasFlag(self, name)
Definition: AthConfigFlags.py:457
python.AthConfigFlags.AthConfigFlags._locked
_locked
Definition: AthConfigFlags.py:228
python.AthConfigFlags.AthConfigFlags.initAll
def initAll(self)
Definition: AthConfigFlags.py:617
python.AthConfigFlags.AthConfigFlags._loadDynaFlags
def _loadDynaFlags(self, name)
Definition: AthConfigFlags.py:401
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:382
python.AthConfigFlags.AthConfigFlags._loaded
_loaded
Definition: AthConfigFlags.py:230
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:507
python.AthConfigFlags.AthConfigFlags.parser
def parser(self)
Definition: AthConfigFlags.py:637
python.AthConfigFlags.AthConfigFlags.lock
def lock(self)
Definition: AthConfigFlags.py:481
python.AthConfigFlags.AthConfigFlags.loadAllDynamicFlags
def loadAllDynamicFlags(self)
Definition: AthConfigFlags.py:427
python.AthConfigFlags.AthConfigFlags._set
def _set(self, name, value)
Definition: AthConfigFlags.py:460
python.AthConfigFlags.AthConfigFlags.getArgumentParser
def getArgumentParser(self, **kwargs)
Definition: AthConfigFlags.py:626
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:281
python.AthConfigFlags.AthConfigFlags.args
def args(self)
Definition: AthConfigFlags.py:641
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
XMLtoHeader.count
count
Definition: XMLtoHeader.py:85
python.Bindings.values
values
Definition: Control/AthenaPython/python/Bindings.py:805
python.AthConfigFlags.FlagAddress.__setattr__
def __setattr__(self, name, value)
Definition: AthConfigFlags.py:137
python.AthConfigFlags.AthConfigFlags._get
def _get(self, name)
Definition: AthConfigFlags.py:469
python.AthConfigFlags.AthConfigFlags._dynaflags
_dynaflags
Definition: AthConfigFlags.py:229
python.AthConfigFlags.AthConfigFlags._renames
_renames
Definition: AthConfigFlags.py:235
python.AthConfigFlags.AthConfigFlags._categoryCache
_categoryCache
Definition: AthConfigFlags.py:231
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:375
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:645
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:327
python.AthConfigFlags.AthConfigFlags._parser
_parser
Definition: AthConfigFlags.py:233
python.AthConfigFlags.AthConfigFlags.hasCategory
def hasCategory(self, name)
Definition: AthConfigFlags.py:434
python.AthConfigFlags.AthConfigFlags.__setitem__
def __setitem__(self, name, value)
Definition: AthConfigFlags.py:295
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:25
python.AthConfigFlags.AthConfigFlags.__iter__
def __iter__(self)
Definition: AthConfigFlags.py:306
python.AthConfigFlags.AthConfigFlags.__hash__
def __hash__(self)
Definition: AthConfigFlags.py:244
id
SG::auxid_t id
Definition: Control/AthContainers/Root/debug.cxx:239
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:579
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:71
python.AthConfigFlags.AthConfigFlags.athHash
def athHash(self)
Definition: AthConfigFlags.py:237
python.AthConfigFlags.AthConfigFlags.clone
def clone(self)
Definition: AthConfigFlags.py:498
python.AthConfigFlags.AthConfigFlags.__getitem__
def __getitem__(self, name)
Definition: AthConfigFlags.py:292
python.AthConfigFlags.AthConfigFlags.__delattr__
def __delattr__(self, name)
Definition: AthConfigFlags.py:289
python.AthConfigFlags.AthConfigFlags._args
_args
Definition: AthConfigFlags.py:234
python.AthConfigFlags.CfgFlag.__repr__
def __repr__(self)
Definition: AthConfigFlags.py:89
python.AthConfigFlags.AthConfigFlags._hash
_hash
Definition: AthConfigFlags.py:232
python.AthConfigFlags.AthConfigFlags._calculateHash
def _calculateHash(self)
Definition: AthConfigFlags.py:247
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:397
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.AthConfigFlags.FlagAddress.__bool__
def __bool__(self)
Definition: AthConfigFlags.py:161
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:317
python.AthConfigFlags.AthConfigFlags._tryModify
def _tryModify(self)
Definition: AthConfigFlags.py:491
python.AthConfigFlags.CfgFlag._type
_type
Definition: AthConfigFlags.py:39
python.AthConfigFlags.AthConfigFlags.__call__
def __call__(self, name)
Definition: AthConfigFlags.py:478
pickleTool.object
object
Definition: pickleTool.py:30
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:798
calibdata.copy
bool copy
Definition: calibdata.py:27
python.AthConfigFlags.AthConfigFlags._subflag_itr
def _subflag_itr(self)
Definition: AthConfigFlags.py:354
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:257
python.AthConfigFlags.AthConfigFlags.__init__
def __init__(self)
Definition: AthConfigFlags.py:226
python.AthConfigFlags.FlagAddress.__getitem__
def __getitem__(self, name)
Definition: AthConfigFlags.py:164
python.AthConfigFlags.AthConfigFlags.__delitem__
def __delitem__(self, name)
Definition: AthConfigFlags.py:298
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:686
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