16 import xml.sax.saxutils
as XSS
17 from pathlib
import Path
18 from subprocess
import PIPE, Popen
19 from typing
import Any, Dict, List
28 return f
'<pre><code class="language-{self.language}">{XSS.escape(self.code)}</code></pre>'
33 return any(re.search(p, platform_id)
for p
in unsupported_platforms)
39 return dumper.represent_scalar(
"tag:yaml.org,2002:str", data, style=
"|")
40 return dumper.represent_scalar(
"tag:yaml.org,2002:str", data)
45 Send a signal to a process and all its child processes (starting from the
48 ps_cmd = [
"ps",
"--no-headers",
"-o",
"pid",
"--ppid", str(ppid)]
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:
57 except OSError
as err:
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
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]
74 executable = os.path.split(executable)[1]
77 for d
in os.environ.get(
"PATH").split(os.pathsep):
78 fullpath = os.path.join(d, executable)
79 if os.path.isfile(fullpath):
81 elif executable.endswith(
".exe")
and os.path.isfile(fullpath[:-4]):
88 Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.
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 (
105 elif os.environ.get(
"ENV_CMAKE_BUILD_TYPE",
"")
in (
120 def platform_split(p):
121 return set(re.split(
r"[-+]", p))
if p
else set()
124 dirname, basename = os.path.split(reference)
128 for suffix
in (
".yaml",
".yml"):
129 if basename.endswith(suffix):
130 prefix = f
"{basename[:-(len(suffix))]}."
134 prefix = f
"{basename}."
137 flags_slice = slice(len(prefix), -len(suffix)
if suffix
else None)
141 Extract the platform flags from a filename, return None if name does not match prefix and suffix
143 if name.startswith(prefix)
and name.endswith(suffix):
144 return platform_split(name[flags_slice])
148 if "do0" in platform:
153 (get_flags(name), name)
154 for name
in (os.listdir(dirname)
if os.path.isdir(dirname)
else [])
156 if flags
and platform.issuperset(flags)
162 return os.path.join(dirname, candidates[-1][1])
163 return os.path.join(dirname, basename)
166 def filter_dict(d: Dict[str, Any], ignore_re: re.Pattern) -> Dict[str, Any]:
168 Recursively filter out keys from the dictionary that match the ignore pattern.
171 for k, v
in d.items():
172 if not ignore_re.match(k):
173 if isinstance(v, dict):
180 def compare_dicts(d1: Dict[str, Any], d2: Dict[str, Any], ignore_re: str =
None) -> str:
182 Compare two dictionaries and return the diff as a string, ignoring keys that match the regex.
184 ignore_re = re.compile(ignore_re)
188 return "\n" +
"\n".join(
189 difflib.unified_diff(
190 pprint.pformat(filtered_d1).splitlines(),
191 pprint.pformat(filtered_d2).splitlines(),
197 h_count_re = re.compile(
r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+([\s\w=-]*)")
202 Parse the TTree summary table in lines, starting from pos.
203 Returns a tuple with the dictionary with the digested informations and the
204 position of the first line after the summary.
211 return [f.strip()
for f
in l.strip(
"*\n").split(
":", 2)]
216 cols = splitcols(ll[0])
220 r[
"Name"], r[
"Title"] = cols[1:]
225 r[
"Title"] = ll[1].strip(
"*\n").split(
"|")[1].strip()
229 cols = splitcols(ll[1 + delta_i])
230 r[
"Entries"] = int(cols[1])
232 sizes = cols[2].split()
233 r[
"Total size"] = int(sizes[2])
234 if sizes[-1] ==
"memory":
237 r[
"File size"] = int(sizes[-1])
239 cols = splitcols(ll[2 + delta_i])
240 sizes = cols[2].split()
241 if cols[0] ==
"Baskets":
242 r[
"Baskets"] = int(cols[1])
243 r[
"Basket size"] = int(sizes[2])
244 r[
"Compression"] = float(sizes[-1])
248 def nextblock(lines, i):
250 dots = re.compile(
r"^\.+$")
251 stars = re.compile(
r"^\*+$")
255 and not dots.match(lines[i + delta_i][1:-1])
256 and not stars.match(lines[i + delta_i])
261 if i < (count - 3)
and lines[i].startswith(
"*Tree"):
262 i_nextblock = nextblock(lines, i)
263 result = parseblock(lines[i:i_nextblock])
264 result[
"Branches"] = {}
266 while i < (count - 3)
and lines[i].startswith(
"*Br"):
267 if i < (count - 2)
and lines[i].startswith(
"*Branch "):
271 i_nextblock = nextblock(lines, i)
272 if i_nextblock >= count:
274 branch = parseblock(lines[i:i_nextblock])
275 result[
"Branches"][branch[
"Name"]] = branch
283 Extract the histograms infos from the lines starting at pos.
284 Returns the position of the first line after the summary block.
287 h_table_head = re.compile(
288 r'SUCCESS\s+(1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"'
290 h_short_summ = re.compile(
r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
295 m = h_count_re.search(lines[pos])
296 name = m.group(1).strip()
297 total = int(m.group(2))
299 for k, v
in [x.split(
"=")
for x
in m.group(3).split()]:
302 header[
"Total"] = total
306 m = h_table_head.search(lines[pos])
309 t = t.replace(
" profile",
"Prof")
316 if l.startswith(
" | ID"):
318 titles = [x.strip()
for x
in l.split(
"|")][1:]
320 while pos < nlines
and lines[pos].startswith(
" |"):
322 values = [x.strip()
for x
in l.split(
"|")][1:]
324 for i
in range(len(titles)):
325 hcont[titles[i]] = values[i]
326 cont[hcont[
"ID"]] = hcont
328 elif l.startswith(
" ID="):
329 while pos < nlines
and lines[pos].startswith(
" ID="):
331 x.strip()
for x
in h_short_summ.search(lines[pos]).groups()
333 cont[values[0]] = values
336 raise RuntimeError(
"Cannot understand line %d: '%s'" % (pos, l))
340 summ[d][
"header"] = header
345 summ[name] = {
"header": header}
351 Scan stdout to find ROOT Histogram summaries and digest them.
353 outlines = stdout.splitlines()
if hasattr(stdout,
"splitlines")
else stdout
354 nlines = len(outlines) - 1
362 match = h_count_re.search(outlines[pos])
363 while pos < nlines
and not match:
365 match = h_count_re.search(outlines[pos])
368 summaries.update(summ)
374 Scan stdout to find ROOT TTree summaries and digest them.
376 stars = re.compile(
r"^\*+$")
377 outlines = stdout.splitlines()
if hasattr(stdout,
"splitlines")
else stdout
378 nlines = len(outlines)
384 while i < nlines
and not stars.match(outlines[i]):
389 trees[tree[
"Name"]] = tree
395 return Path(sys.modules[cls.__module__].__file__)