The Gaudi Framework  master (ff829712)
Loading...
Searching...
No Matches
GaudiTesting.utils Namespace Reference

Classes

class  CodeWrapper
 

Functions

 platform_matches (List[str] unsupported_platforms)
 
 str_representer (dumper, data)
 
 kill_tree (ppid, sig)
 
 which (executable)
 
 get_platform ()
 
 expand_reference_file_name (reference)
 
Dict[str, Any] filter_dict (Dict[str, Any] d, re.Pattern ignore_re)
 
str compare_dicts (Dict[str, Any] d1, Dict[str, Any] d2, str ignore_re=None)
 
 _parse_ttree_summary (lines, pos)
 
 _parse_histos_summary (lines, pos)
 
 find_histos_summaries (stdout)
 
 find_ttree_summaries (stdout)
 
 file_path_for_class (cls)
 

Variables

 h_count_re
 

Function Documentation

◆ _parse_histos_summary()

GaudiTesting.utils._parse_histos_summary ( lines,
pos )
protected
Extract the histograms infos from the lines starting at pos.
Returns the position of the first line after the summary block.

Definition at line 283 of file utils.py.

283def _parse_histos_summary(lines, pos):
284 """
285 Extract the histograms infos from the lines starting at pos.
286 Returns the position of the first line after the summary block.
287 """
288 global h_count_re
289 h_table_head = re.compile(
290 r'(?:SUCCESS|INFO)\s+(1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"'
291 )
292 h_short_summ = re.compile(r"ID=([^\"]+)\s+\"([^\"]*)\"\s+(.*)")
293
294 nlines = len(lines)
295
296 # decode header
297 m = h_count_re.search(lines[pos])
298 name = m.group(1).strip()
299 total = int(m.group(2))
300 header = {}
301 for k, v in [x.split("=") for x in m.group(3).split()]:
302 header[k] = int(v)
303 pos += 1
304 header["Total"] = total
305
306 summ = {}
307 while pos < nlines:
308 m = h_table_head.search(lines[pos])
309 if m:
310 t, d = m.groups(1) # type and directory
311 t = t.replace(" profile", "Prof")
312 pos += 1
313 if pos < nlines:
314 l = lines[pos]
315 else:
316 l = ""
317 cont = {}
318 if l.startswith(" | ID"):
319 # table format
320 titles = [x.strip() for x in l.split("|")][1:-1]
321 pos += 1
322 while pos < nlines and lines[pos].startswith(" |"):
323 l = lines[pos]
324 values = [x.strip() for x in l.split("|")][1:]
325 hcont = {}
326 for i in range(len(titles)):
327 hcont[titles[i]] = values[i]
328 cont[hcont["ID"]] = hcont
329 pos += 1
330 elif l.startswith(" ID="):
331 while pos < nlines and lines[pos].startswith(" ID="):
332 values = [
333 x.strip() for x in h_short_summ.search(lines[pos]).groups()
334 ]
335 cont[values[0]] = values
336 pos += 1
337 else: # not interpreted
338 raise RuntimeError("Cannot understand line %d: '%s'" % (pos, l))
339 if d not in summ:
340 summ[d] = {}
341 summ[d][t] = cont
342 summ[d]["header"] = header
343 else:
344 break
345 if not summ:
346 # If the full table is not present, we use only the header
347 summ[name] = {"header": header}
348 return summ, pos
349
350

◆ _parse_ttree_summary()

GaudiTesting.utils._parse_ttree_summary ( lines,
pos )
protected
Parse the TTree summary table in lines, starting from pos.
Returns a tuple with the dictionary with the digested informations and the
position of the first line after the summary.

Definition at line 202 of file utils.py.

202def _parse_ttree_summary(lines, pos):
203 """
204 Parse the TTree summary table in lines, starting from pos.
205 Returns a tuple with the dictionary with the digested informations and the
206 position of the first line after the summary.
207 """
208 result = {}
209 i = pos + 1 # first line is a sequence of '*'
210 count = len(lines)
211
212 def splitcols(l):
213 return [f.strip() for f in l.strip("*\n").split(":", 2)]
214
215 def parseblock(ll):
216 r = {}
217 delta_i = 0
218 cols = splitcols(ll[0])
219
220 if len(ll) == 3:
221 # default one line name/title
222 r["Name"], r["Title"] = cols[1:]
223 elif len(ll) == 4:
224 # in case title is moved to next line due to too long name
225 delta_i = 1
226 r["Name"] = cols[1]
227 r["Title"] = ll[1].strip("*\n").split("|")[1].strip()
228 else:
229 assert False
230
231 cols = splitcols(ll[1 + delta_i])
232 r["Entries"] = int(cols[1])
233
234 sizes = cols[2].split()
235 r["Total size"] = int(sizes[2])
236 if sizes[-1] == "memory":
237 r["File size"] = 0
238 else:
239 r["File size"] = int(sizes[-1])
240
241 cols = splitcols(ll[2 + delta_i])
242 sizes = cols[2].split()
243 if cols[0] == "Baskets":
244 r["Baskets"] = int(cols[1])
245 r["Basket size"] = int(sizes[2])
246 r["Compression"] = float(sizes[-1])
247
248 return r
249
250 def nextblock(lines, i):
251 delta_i = 1
252 dots = re.compile(r"^\.+$")
253 stars = re.compile(r"^\*+$")
254 count = len(lines)
255 while (
256 i + delta_i < count
257 and not dots.match(lines[i + delta_i][1:-1])
258 and not stars.match(lines[i + delta_i])
259 ):
260 delta_i += 1
261 return i + delta_i
262
263 if i < (count - 3) and lines[i].startswith("*Tree"):
264 i_nextblock = nextblock(lines, i)
265 result = parseblock(lines[i:i_nextblock])
266 result["Branches"] = {}
267 i = i_nextblock + 1
268 while i < (count - 3) and lines[i].startswith("*Br"):
269 if i < (count - 2) and lines[i].startswith("*Branch "):
270 # skip branch header
271 i += 3
272 continue
273 i_nextblock = nextblock(lines, i)
274 if i_nextblock >= count:
275 break
276 branch = parseblock(lines[i:i_nextblock])
277 result["Branches"][branch["Name"]] = branch
278 i = i_nextblock + 1
279
280 return (result, i)
281
282

◆ compare_dicts()

str GaudiTesting.utils.compare_dicts ( Dict[str, Any] d1,
Dict[str, Any] d2,
str ignore_re = None )
Compare two dictionaries and return the diff as a string, ignoring keys that match the regex.

Definition at line 180 of file utils.py.

180def compare_dicts(d1: Dict[str, Any], d2: Dict[str, Any], ignore_re: str = None) -> str:
181 """
182 Compare two dictionaries and return the diff as a string, ignoring keys that match the regex.
183 """
184 ignore_re = re.compile(ignore_re)
185 filtered_d1 = filter_dict(d1, ignore_re)
186 filtered_d2 = filter_dict(d2, ignore_re)
187
188 return "\n" + "\n".join(
189 difflib.unified_diff(
190 pprint.pformat(filtered_d1).splitlines(),
191 pprint.pformat(filtered_d2).splitlines(),
192 )
193 )
194
195
196# signature of the print-out of the histograms

◆ expand_reference_file_name()

GaudiTesting.utils.expand_reference_file_name ( reference)

Definition at line 114 of file utils.py.

114def expand_reference_file_name(reference):
115 # if no file is passed, do nothing
116 if not reference:
117 return reference
118
119 # function to split an extension in constituents parts
120 def platform_split(p):
121 return set(re.split(r"[-+]", p)) if p else set()
122
123 # get all the files whose name start with the reference filename
124 dirname, basename = os.path.split(reference)
125 if not dirname:
126 dirname = "."
127
128 for suffix in (".yaml", ".yml"):
129 if basename.endswith(suffix):
130 prefix = f"{basename[:-(len(suffix))]}."
131 break
132 else:
133 # no special suffix matched, fallback on no suffix
134 prefix = f"{basename}."
135 suffix = ""
136
137 flags_slice = slice(len(prefix), -len(suffix) if suffix else None)
138
139 def get_flags(name):
140 """
141 Extract the platform flags from a filename, return None if name does not match prefix and suffix
142 """
143 if name.startswith(prefix) and name.endswith(suffix):
144 return platform_split(name[flags_slice])
145 return None
146
147 platform = platform_split(get_platform())
148 if "do0" in platform:
149 platform.add("dbg")
150 candidates = [
151 (len(flags), name)
152 for flags, name in [
153 (get_flags(name), name)
154 for name in (os.listdir(dirname) if os.path.isdir(dirname) else [])
155 ]
156 if flags and platform.issuperset(flags)
157 ]
158 if candidates: # take the one with highest matching
159 # FIXME: it is not possible to say if x86_64-slc5-gcc43-dbg
160 # has to use yaml.x86_64-gcc43 or yaml.slc5-dbg
161 candidates.sort()
162 return os.path.join(dirname, candidates[-1][1])
163 return os.path.join(dirname, basename)
164
165

◆ file_path_for_class()

GaudiTesting.utils.file_path_for_class ( cls)

Definition at line 396 of file utils.py.

396def file_path_for_class(cls):
397 return Path(sys.modules[cls.__module__].__file__)

◆ filter_dict()

Dict[str, Any] GaudiTesting.utils.filter_dict ( Dict[str, Any] d,
re.Pattern ignore_re )
Recursively filter out keys from the dictionary that match the ignore pattern.

Definition at line 166 of file utils.py.

166def filter_dict(d: Dict[str, Any], ignore_re: re.Pattern) -> Dict[str, Any]:
167 """
168 Recursively filter out keys from the dictionary that match the ignore pattern.
169 """
170 filteredDict = {}
171 for k, v in d.items():
172 if not ignore_re.match(k):
173 if isinstance(v, dict):
174 filteredDict[k] = filter_dict(v, ignore_re)
175 else:
176 filteredDict[k] = v
177 return filteredDict
178
179

◆ find_histos_summaries()

GaudiTesting.utils.find_histos_summaries ( stdout)
Scan stdout to find ROOT Histogram summaries and digest them.

Definition at line 351 of file utils.py.

351def find_histos_summaries(stdout):
352 """
353 Scan stdout to find ROOT Histogram summaries and digest them.
354 """
355 outlines = stdout.splitlines() if hasattr(stdout, "splitlines") else stdout
356 nlines = len(outlines) - 1
357 summaries = {}
358 global h_count_re
359
360 pos = 0
361 while pos < nlines:
362 summ = {}
363 # find first line of block:
364 match = h_count_re.search(outlines[pos])
365 while pos < nlines and not match:
366 pos += 1
367 match = h_count_re.search(outlines[pos])
368 if match:
369 summ, pos = _parse_histos_summary(outlines, pos)
370 summaries.update(summ)
371 return summaries
372
373

◆ find_ttree_summaries()

GaudiTesting.utils.find_ttree_summaries ( stdout)
Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 374 of file utils.py.

374def find_ttree_summaries(stdout):
375 """
376 Scan stdout to find ROOT TTree summaries and digest them.
377 """
378 stars = re.compile(r"^\*+$")
379 outlines = stdout.splitlines() if hasattr(stdout, "splitlines") else stdout
380 nlines = len(outlines)
381 trees = {}
382
383 i = 0
384 while i < nlines: # loop over the output
385 # look for
386 while i < nlines and not stars.match(outlines[i]):
387 i += 1
388 if i < nlines:
389 tree, i = _parse_ttree_summary(outlines, i)
390 if tree:
391 trees[tree["Name"]] = tree
392
393 return trees
394
395

◆ get_platform()

GaudiTesting.utils.get_platform ( )
Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.

Definition at line 86 of file utils.py.

86def get_platform():
87 """
88 Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.
89 """
90 arch = "None"
91 # check architecture name
92 if "BINARY_TAG" in os.environ:
93 arch = os.environ["BINARY_TAG"]
94 elif "CMTCONFIG" in os.environ:
95 arch = os.environ["CMTCONFIG"]
96 elif "SCRAM_ARCH" in os.environ:
97 arch = os.environ["SCRAM_ARCH"]
98 elif os.environ.get("ENV_CMAKE_BUILD_TYPE", "") in (
99 "Debug", # -O0 -g
100 "FastDebug", # -Og -g (LHCb only)
101 "Developer", # same as Debug, but with many warnings enabled
102 "", # no options (equivalent to -O0)
103 ):
104 arch = "unknown-dbg"
105 elif os.environ.get("ENV_CMAKE_BUILD_TYPE", "") in (
106 "Release", # -O3 -DNDEBUG
107 "MinSizeRel", # -Os -DNDEBUG
108 "RelWithDebInfo", # -O2 -g -DNDEBUG (-O3 for LHCb)
109 ):
110 arch = "unknown-opt"
111 return arch
112
113

◆ kill_tree()

GaudiTesting.utils.kill_tree ( ppid,
sig )
Send a signal to a process and all its child processes (starting from the
leaves).

Definition at line 43 of file utils.py.

43def kill_tree(ppid, sig):
44 """
45 Send a signal to a process and all its child processes (starting from the
46 leaves).
47 """
48 ps_cmd = ["ps", "--no-headers", "-o", "pid", "--ppid", str(ppid)]
49 # Note: start in a clean env to avoid a freeze with libasan.so
50 # See https://sourceware.org/bugzilla/show_bug.cgi?id=27653
51 get_children = Popen(ps_cmd, stdout=PIPE, stderr=PIPE, env={})
52 children = map(int, get_children.communicate()[0].split())
53 for child in children:
54 kill_tree(child, sig)
55 try:
56 os.kill(ppid, sig)
57 except OSError as err:
58 if err.errno != 3: # No such process
59 raise
60
61

◆ platform_matches()

GaudiTesting.utils.platform_matches ( List[str] unsupported_platforms)

Definition at line 31 of file utils.py.

31def platform_matches(unsupported_platforms: List[str]):
32 platform_id = get_platform()
33 return any(re.search(p, platform_id) for p in unsupported_platforms)
34
35
36# merci https://stackoverflow.com/a/33300001

◆ str_representer()

GaudiTesting.utils.str_representer ( dumper,
data )

Definition at line 37 of file utils.py.

37def str_representer(dumper, data):
38 if "\n" in data:
39 return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
40 return dumper.represent_scalar("tag:yaml.org,2002:str", data)
41
42

◆ which()

GaudiTesting.utils.which ( executable)
Locates an executable in the executables path ($PATH) and returns the full
path to it.  An application is looked for with or without the '.exe' suffix.
If the executable cannot be found, None is returned

Definition at line 62 of file utils.py.

62def which(executable):
63 """
64 Locates an executable in the executables path ($PATH) and returns the full
65 path to it. An application is looked for with or without the '.exe' suffix.
66 If the executable cannot be found, None is returned
67 """
68 if os.path.isabs(executable):
69 if not os.path.isfile(executable):
70 if executable.endswith(".exe"):
71 if os.path.isfile(executable[:-4]):
72 return executable[:-4]
73 else:
74 executable = os.path.split(executable)[1]
75 else:
76 return executable
77 for d in os.environ.get("PATH").split(os.pathsep):
78 fullpath = os.path.join(d, executable)
79 if os.path.isfile(fullpath):
80 return fullpath
81 elif executable.endswith(".exe") and os.path.isfile(fullpath[:-4]):
82 return fullpath[:-4]
83 return None
84
85

Variable Documentation

◆ h_count_re

GaudiTesting.utils.h_count_re
Initial value:
1= re.compile(
2 r"^(.*)(?:SUCCESS|INFO)\s+Booked (\d+) Histogram\‍(s\‍) :\s+([\s\w=-]*)"
3)

Definition at line 197 of file utils.py.