ATLAS Offline Software
loaders.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 import pickle
4 import ast
5 import logging
6 from typing import Dict, List, Set, Tuple, cast
7 import collections
8 import json
9 import sys
10 import re
11 import argparse
12 
13 from AthenaConfiguration.iconfTool.models.element import (
14  Element,
15  GroupingElement,
16 )
17 from AthenaConfiguration.iconfTool.models.structure import ComponentsStructure
18 
19 logger = logging.getLogger("confTool")
20 logger.setLevel(level=logging.INFO)
21 logger.addHandler(logging.FileHandler("confTool-last-run.log", mode='w'))
22 
23 componentRenamingDict={}
24 
25 baseParser = argparse.ArgumentParser()
26 baseParser.add_argument(
27  "--includeComps",
28  nargs="*",
29  default=[],
30  help="Include only components matching (regex) this string",
31  action="append",
32 )
33 baseParser.add_argument(
34  "--excludeComps",
35  nargs="*",
36  default=[],
37  help="Exclude components matching (regex) this string",
38  action="append",
39 )
40 baseParser.add_argument(
41  "--ignoreIrrelevant",
42  help="Ignore differences in e.g. outputlevel",
43  action="store_true",
44 )
45 
46 baseParser.add_argument(
47  "--includeClasses",
48  action='append',
49  default=[],
50  help="Only list the components selected by the given classname (anchored regular expression) - only used in class listing, for diff or similar use --includeComps"
51  )
52 
53 baseParser.add_argument(
54  "--excludeClasses",
55  action='append',
56  default=[],
57  help="Don't list the components excluded by the given classname (anchored regular expression) - only used in class listing, for diff or similar use --excludeComps"
58 )
59 
60 baseParser.add_argument(
61  "--ignore",
62  action="append",
63  default= [
64  "StoreGateSvc",
65  "OutputLevel",
66  "ExtraInputs",
67  "ExtraOutputs",
68  "DetStore",
69  "EvtStore",
70  "EventStore",
71  "NeededResources",
72  "GeoModelSvc",
73  "MetaDataStore",
74  "wallTimeOffset" # perfmon Svc property, is timestamp of stating the job, different by construction
75  ],
76  help="Ignore properties",
77 )
78 baseParser.add_argument(
79  "--renameComps",
80  nargs="*",
81  help="Pass comps You want to rename as OldName=NewName.",
82  action="append",
83 )
84 baseParser.add_argument(
85  "--renameCompsFile",
86  help="Pass the file containing remaps",
87 )
88 
89 baseParser.add_argument(
90  "--ignoreDefaults",
91  help="Ignore values that are identical to the c++ defaults. Use it only when when the same release is setup as the one used to generate the config.",
92  action="store_true"
93 )
94 
95 baseParser.add_argument(
96  "--ignoreDefaultNamedComps",
97  help="""Ignores default handles that have full type specified. That is, if the setting is actually: Tool/A and the default value was just A, the Tool/A is assumed to be default and eliminated.
98  Beware that there is a caveat, the ignored class name may be actually different from the default (there is no way to check that in python).""",
99  action="store_true"
100 )
101 
102 
103 baseParser.add_argument(
104  "--shortenDefaultComponents",
105  help="Automatically shorten component names that have a default name i.e. ToolX/ToolX to ToolX. It helps comparing Run2 & Run3 configurations where these are handled differently",
106  action="store_true",
107 )
108 
109 baseParser.add_argument(
110  "--skipProperties",
111  help="Do not load properties other than those referring to other components",
112  action="store_true",
113 )
114 
115 baseParser.add_argument(
116  "--follow",
117  help="Follow to related components up to given recursion depth (3)",
118  type=int,
119  default=3
120 )
121 
122 baseParser.add_argument(
123  "--debug",
124  help="Enable tool debugging messages",
125  action="store_true",
126 )
127 
129  return [item for elem in l for item in elem] if l else []
130 
131 
132 def types_in_properties(comp_name, value, dict_to_update):
133  """Updates the dictionary with (potentially) component name -> component type"""
134  parsable = False
135  try:
136  s = ast.literal_eval(str(value))
137  parsable = True
138  if isinstance(s, list):
139  for el in s:
140  types_in_properties(comp_name, el, dict_to_update)
141  except Exception:
142  pass
143  # Exclude non-strings, or strings that look like paths rather than type/name pairs
144  if isinstance(value,str):
145  slash_startend = value.startswith("/") or value.endswith("/")
146  json_dict = value.startswith("{") and value.endswith("}")
147  if value.count("/")==1 and not parsable and not slash_startend and not json_dict:
148  comp = value.split("/")
149  if len(comp) == 2:
150  # Record with and without parent
151  dict_to_update[f'{comp_name}.{comp[1]}'] = comp[0]
152  dict_to_update[f'{comp[1]}'] = comp[0]
153  logger.debug("Parsing %s, found type of %s.%s to be %s", value, comp_name, comp[1], comp[0])
154  else:
155  logger.debug("What is typeless comp? %s", value)
156  if isinstance(value, dict):
157  for v in value.values():
158  types_in_properties(comp_name, v, dict_to_update)
159 
160 
161 def collect_types(conf):
162  name_to_type = {}
163  for (comp_name, comp_settings) in conf.items():
164  types_in_properties(comp_name, comp_settings, name_to_type)
165  return name_to_type
166 
167 
168 def excludeIncludeComps(dic, args, depth, compsToFollow=[]) -> Dict:
169  conf = {}
170  if depth == 0:
171  return conf
172  compsToInclude = __flatten_list(args.includeComps)
173  compsToExclude = __flatten_list(args.excludeComps)
174 
175  def eligible(component):
176  exclude = any(re.match(s, component) for s in compsToExclude)
177  if (component in compsToFollow or component.removeprefix("ToolSvc.") in compsToFollow) and not (exclude or component in args.ignore):
178  logger.debug("Considering this component: %s because some other one depends on it", component)
179  return True
180  include = any(re.match(s, component) for s in compsToInclude)
181  if args.includeComps and args.excludeComps:
182  return include and not exclude
183  elif args.includeComps:
184  return include
185  elif args.excludeComps:
186  return not exclude
187 
188  for (comp_name, comp_attributes) in dic.items():
189  if eligible(comp_name):
190  conf[comp_name] = comp_attributes
191  if depth > 0:
192  types = {}
193  types_in_properties(comp_attributes, types, compsToFollow)
194  logger.debug("Following up for types included in here %s whole set of components to follow %s ", types, compsToFollow)
195  compsToFollow += types.keys()
196  logger.debug("Included component %s", comp_name)
197  else:
198  logger.debug("Ignored component %s", comp_name)
199  if depth > 0:
200  conf.update(excludeIncludeComps(dic, args, depth-1, compsToFollow))
201  return conf
202 
203 def ignoreIrrelevant(dic, args) -> Dict:
204  def remove_irrelevant(val_dict):
205  return (
206  { key: val for key, val in val_dict.items() if key not in args.ignore }
207  if isinstance(val_dict, dict)
208  else val_dict
209  )
210  conf = {}
211  for (key, value) in dic.items():
212  conf[key] = remove_irrelevant(value)
213  return conf
214 
215 def renameComps(dic, args) -> Dict:
216  compsToRename = __flatten_list(args.renameComps)
217  if args.renameCompsFile:
218  with open( args.renameCompsFile, "r") as refile:
219  for line in refile:
220  if not (line.startswith("#") or line.isspace() ):
221  compsToRename.append( line.rstrip('\n') )
222  global componentRenamingDict
223  componentRenamingDict.update({
224  old_name: new_name
225  for old_name, new_name in [
226  [e.strip() for e in element.split("=")] for element in compsToRename
227  ]
228  })
229  for f,t in componentRenamingDict.items():
230  logger.info("Renaming from: %s to %s", f, t)
231 
232  def rename_comps(comp_name):
233  """Renames component if it is in the dict or, when name fragment is in the dict
234  The later is for cases like: ToolSvc.ToolA.X.Y is renamed to ToolSvc.ToolB.X.Y
235  """
236  logger.debug("Trying renaming on, %s", comp_name)
237  for k,v in componentRenamingDict.items():
238  if k == comp_name:
239 # logger.debug("Renamed comp %s to %s", k, v)
240  return v
241 
242  old = f".{k}."
243  if old in comp_name:
244  return comp_name.replace(old, f".{v}.")
245 
246  old = f"{k}."
247  if comp_name.startswith(old):
248  return comp_name.replace(old, f"{v}.")
249 
250 
251  old = f".{k}"
252  if comp_name.endswith(old):
253  return comp_name.replace(old, f".{k}")
254  return comp_name
255 
256  conf = {}
257  for (key, value) in dic.items():
258  renamed = rename_comps(key)
259  if renamed != key:
260  logger.debug("Renamed comp %s to %s", key, renamed)
261  conf[renamed] = value
262  return conf
263 
264 def ignoreDefaults(allconf, args, known) -> Dict:
265  conf = {}
266  def drop_defaults(component_name, val_dict):
267  # try picking the name from the dict, if missing use last part of the name, if that fails use the component_name (heuristic)
268  component_name_last_part = component_name.split(".")[-1]
269  component_type = known.get(component_name, known.get(component_name_last_part, component_name_last_part))
270  comp_cls = None
271  try:
272  from AthenaConfiguration.ComponentFactory import CompFactory
273  comp_cls = CompFactory.getComp(component_type)
274  logger.debug("Loaded the configuration class %s/%s for defaults elimination", component_type, component_name)
275  except Exception:
276  logger.debug("Could not find the configuration class %s/%s, no defaults for it can be eliminated", component_type, component_name)
277  return val_dict
278  c = {}
279 
280  for k,v in val_dict.items():
281  if not hasattr(comp_cls,'_descriptors'):
282  logger.debug('No \'_descriptors\' attibute for %s', comp_cls)
283  continue
284  if k not in comp_cls._descriptors: # property not in descriptors (for instance, removed from component now)
285  c[k] = v
286  else:
287  default = str(comp_cls._descriptors[k].default)
288  sv = str(v)
289  if (default == sv or
290  default.replace("StoreGateSvc+", "") == sv.replace("StoreGateSvc+", "") or
291  default.replace("ConditionStore+", "") == sv.replace("ConditionStore+", "")):
292  logger.debug("Dropped default value \'%s\' of property %s in %s because the default is \'%s\'", sv, k, component_name, str(default))
293  elif args.ignoreDefaultNamedComps and isinstance(v, str) and sv.endswith(f"/{default}"):
294  logger.debug("Dropped speculatively value %s of property %s in %s because the default it ends with %s", sv, k, component_name, str(default))
295  else:
296  c[k] = v
297  logger.debug("Keep value %s of property %s in %s because it is different from default %s", str(v), str(k), component_name, str(comp_cls._descriptors[k].default))
298  return c
299 
300  # collect types for all components (we look for A/B or lost of A/B strings)
301  for (comp_name, comp_settings) in allconf.items():
302  remaining = drop_defaults(comp_name, comp_settings)
303  if len(remaining) != 0: # ignore components that have only default settings
304  conf[comp_name] = remaining
305  return conf
306 
307 def shortenDefaultComponents(dic, args) -> Dict:
308  conf = {}
309  def shorten(val):
310  value = val
311  # the value can possibly be a serialized object (like a list)
312  try:
313  value = ast.literal_eval(str(value))
314  except Exception:
315  pass
316 
317  if isinstance(value, str):
318  svalue = value.split("/")
319  if len(svalue) == 2 and svalue[0] == svalue[1]:
320  logger.debug("Shortened %s", svalue)
321  return svalue[0]
322  if isinstance(value, list):
323  return [shorten(el) for el in value]
324  if isinstance(value, dict):
325  return shorten_defaults(value)
326 
327  return value
328 
329  def shorten_defaults(val_dict):
330  if isinstance(val_dict, dict):
331  return { key: shorten(val) for key,val in val_dict.items() }
332 
333  for (key, value) in dic.items():
334  conf[key] = shorten_defaults(value)
335  return conf
336 
337 def isReference(value, compname, conf, svcCache={}) -> list:
338  """Returns a list of (component,class) if value stores reference to other components
339  value - the value to check
340  compname - full component name
341  conf - complete config dict
342  """
343 
344  def _getSvcClass(instance):
345  """Find instance in service lists to get class.
346  Keeps a cache of the service classes in the svcCache default value.
347  That's fine, unless we are dealing with more than one conf in the program.
348  In that case, initialise svcCache to {} and specify in the caller."""
349  if not svcCache: # only scan ApplicationMgr once
350  props = conf.get('ApplicationMgr',{"":None})
351  if isinstance(props,dict):
352  for prop,val in props.items():
353  if 'Svc' in prop:
354  try:
355  val = ast.literal_eval(str(val))
356  except Exception:
357  pass
358  if isinstance(val,list):
359  for v in val:
360  if isinstance(v,str):
361  vv = v.split('/')
362  if len(vv) == 2:
363  if svcCache.setdefault(vv[1], vv[0]) != vv[0]:
364  svcCache[vv[1]] = None # fail if same instance, different class
365  return svcCache.get(instance)
366 
367  try:
368  value = ast.literal_eval(str(value))
369  except Exception:
370  pass
371 
372  if isinstance(value, str):
373  ctype_name = value.split('/')
374  cls = ctype_name[0] if len(ctype_name) == 2 else None
375  instance = ctype_name[-1]
376  ref = None
377  if instance:
378  if compname and f"{compname}.{instance}" in conf: # private tool
379  ref = f"{compname}.{instance}"
380  elif f"ToolSvc.{instance}" in conf: # public tool
381  ref = f"ToolSvc.{instance}"
382  elif cls is not None or instance in conf: # service or other component
383  ref = instance
384  if cls is None:
385  cls = _getSvcClass(instance)
386  if ref is not None:
387  return [(ref, cls)]
388 
389  elif isinstance(value, list):
390  refs = [isReference(el, compname, conf) for el in value]
391  if any(refs):
392  flattened = []
393  [flattened.extend(el) for el in refs if el]
394  return flattened
395  return []
396 
397 
398 def skipProperties(conf, args) -> Dict:
399  updated = {}
400  for (name, properties) in conf.items():
401  updated[name] = {}
402  if not isinstance(properties, dict): # keep it
403  updated[name] = properties
404  else:
405  for property_name, value in properties.items():
406  if isReference( value, name, conf) or property_name == 'Members': # later for sequences structure
407  updated[name][property_name] = value
408  return updated
409 
410 def loadConfigFile(fname, args) -> Dict:
411  """loads config file into a dictionary, supports several modifications of the input switched on via additional arguments
412  Supports reading: Pickled file with the CA or properties & JSON
413  """
414  if args.debug:
415  print("Debugging info from reading ", fname, " in ", logger.handlers[0].baseFilename)
416  logger.setLevel(logging.DEBUG)
417 
418  conf = {}
419  if fname.endswith(".pkl"):
420  with open(fname, "rb") as input_file:
421  # determine if there is a old or new configuration pickled
422  cfg = pickle.load(input_file)
423  logger.info("... Read %s from %s", cfg.__class__.__name__, fname)
424  from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
425  if isinstance(cfg, ComponentAccumulator): # new configuration
426  props = cfg.gatherProps()
427  # to make json compatible with old configuration
428  jos_props = props[2]
429  to_json = {}
430  for comp, name, value in jos_props:
431  to_json.setdefault(comp, {})[name] = value
432  to_json[comp][name] = value
433  conf.update(to_json)
434  conf['ApplicationMgr'] = props[0]
435  conf['MessageSvc'] = props[1]
436 
437  elif isinstance(
438  cfg, (collections.defaultdict, dict)
439  ): # old configuration
440  conf.update(cfg)
441  conf.update(pickle.load(input_file)) # special services
442  # FIXME: there's a third pickle object with python components
443  elif isinstance(cfg, (collections.Sequence)):
444  for c in cfg:
445  conf.update(c)
446  logger.info("... Read %d items from python pickle file: %s", len(conf), fname)
447 
448  elif fname.endswith(".json"):
449 
450  def __keepPlainStrings(element):
451  if isinstance(element, str):
452  return str(element)
453  if isinstance(element, list):
454  return [__keepPlainStrings(x) for x in element]
455  if isinstance(element, dict):
456  return {
457  __keepPlainStrings(key): __keepPlainStrings(value)
458  for key, value in element.items()
459  }
460  return element
461 
462  with open(fname, "r") as input_file:
463  cfg = json.load(input_file, object_hook=__keepPlainStrings)
464  for c in cfg:
465  conf.update(cfg)
466 
467  # For compatibility with HLTJobOptions json, which produces structure:
468  # {
469  # "filetype": "joboptions",
470  # "properties": { the thing we are interested in}
471  # }
472  if 'properties' in conf:
473  conf = conf['properties']
474 
475  logger.info("... Read %d items from json file: %s", len(conf), fname)
476 
477  else:
478  sys.exit("File format not supported.")
479 
480  if conf is None:
481  sys.exit("Unable to load %s file" % fname)
482 
483  if args.includeComps or args.excludeComps or args.includeClasses or args.excludeClasses:
484  logger.info(f"include/exclude comps like {args.includeComps}/{args.excludeComps}")
485  conf = excludeIncludeComps(conf, args, args.follow)
486 
487  if args.ignoreIrrelevant:
488  conf = ignoreIrrelevant(conf, args)
489 
490  if args.renameComps or args.renameCompsFile:
491  conf = renameComps(conf, args)
492 
493  if args.ignoreDefaults:
494  known_types = collect_types(conf)
495  conf = ignoreDefaults(conf, args, known_types)
496 
497  if args.shortenDefaultComponents:
498  conf = shortenDefaultComponents(conf, args)
499 
500  if args.skipProperties:
501  conf = skipProperties(conf, args)
502  return conf
503 
505  def __init__(self, file_path: str, args, checked_elements=set()) -> None:
506  self.file_path: str = file_path
507  self.checked_elements: Set[str] = checked_elements
508  self.args = args
509 
510  def _load_file_data(self) -> Dict:
511  logger.info(f"Loading {self.file_path}")
512  return loadConfigFile(self.file_path, self.args)
513 
514 
515  def load_structure(self) -> ComponentsStructure:
516  data = self._load_file_data()
517  structure = ComponentsStructure(data, self.checked_elements)
518  structure.generate()
519  return structure
520 
521  def get_data(self) -> ComponentsStructure:
522  return self.load_structure()
523 
524 
526  def __init__(
527  self,
528  file_path: str,
529  diff_file_path: str,
530  checked_elements: Set[str],
531  ) -> None:
532  self.main_loader: ComponentsFileLoader = ComponentsFileLoader(
533  file_path, checked_elements
534  )
535  self.diff_loader: ComponentsFileLoader = ComponentsFileLoader(
536  diff_file_path, checked_elements
537  )
538 
539  def get_data(self) -> Tuple[ComponentsStructure, ComponentsStructure]:
540  structure = self.main_loader.load_structure()
541  diff_structure = self.diff_loader.load_structure()
542  self.mark_differences(structure.get_list(), diff_structure.get_list())
543  return structure, diff_structure
544 
545  def equal(self, first: Element, second: Element) -> bool:
546  return (
547  first.get_name() == second.get_name()
548  and first.x_pos == second.x_pos
549  and type(first) is type(second)
550  )
551 
553  self, structure: List[Element], diff_structure: List[Element]
554  ) -> None:
555  i, j = 0, 0
556  while i < len(structure) and j < len(diff_structure):
557  if self.equal(structure[i], diff_structure[j]):
558  if isinstance(structure[i], GroupingElement):
559  self.mark_differences(
560  cast(GroupingElement, structure[i]).children,
561  cast(GroupingElement, diff_structure[j]).children,
562  )
563  i += 1
564  j += 1
565  continue
566 
567  # Find equal element in diff structure
568  for tmp_j in range(j, len(diff_structure)):
569  if self.equal(structure[i], diff_structure[tmp_j]):
570  for marking_j in range(j, tmp_j):
571  diff_structure[marking_j].mark()
572  j = tmp_j
573  break
574  else:
575  # Not found equal element in diff structure
576  # Find equal element in first structure
577  for tmp_i in range(i, len(structure)):
578  if self.equal(structure[tmp_i], diff_structure[j]):
579  for marking_i in range(i, tmp_i):
580  structure[marking_i].mark()
581  i = tmp_i
582  break
583  else:
584  structure[i].mark()
585  diff_structure[j].mark()
586  i += 1
587  j += 1
588 
589  # Mark remaining elements in both structures
590  while i < len(structure):
591  structure[i].mark()
592  i += 1
593 
594  while j < len(diff_structure):
595  diff_structure[j].mark()
596  j += 1
597 
598 
599 def loadDifferencesFile(fname) -> Dict:
600  """
601  Read differences file
602  Format:
603  full_component_name.property oldvalue=newvalue
604  example:
605  AlgX.ToolA.SubToolB.SettingZ 45=46
606  It is possible to specify missing values, e.g:
607  AlgX.ToolA.SubToolB.SettingZ 45= means that now the old value should be ignored
608  AlgX.ToolA.SubToolB.SettingZ =46 means that now the new value should be ignored
609  AlgX.ToolA.SubToolB.SettingZ = means that any change of the value should be ignored
610 
611  """
612  from collections import defaultdict
613  differences = defaultdict(dict)
614  count=0
615  with open(fname, "r") as f:
616  for line in f:
617  if line[0] == "#" or line == "\n":
618  continue
619  line = line.strip()
620  compAndProp, values = line.split(" ")
621  comp, prop = compAndProp.rsplit(".", 1)
622  o,n = values.split("=")
623  oldval,newval = o if o else None, n if n else None
624 
625  differences[comp][prop] = (oldval,newval)
626  count+=1
627  logger.info("... Read %d known differences from file: %s", count, fname)
628  logger.info("..... %s", str(differences))
629 
630  return differences
631 
python.iconfTool.models.loaders.ComponentsFileLoader._load_file_data
Dict _load_file_data(self)
Definition: loaders.py:510
python.iconfTool.models.loaders.ignoreDefaults
Dict ignoreDefaults(allconf, args, known)
Definition: loaders.py:264
python.iconfTool.models.loaders.type
type
Definition: loaders.py:118
python.iconfTool.models.loaders.collect_types
def collect_types(conf)
Definition: loaders.py:161
python.iconfTool.models.loaders.shortenDefaultComponents
Dict shortenDefaultComponents(dic, args)
Definition: loaders.py:307
python.iconfTool.models.loaders.__flatten_list
def __flatten_list(l)
Definition: loaders.py:128
python.iconfTool.models.loaders.ComponentsFileLoader.args
args
Definition: loaders.py:508
python.iconfTool.models.loaders.types_in_properties
def types_in_properties(comp_name, value, dict_to_update)
Definition: loaders.py:132
python.iconfTool.models.loaders.skipProperties
Dict skipProperties(conf, args)
Definition: loaders.py:398
python.iconfTool.models.loaders.renameComps
Dict renameComps(dic, args)
Definition: loaders.py:215
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
python.iconfTool.models.loaders.isReference
list isReference(value, compname, conf, svcCache={})
Definition: loaders.py:337
python.iconfTool.models.loaders.excludeIncludeComps
Dict excludeIncludeComps(dic, args, depth, compsToFollow=[])
Definition: loaders.py:168
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
python.iconfTool.models.loaders.ComponentsFileLoader
Definition: loaders.py:504
python.iconfTool.models.loaders.ComponentsDiffFileLoader.__init__
None __init__(self, str file_path, str diff_file_path, Set[str] checked_elements)
Definition: loaders.py:526
python.iconfTool.models.loaders.ComponentsFileLoader.get_data
ComponentsStructure get_data(self)
Definition: loaders.py:521
python.iconfTool.models.loaders.ignoreIrrelevant
Dict ignoreIrrelevant(dic, args)
Definition: loaders.py:203
Trk::open
@ open
Definition: BinningType.h:40
python.iconfTool.models.loaders.loadDifferencesFile
Dict loadDifferencesFile(fname)
Definition: loaders.py:599
python.iconfTool.models.loaders.ComponentsFileLoader.__init__
None __init__(self, str file_path, args, checked_elements=set())
Definition: loaders.py:505
python.iconfTool.models.loaders.ComponentsDiffFileLoader.get_data
Tuple[ComponentsStructure, ComponentsStructure] get_data(self)
Definition: loaders.py:539
python.iconfTool.models.loaders.ComponentsDiffFileLoader
Definition: loaders.py:525
python.iconfTool.models.loaders.ComponentsFileLoader.load_structure
ComponentsStructure load_structure(self)
Definition: loaders.py:515
python.iconfTool.models.loaders.ComponentsDiffFileLoader.mark_differences
None mark_differences(self, List[Element] structure, List[Element] diff_structure)
Definition: loaders.py:552
str
Definition: BTagTrackIpAccessor.cxx:11
dbg::print
void print(std::FILE *stream, std::format_string< Args... > fmt, Args &&... args)
Definition: SGImplSvc.cxx:70
python.iconfTool.models.loaders.ComponentsDiffFileLoader.equal
bool equal(self, Element first, Element second)
Definition: loaders.py:545
python.iconfTool.models.loaders.loadConfigFile
Dict loadConfigFile(fname, args)
Definition: loaders.py:410