ATLAS Offline Software
loaders.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2025 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  componentRenamingDict.update({
223  old_name: new_name
224  for old_name, new_name in [
225  [e.strip() for e in element.split("=")] for element in compsToRename
226  ]
227  })
228  for f,t in componentRenamingDict.items():
229  logger.info("Renaming from: %s to %s", f, t)
230 
231  def rename_comps(comp_name):
232  """Renames component if it is in the dict or, when name fragment is in the dict
233  The later is for cases like: ToolSvc.ToolA.X.Y is renamed to ToolSvc.ToolB.X.Y
234  """
235  logger.debug("Trying renaming on, %s", comp_name)
236  for k,v in componentRenamingDict.items():
237  if k == comp_name:
238 # logger.debug("Renamed comp %s to %s", k, v)
239  return v
240 
241  old = f".{k}."
242  if old in comp_name:
243  return comp_name.replace(old, f".{v}.")
244 
245  old = f"{k}."
246  if comp_name.startswith(old):
247  return comp_name.replace(old, f"{v}.")
248 
249 
250  old = f".{k}"
251  if comp_name.endswith(old):
252  return comp_name.replace(old, f".{k}")
253  return comp_name
254 
255  conf = {}
256  for (key, value) in dic.items():
257  renamed = rename_comps(key)
258  if renamed != key:
259  logger.debug("Renamed comp %s to %s", key, renamed)
260  conf[renamed] = value
261  return conf
262 
263 def ignoreDefaults(allconf, args, known) -> Dict:
264  conf = {}
265  def drop_defaults(component_name, val_dict):
266  # try picking the name from the dict, if missing use last part of the name, if that fails use the component_name (heuristic)
267  component_name_last_part = component_name.split(".")[-1]
268  component_type = known.get(component_name, known.get(component_name_last_part, component_name_last_part))
269  comp_cls = None
270  try:
271  from AthenaConfiguration.ComponentFactory import CompFactory
272  comp_cls = CompFactory.getComp(component_type)
273  logger.debug("Loaded the configuration class %s/%s for defaults elimination", component_type, component_name)
274  except Exception:
275  logger.debug("Could not find the configuration class %s/%s, no defaults for it can be eliminated", component_type, component_name)
276  return val_dict
277  c = {}
278 
279  for k,v in val_dict.items():
280  if not hasattr(comp_cls,'_descriptors'):
281  logger.debug('No \'_descriptors\' attibute for %s', comp_cls)
282  continue
283  if k not in comp_cls._descriptors: # property not in descriptors (for instance, removed from component now)
284  c[k] = v
285  else:
286  default = str(comp_cls._descriptors[k].default)
287  sv = str(v)
288  if (default == sv or
289  default.replace("StoreGateSvc+", "") == sv.replace("StoreGateSvc+", "") or
290  default.replace("ConditionStore+", "") == sv.replace("ConditionStore+", "")):
291  logger.debug("Dropped default value \'%s\' of property %s in %s because the default is \'%s\'", sv, k, component_name, str(default))
292  elif args.ignoreDefaultNamedComps and isinstance(v, str) and sv.endswith(f"/{default}"):
293  logger.debug("Dropped speculatively value %s of property %s in %s because the default it ends with %s", sv, k, component_name, str(default))
294  else:
295  c[k] = v
296  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))
297  return c
298 
299  # collect types for all components (we look for A/B or lost of A/B strings)
300  for (comp_name, comp_settings) in allconf.items():
301  remaining = drop_defaults(comp_name, comp_settings)
302  if len(remaining) != 0: # ignore components that have only default settings
303  conf[comp_name] = remaining
304  return conf
305 
306 def shortenDefaultComponents(dic, args) -> Dict:
307  conf = {}
308  def shorten(val):
309  value = val
310  # the value can possibly be a serialized object (like a list)
311  try:
312  value = ast.literal_eval(str(value))
313  except Exception:
314  pass
315 
316  if isinstance(value, str):
317  svalue = value.split("/")
318  if len(svalue) == 2 and svalue[0] == svalue[1]:
319  logger.debug("Shortened %s", svalue)
320  return svalue[0]
321  if isinstance(value, list):
322  return [shorten(el) for el in value]
323  if isinstance(value, dict):
324  return shorten_defaults(value)
325 
326  return value
327 
328  def shorten_defaults(val_dict):
329  if isinstance(val_dict, dict):
330  return { key: shorten(val) for key,val in val_dict.items() }
331 
332  for (key, value) in dic.items():
333  conf[key] = shorten_defaults(value)
334  return conf
335 
336 def isReference(value, compname, conf, svcCache={}) -> list:
337  """Returns a list of (component,class) if value stores reference to other components
338  value - the value to check
339  compname - full component name
340  conf - complete config dict
341  """
342 
343  def _getSvcClass(instance):
344  """Find instance in service lists to get class.
345  Keeps a cache of the service classes in the svcCache default value.
346  That's fine, unless we are dealing with more than one conf in the program.
347  In that case, initialise svcCache to {} and specify in the caller."""
348  if not svcCache: # only scan ApplicationMgr once
349  props = conf.get('ApplicationMgr',{"":None})
350  if isinstance(props,dict):
351  for prop,val in props.items():
352  if 'Svc' in prop:
353  try:
354  val = ast.literal_eval(str(val))
355  except Exception:
356  pass
357  if isinstance(val,list):
358  for v in val:
359  if isinstance(v,str):
360  vv = v.split('/')
361  if len(vv) == 2:
362  if svcCache.setdefault(vv[1], vv[0]) != vv[0]:
363  svcCache[vv[1]] = None # fail if same instance, different class
364  return svcCache.get(instance)
365 
366  try:
367  value = ast.literal_eval(str(value))
368  except Exception:
369  pass
370 
371  if isinstance(value, str):
372  ctype_name = value.split('/')
373  cls = ctype_name[0] if len(ctype_name) == 2 else None
374  instance = ctype_name[-1]
375  ref = None
376  if instance:
377  if compname and f"{compname}.{instance}" in conf: # private tool
378  ref = f"{compname}.{instance}"
379  elif f"ToolSvc.{instance}" in conf: # public tool
380  ref = f"ToolSvc.{instance}"
381  elif cls is not None or instance in conf: # service or other component
382  ref = instance
383  if cls is None:
384  cls = _getSvcClass(instance)
385  if ref is not None:
386  return [(ref, cls)]
387 
388  elif isinstance(value, list):
389  refs = [isReference(el, compname, conf) for el in value]
390  if any(refs):
391  flattened = []
392  [flattened.extend(el) for el in refs if el]
393  return flattened
394  return []
395 
396 
397 def skipProperties(conf, args) -> Dict:
398  updated = {}
399  for (name, properties) in conf.items():
400  updated[name] = {}
401  if not isinstance(properties, dict): # keep it
402  updated[name] = properties
403  else:
404  for property_name, value in properties.items():
405  if isReference( value, name, conf) or property_name == 'Members': # later for sequences structure
406  updated[name][property_name] = value
407  return updated
408 
409 def loadConfigFile(fname, args) -> Dict:
410  """loads config file into a dictionary, supports several modifications of the input switched on via additional arguments
411  Supports reading: Pickled file with the CA or properties & JSON
412  """
413  if args.debug:
414  print("Debugging info from reading ", fname, " in ", logger.handlers[0].baseFilename)
415  logger.setLevel(logging.DEBUG)
416 
417  conf = {}
418  if fname.endswith(".pkl"):
419  with open(fname, "rb") as input_file:
420  # determine if there is a old or new configuration pickled
421  cfg = pickle.load(input_file)
422  logger.info("... Read %s from %s", cfg.__class__.__name__, fname)
423  from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
424  if isinstance(cfg, ComponentAccumulator): # new configuration
425  props = cfg.gatherProps()
426  # to make json compatible with old configuration
427  jos_props = props[2]
428  to_json = {}
429  for comp, name, value in jos_props:
430  to_json.setdefault(comp, {})[name] = value
431  to_json[comp][name] = value
432  conf.update(to_json)
433  conf['ApplicationMgr'] = props[0]
434  conf['MessageSvc'] = props[1]
435 
436  elif isinstance(
437  cfg, (collections.defaultdict, dict)
438  ): # old configuration
439  conf.update(cfg)
440  conf.update(pickle.load(input_file)) # special services
441  # FIXME: there's a third pickle object with python components
442  elif isinstance(cfg, (collections.Sequence)):
443  for c in cfg:
444  conf.update(c)
445  logger.info("... Read %d items from python pickle file: %s", len(conf), fname)
446 
447  elif fname.endswith(".json"):
448 
449  def __keepPlainStrings(element):
450  if isinstance(element, str):
451  return str(element)
452  if isinstance(element, list):
453  return [__keepPlainStrings(x) for x in element]
454  if isinstance(element, dict):
455  return {
456  __keepPlainStrings(key): __keepPlainStrings(value)
457  for key, value in element.items()
458  }
459  return element
460 
461  with open(fname, "r") as input_file:
462  cfg = json.load(input_file, object_hook=__keepPlainStrings)
463  for c in cfg:
464  conf.update(cfg)
465 
466  # For compatibility with HLTJobOptions json, which produces structure:
467  # {
468  # "filetype": "joboptions",
469  # "properties": { the thing we are interested in}
470  # }
471  if 'properties' in conf:
472  conf = conf['properties']
473 
474  logger.info("... Read %d items from json file: %s", len(conf), fname)
475 
476  else:
477  sys.exit("File format not supported.")
478 
479  if conf is None:
480  sys.exit("Unable to load %s file" % fname)
481 
482  if args.includeComps or args.excludeComps or args.includeClasses or args.excludeClasses:
483  logger.info(f"include/exclude comps like {args.includeComps}/{args.excludeComps}")
484  conf = excludeIncludeComps(conf, args, args.follow)
485 
486  if args.ignoreIrrelevant:
487  conf = ignoreIrrelevant(conf, args)
488 
489  if args.renameComps or args.renameCompsFile:
490  conf = renameComps(conf, args)
491 
492  if args.ignoreDefaults:
493  known_types = collect_types(conf)
494  conf = ignoreDefaults(conf, args, known_types)
495 
496  if args.shortenDefaultComponents:
497  conf = shortenDefaultComponents(conf, args)
498 
499  if args.skipProperties:
500  conf = skipProperties(conf, args)
501  return conf
502 
504  def __init__(self, file_path: str, args, checked_elements=set()) -> None:
505  self.file_path: str = file_path
506  self.checked_elements: Set[str] = checked_elements
507  self.args = args
508 
509  def _load_file_data(self) -> Dict:
510  logger.info(f"Loading {self.file_path}")
511  return loadConfigFile(self.file_path, self.args)
512 
513 
514  def load_structure(self) -> ComponentsStructure:
515  data = self._load_file_data()
516  structure = ComponentsStructure(data, self.checked_elements)
517  structure.generate()
518  return structure
519 
520  def get_data(self) -> ComponentsStructure:
521  return self.load_structure()
522 
523 
525  def __init__(
526  self,
527  file_path: str,
528  diff_file_path: str,
529  checked_elements: Set[str],
530  ) -> None:
531  self.main_loader: ComponentsFileLoader = ComponentsFileLoader(
532  file_path, checked_elements
533  )
534  self.diff_loader: ComponentsFileLoader = ComponentsFileLoader(
535  diff_file_path, checked_elements
536  )
537 
538  def get_data(self) -> Tuple[ComponentsStructure, ComponentsStructure]:
539  structure = self.main_loader.load_structure()
540  diff_structure = self.diff_loader.load_structure()
541  self.mark_differences(structure.get_list(), diff_structure.get_list())
542  return structure, diff_structure
543 
544  def equal(self, first: Element, second: Element) -> bool:
545  return (
546  first.get_name() == second.get_name()
547  and first.x_pos == second.x_pos
548  and type(first) is type(second)
549  )
550 
552  self, structure: List[Element], diff_structure: List[Element]
553  ) -> None:
554  i, j = 0, 0
555  while i < len(structure) and j < len(diff_structure):
556  if self.equal(structure[i], diff_structure[j]):
557  if isinstance(structure[i], GroupingElement):
558  self.mark_differences(
559  cast(GroupingElement, structure[i]).children,
560  cast(GroupingElement, diff_structure[j]).children,
561  )
562  i += 1
563  j += 1
564  continue
565 
566  # Find equal element in diff structure
567  for tmp_j in range(j, len(diff_structure)):
568  if self.equal(structure[i], diff_structure[tmp_j]):
569  for marking_j in range(j, tmp_j):
570  diff_structure[marking_j].mark()
571  j = tmp_j
572  break
573  else:
574  # Not found equal element in diff structure
575  # Find equal element in first structure
576  for tmp_i in range(i, len(structure)):
577  if self.equal(structure[tmp_i], diff_structure[j]):
578  for marking_i in range(i, tmp_i):
579  structure[marking_i].mark()
580  i = tmp_i
581  break
582  else:
583  structure[i].mark()
584  diff_structure[j].mark()
585  i += 1
586  j += 1
587 
588  # Mark remaining elements in both structures
589  while i < len(structure):
590  structure[i].mark()
591  i += 1
592 
593  while j < len(diff_structure):
594  diff_structure[j].mark()
595  j += 1
596 
597 
598 def loadDifferencesFile(fname) -> Dict:
599  """
600  Read differences file
601  Format:
602  full_component_name.property oldvalue=newvalue
603  example:
604  AlgX.ToolA.SubToolB.SettingZ 45=46
605  It is possible to specify missing values, e.g:
606  AlgX.ToolA.SubToolB.SettingZ 45= means that now the old value should be ignored
607  AlgX.ToolA.SubToolB.SettingZ =46 means that now the new value should be ignored
608  AlgX.ToolA.SubToolB.SettingZ = means that any change of the value should be ignored
609 
610  """
611  from collections import defaultdict
612  differences = defaultdict(dict)
613  count=0
614  with open(fname, "r") as f:
615  for line in f:
616  if line[0] == "#" or line == "\n":
617  continue
618  line = line.strip()
619  compAndProp, values = line.split(" ")
620  comp, prop = compAndProp.rsplit(".", 1)
621  o,n = values.split("=")
622  oldval,newval = o if o else None, n if n else None
623 
624  differences[comp][prop] = (oldval,newval)
625  count+=1
626  logger.info("... Read %d known differences from file: %s", count, fname)
627  logger.info("..... %s", str(differences))
628 
629  return differences
630 
python.iconfTool.models.loaders.ComponentsFileLoader._load_file_data
Dict _load_file_data(self)
Definition: loaders.py:509
python.iconfTool.models.loaders.ignoreDefaults
Dict ignoreDefaults(allconf, args, known)
Definition: loaders.py:263
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:306
python.iconfTool.models.loaders.__flatten_list
def __flatten_list(l)
Definition: loaders.py:128
python.iconfTool.models.loaders.ComponentsFileLoader.args
args
Definition: loaders.py:507
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:397
python.iconfTool.models.loaders.renameComps
Dict renameComps(dic, args)
Definition: loaders.py:215
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:194
python.iconfTool.models.loaders.isReference
list isReference(value, compname, conf, svcCache={})
Definition: loaders.py:336
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
print
void print(char *figname, TCanvas *c1)
Definition: TRTCalib_StrawStatusPlots.cxx:26
python.iconfTool.models.loaders.ComponentsFileLoader
Definition: loaders.py:503
python.iconfTool.models.loaders.ComponentsDiffFileLoader.__init__
None __init__(self, str file_path, str diff_file_path, Set[str] checked_elements)
Definition: loaders.py:525
python.iconfTool.models.loaders.ComponentsFileLoader.get_data
ComponentsStructure get_data(self)
Definition: loaders.py:520
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:598
python.iconfTool.models.loaders.ComponentsFileLoader.__init__
None __init__(self, str file_path, args, checked_elements=set())
Definition: loaders.py:504
python.iconfTool.models.loaders.ComponentsDiffFileLoader.get_data
Tuple[ComponentsStructure, ComponentsStructure] get_data(self)
Definition: loaders.py:538
python.iconfTool.models.loaders.ComponentsDiffFileLoader
Definition: loaders.py:524
python.iconfTool.models.loaders.ComponentsFileLoader.load_structure
ComponentsStructure load_structure(self)
Definition: loaders.py:514
python.iconfTool.models.loaders.ComponentsDiffFileLoader.mark_differences
None mark_differences(self, List[Element] structure, List[Element] diff_structure)
Definition: loaders.py:551
str
Definition: BTagTrackIpAccessor.cxx:11
python.iconfTool.models.loaders.ComponentsDiffFileLoader.equal
bool equal(self, Element first, Element second)
Definition: loaders.py:544
python.iconfTool.models.loaders.loadConfigFile
Dict loadConfigFile(fname, args)
Definition: loaders.py:409