ATLAS Offline Software
han_lark_tester.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 
4 # More detailed syntax checking for han files
5 
6 
7 import lark, sys
8 from AthenaCommon.Utils.unixtools import find_datafile
9 
10 class T(lark.Transformer):
11  def __init__(self):
12  super().__init__()
13  self.library_set = set()
14 
15  def __default_token__(self,tok):
16  print(tok)
17  return tok
18  def referenceblock(self, tok):
19  locations = [_ for _ in tok if isinstance(_, lark.Tree) and _.data == 'location']
20  if len(locations) > 1:
21  raise ValueError(f'More than one location given for reference {tok[0].children[0]}')
22  if len(locations) > 0:
23  locs = locations[0].children[0].split(',')
24  else:
25  locs = [_.children[0] for _ in tok if isinstance(_, lark.Tree) and _.data == 'file']
26  afspaths = [_ for _ in locs if _.startswith('/afs')]
27  if any(afspaths):
28  raise ValueError('A reference location for a production han configuration is given with an AFS path\n'
29  f'Offending path is {",".join(afspaths)} of reference {tok[0].children[0]}')
30  eospaths = [_ for _ in locs if _.startswith('/eos')]
31  xrootdpaths = [_ for _ in locs if _.startswith('root://')]
32  if len(eospaths) != len(xrootdpaths):
33  raise ValueError(f'Backup xrootd locations must be given for references with EOS paths for reference {tok[0].children[0]}\n'
34  f'Given EOS paths are {",".join(eospaths)}\n'
35  f'Given xrootd paths are {",".join(xrootdpaths)}')
36  return tok
37 
38 
39 class AlgorithmNameInterpreter(lark.visitors.Interpreter):
40  def __init__(self):
41  super().__init__()
42 
43  def algorithmblock(self, tree: lark.Tree):
44  thisalgname = [_.children[0].value for _ in tree.children if isinstance(_, lark.Tree) and _.data == "algorithmname"][0]
45  thisalgrealname = [_ for _ in tree.children if isinstance(_, lark.Tree) and _.data == "realname"]
46  if not thisalgrealname:
47  raise ValueError(f"no underlying algorithm specified for {thisalgname}")
48  for node in tree.children:
49  if node.data == 'algorithmblock':
50  for subnode in node.children:
51  if subnode.data == 'algorithmname':
52  subnode.children[0].value = thisalgname + '/' + subnode.children[0].value
53  if not [_ for _ in node.children if isinstance(_, lark.Tree) and _.data == "realname"]:
54  node.children.append(thisalgrealname[0])
55  self.visit(node)
56 
57  def dirblock(self, tree: lark.Tree):
58  thisdirname = [_.children[0].value for _ in tree.children if isinstance(_, lark.Tree) and _.data == "dirname"][0]
59  for node in tree.children:
60  if node.data == 'dirblock':
61  for subnode in node.children:
62  if subnode.data == 'dirname':
63  subnode.children[0].value = thisdirname + '/' + subnode.children[0].value
64  self.visit(node)
65  elif node.data == 'histblock':
66  for subnode in node.children:
67  if subnode.data == 'histname':
68  subnode.children[0].value = thisdirname + '/' + subnode.children[0].value
69 
70 
71 grammarfile = find_datafile('DataQualityInterfaces/han_def.lark')
72 if grammarfile is None:
73  raise OSError('Unable to find file parser configuration file')
74 grammar = open(grammarfile).read()
75 transformer = T()
76 parser = lark.Lark(grammar, parser='lalr', lexer='contextual', transformer=transformer)
77 
78 
79 def algorithm_check(tree):
80  import ROOT
81  import cppyy
82 
83  ROOT.gSystem.Load('libdqm_core.so')
84  cppyy.include("dqm_core/AlgorithmManager.h")
85 
86  cppyy.cppdef(r"""
87 #include <dqm_core/AlgorithmManager.h>
88 std::pair<std::vector<std::string>, std::vector<std::string>> get_lib_algs() {
89 std::vector<std::string> rv1;
90 std::vector<std::string> rv2;
91 for (const auto& p : dqm_core::AlgorithmManager::instance().getAlgorithmMap()) {
92  rv1.push_back(p.first);
93 }
94 for (const auto& p : dqm_core::AlgorithmManager::instance().getSummaryMakerMap()) {
95  rv2.push_back(p.first);
96 }
97 return std::move(std::make_pair(rv1, rv2));
98 }
99 """
100  )
101 
102  rv = True
103  try:
104  AlgorithmNameInterpreter().visit(tree)
105  except ValueError as e:
106  print(f'ERROR: {e}')
107  return False
108  libs = {_.children[0].value for _ in tree.find_data('libname')} # Defined libraries
109  compalgs = {_.children[0].value for _ in tree.find_data('compalgname')} # Defined composite algorithms
110 
111  # Check if the referenced libraries exist
112  for lib in libs:
113  if ROOT.gSystem.Load(lib) < 0:
114  rv = False
115  print(f'ERROR: Library {lib} defined by a libname clause cannot be found. Referenced by:')
116  for alg in tree.find_data('algorithmblock'):
117  alibname = alg.find_data('libname')
118  if alibname and lib in {_.children[0].value for _ in alibname}:
119  print(list(alg.find_data('algorithmname'))[0].children[0].value)
120 
121  # Extract the list of "base" algorithms and summaries
122  libalgos, libsummaries = ROOT.get_lib_algs()
123 
124  # Check that the composite algorithms reference existing base algorithms (and not summaries!)
125  for alg in tree.find_data('compalgblock'):
126  subalglist = [_ for _ in alg.children if _.data == 'subalglist']
127  for tok in subalglist[0].children:
128  if tok.value not in libalgos:
129  algname = list(alg.find_data('compalgname'))[0].children[0].value
130  print(f'ERROR: composite algorithm {algname} references {tok.value} which is not a known algorithm')
131  rv = False
132 
133  # Check that all the instantiated algorithms and summaries refer to existing base algorithms and summaries
134  instalgos = set()
135  instsummaries = set()
136  for alg in tree.find_data('algorithmblock'):
137  try:
138  algname = [_.children[0].value for _ in alg.children if isinstance(_, lark.Tree) and _.data == "algorithmname"][0]
139  algrealname = [_.children[0].value for _ in alg.children if isinstance(_, lark.Tree) and _.data == "realname"][0]
140  if algrealname in libalgos:
141  instalgos.add(algname)
142  elif algrealname in libsummaries:
143  instsummaries.add(algname)
144  elif algrealname in compalgs:
145  instalgos.add(algname)
146  else:
147  print(f'ERROR: undefined base algorithm {algrealname} for {algname}')
148  rv = False
149  except Exception as e:
150  print('??? problem processing', alg, e)
151 
152  # These SHOULD just be summaries
153  for ref in tree.find_data('algorefstatement'):
154  nodealg = ref.children[0].children[0].value
155  if nodealg not in instsummaries:
156  print(f'ERROR: an output is specifying algorithm = {nodealg} which is not a known summary algorithm')
157  rv = False
158 
159  # These SHOULD just be non-summary algos
160  for ref in list(tree.find_data('dirblock')) + list(tree.find_data('histblock')):
161  nodealg = [_ for _ in ref.children if _.data == 'algorithmreference']
162  if nodealg:
163  nodealgname = nodealg[0].children[0].value
164  if nodealgname not in instalgos:
165  refname = [_ for _ in ref.children if _.data in ('histname', 'dirname')][0].children[0].value
166  print(f'ERROR: a dir/hist is specifying algorthm = {nodealgname} which is not a known histogram algorithm. Referenced by {refname}')
167  rv = False
168 
169  return rv
170 
171 
172 infile = open(sys.argv[1]).read()
173 try:
174  tree = parser.parse(infile)
175  print('Tree done')
176  print('Checking algorithms')
177  if algorithm_check(tree):
178  print(tree.pretty())
179  print('All ok')
180  else:
181  sys.exit(1)
182 
183 except Exception as e:
184  print('ERROR:')
185  print(e)
186  raise e
187  sys.exit(1)
read
IovVectorMap_t read(const Folder &theFolder, const SelectionCriterion &choice, const unsigned int limit=10)
Definition: openCoraCool.cxx:569
han_lark_tester.AlgorithmNameInterpreter.dirblock
def dirblock(self, lark.Tree tree)
Definition: han_lark_tester.py:57
han_lark_tester.T
Definition: han_lark_tester.py:10
han_lark_tester.AlgorithmNameInterpreter
Definition: han_lark_tester.py:39
han_lark_tester.T.referenceblock
def referenceblock(self, tok)
Definition: han_lark_tester.py:18
han_lark_tester.AlgorithmNameInterpreter.algorithmblock
def algorithmblock(self, lark.Tree tree)
Definition: han_lark_tester.py:43
han_lark_tester.T.library_set
library_set
Definition: han_lark_tester.py:13
han_lark_tester.T.__default_token__
def __default_token__(self, tok)
Definition: han_lark_tester.py:15
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
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
han_lark_tester.algorithm_check
def algorithm_check(tree)
Definition: han_lark_tester.py:79
print
void print(char *figname, TCanvas *c1)
Definition: TRTCalib_StrawStatusPlots.cxx:25
Trk::open
@ open
Definition: BinningType.h:40
han_lark_tester.T.__init__
def __init__(self)
Definition: han_lark_tester.py:11
han_lark_tester.AlgorithmNameInterpreter.__init__
def __init__(self)
Definition: han_lark_tester.py:40
python.Utils.unixtools.find_datafile
def find_datafile(fname, pathlist=None, access=os.R_OK)
pathresolver-like helper function --------------------------------------—
Definition: unixtools.py:67
Trk::split
@ split
Definition: LayerMaterialProperties.h:38