14 from subprocess
import Popen, PIPE, STDOUT
19 Take a string with invalid ASCII/UTF characters and quote them so that the 20 string can be used in an XML text. 22 >>> sanitize_for_xml('this is \x1b') 23 'this is [NON-XML-CHAR-0x1B]' 25 bad_chars = re.compile(
26 u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')
30 return ''.join(
'[NON-XML-CHAR-0x%2X]' % ord(c)
for c
in match.group())
31 return bad_chars.sub(quote, data)
35 '''helper to debug GAUDI-1084, dump the list of processes''' 36 from getpass
import getuser
37 if 'WORKSPACE' in os.environ:
38 p = Popen([
'ps',
'-fH',
'-U', getuser()], stdout=PIPE)
39 with open(os.path.join(os.environ[
'WORKSPACE'], name),
'w')
as f:
40 f.write(p.communicate()[0])
45 Send a signal to a process and all its child processes (starting from the 48 log = logging.getLogger(
'kill_tree')
49 ps_cmd = [
'ps',
'--no-headers',
'-o',
'pid',
'--ppid', str(ppid)]
50 get_children = Popen(ps_cmd, stdout=PIPE, stderr=PIPE)
51 children =
map(int, get_children.communicate()[0].split())
52 for child
in children:
55 log.debug(
'killing process %d', ppid)
60 log.debug(
'no such process %d', ppid)
96 logging.debug(
'running test %s', self.
name)
99 if re.search(
r'from\s+Gaudi.Configuration\s+import\s+\*|' 100 'from\s+Configurables\s+import', self.
options):
101 optionFile = tempfile.NamedTemporaryFile(suffix=
'.py')
103 optionFile = tempfile.NamedTemporaryFile(suffix=
'.opts')
104 optionFile.file.write(self.
options)
113 self.environment.items() + os.environ.items())
115 platform_id = (os.environ.get(
'BINARY_TAG')
or 116 os.environ.get(
'CMTCONFIG')
or 119 skip_test = bool([
None 121 if re.search(prex, platform_id)])
130 workdir = tempfile.mkdtemp()
136 elif "GAUDIEXE" in os.environ:
137 prog = os.environ[
"GAUDIEXE"]
141 dummy, prog_ext = os.path.splitext(prog)
142 if prog_ext
not in [
".exe",
".py",
".bat"]:
146 prog =
which(prog)
or prog
148 args =
map(RationalizePath, self.
args)
150 if prog_ext ==
".py":
155 validatorRes =
Result({
'CAUSE':
None,
'EXCEPTION':
None,
156 'RESOURCE':
None,
'TARGET':
None,
157 'TRACEBACK':
None,
'START_TIME':
None,
158 'END_TIME':
None,
'TIMEOUT_DETAIL':
None})
159 self.
result = validatorRes
167 logging.debug(
'executing %r in %s',
169 self.
proc = Popen(params, stdout=PIPE, stderr=PIPE,
171 logging.debug(
'(pid: %d)', self.proc.pid)
172 self.
out, self.
err = self.proc.communicate()
174 thread = threading.Thread(target=target)
179 if thread.is_alive():
180 logging.debug(
'time out in test %s (pid %d)',
181 self.
name, self.proc.pid)
183 cmd = [
'gdb',
'--pid', str(self.proc.pid),
'--batch',
184 '--eval-command=thread apply all backtrace']
185 gdb = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
190 if thread.is_alive():
192 self.causes.append(
'timeout')
194 logging.debug(
'completed test %s', self.
name)
197 logging.debug(
'returnedCode = %s', self.proc.returncode)
200 logging.debug(
'validating test...')
207 shutil.rmtree(workdir,
True)
212 if self.
signal is not None:
214 self.causes.append(
'exit code')
218 self.causes.append(
'exit code')
221 self.causes.append(
"exit code")
231 logging.debug(
'%s: %s', self.
name, self.
status)
232 field_mapping = {
'Exit Code':
'returnedCode',
235 'Environment':
'environment',
238 'Program Name':
'program',
240 'Validator':
'validator',
241 'Output Reference File':
'reference',
242 'Error Reference File':
'error_reference',
245 'Unsupported Platforms':
'unsupported_platforms',
246 'Stack Trace':
'stack_trace'}
247 resultDict = [(key, getattr(self, attr))
248 for key, attr
in field_mapping.iteritems()
249 if getattr(self, attr)]
250 resultDict.append((
'Working Directory',
254 resultDict.extend(self.result.annotations.iteritems())
256 return dict(resultDict)
265 elif stderr.strip() != self.stderr.strip():
266 self.causes.append(
'standard error')
267 return result, self.
causes 269 def findReferenceBlock(self, reference=None, stdout=None, result=None, causes=None, signature_offset=0, signature=None, id=None):
271 Given a block of text, tries to find it in the output. The block had to be identified by a signature line. By default, the first line is used as signature, or the line pointed to by signature_offset. If signature_offset points outside the block, a signature line can be passed as signature argument. Note: if 'signature' is None (the default), a negative signature_offset is interpreted as index in a list (e.g. -1 means the last line), otherwise the it is interpreted as the number of lines before the first one of the block the signature must appear. The parameter 'id' allow to distinguish between different calls to this function in the same validation code. 274 if reference
is None:
284 None,
map(
lambda s: s.rstrip(), reference.splitlines()))
286 raise RuntimeError(
"Empty (or null) reference")
288 outlines = filter(
None,
map(
lambda s: s.rstrip(), stdout.splitlines()))
290 res_field =
"GaudiTest.RefBlock" 292 res_field +=
"_%s" % id
294 if signature
is None:
295 if signature_offset < 0:
296 signature_offset = len(reference) + signature_offset
297 signature = reflines[signature_offset]
300 pos = outlines.index(signature)
301 outlines = outlines[pos - signature_offset:pos +
302 len(reflines) - signature_offset]
303 if reflines != outlines:
304 msg =
"standard output" 307 if not msg
in causes:
310 ".observed"] = result.Quote(
"\n".join(outlines))
312 causes.append(
"missing signature")
313 result[res_field +
".signature"] = result.Quote(signature)
314 if len(reflines) > 1
or signature != reflines[0]:
315 result[res_field +
".expected"] = result.Quote(
"\n".join(reflines))
318 def countErrorLines(self, expected={'ERROR': 0,
'FATAL': 0}, stdout=
None, result=
None, causes=
None):
320 Count the number of messages with required severity (by default ERROR and FATAL) 321 and check if their numbers match the expected ones (0 by default). 322 The dictionary "expected" can be used to tune the number of errors and fatals 323 allowed, or to limit the number of expected warnings etc. 338 outlines = stdout.splitlines()
339 from math
import log10
340 fmt =
"%%%dd - %%s" % (int(log10(len(outlines) + 1)))
346 if len(words) >= 2
and words[1]
in errors:
347 errors[words[1]].append(fmt % (linecount, l.rstrip()))
350 if len(errors[e]) != expected[e]:
351 causes.append(
'%s(%d)' % (e, len(errors[e])))
352 result[
"GaudiTest.lines.%s" %
353 e] = result.Quote(
'\n'.join(errors[e]))
354 result[
"GaudiTest.lines.%s.expected#" %
355 e] = result.Quote(str(expected[e]))
361 ignore=
r"Basket|.*size|Compression"):
363 Compare the TTree summaries in stdout with the ones in trees_dict or in 364 the reference file. By default ignore the size, compression and basket 366 The presence of TTree summaries when none is expected is not a failure. 374 if trees_dict
is None:
377 if lreference
and os.path.isfile(lreference):
382 from pprint
import PrettyPrinter
385 result[
"GaudiTest.TTrees.expected"] = result.Quote(
386 pp.pformat(trees_dict))
388 result[
"GaudiTest.TTrees.ignore"] = result.Quote(ignore)
393 causes.append(
"trees summaries")
395 trees_dict, trees, failed)
396 result[
"GaudiTest.TTrees.failure_on"] = result.Quote(msg)
397 result[
"GaudiTest.TTrees.found"] = result.Quote(pp.pformat(trees))
405 Compare the TTree summaries in stdout with the ones in trees_dict or in 406 the reference file. By default ignore the size, compression and basket 408 The presence of TTree summaries when none is expected is not a failure. 420 if lreference
and os.path.isfile(lreference):
425 from pprint
import PrettyPrinter
428 result[
"GaudiTest.Histos.expected"] = result.Quote(
431 result[
"GaudiTest.Histos.ignore"] = result.Quote(ignore)
436 causes.append(
"histos summaries")
438 result[
"GaudiTest.Histos.failure_on"] = result.Quote(msg)
439 result[
"GaudiTest.Histos.found"] = result.Quote(pp.pformat(histos))
444 causes=
None, preproc=
None):
446 Default validation acti*on: compare standard output and error to the 461 preproc = normalizeExamples
465 if lreference
and os.path.isfile(lreference):
469 preproc=preproc)(stdout, result)
475 newref = open(lreference +
".new",
"w")
477 for l
in stdout.splitlines():
478 newref.write(l.rstrip() +
'\n')
488 if lreference
and os.path.isfile(lreference):
492 preproc=preproc)(stderr, result)
495 newref = open(lreference +
".new",
"w")
497 for l
in stderr.splitlines():
498 newref.write(l.rstrip() +
'\n')
502 "ExecTest.expected_stderr")(stderr, result)
511 def platformSplit(p):
return set(p.split(
'-' in p
and '-' or '_'))
513 reference = os.path.normpath(os.path.join(self.
basedir,
514 os.path.expandvars(reffile)))
517 spec_ref = reference[:-3] +
GetPlatform(self)[0:3] + reference[-3:]
518 if os.path.isfile(spec_ref):
522 dirname, basename = os.path.split(reference)
525 head = basename +
"." 528 if 'do0' in platform:
531 for f
in os.listdir(dirname):
532 if f.startswith(head):
533 req_plat = platformSplit(f[head_len:])
534 if platform.issuperset(req_plat):
535 candidates.append((len(req_plat), f))
540 reference = os.path.join(dirname, candidates[-1][1])
552 from GaudiKernel
import ROOT6WorkAroundEnabled
563 Function used to normalize the used path 565 newPath = os.path.normpath(os.path.expandvars(p))
566 if os.path.exists(newPath):
567 p = os.path.realpath(newPath)
573 Locates an executable in the executables path ($PATH) and returns the full 574 path to it. An application is looked for with or without the '.exe' suffix. 575 If the executable cannot be found, None is returned 577 if os.path.isabs(executable):
578 if not os.path.exists(executable):
579 if executable.endswith(
'.exe'):
580 if os.path.exists(executable[:-4]):
581 return executable[:-4]
583 head, executable = os.path.split(executable)
586 for d
in os.environ.get(
"PATH").split(os.pathsep):
587 fullpath = os.path.join(d, executable)
588 if os.path.exists(fullpath):
590 if executable.endswith(
'.exe'):
591 return which(executable[:-4])
606 UNTESTED =
'UNTESTED' 616 def __init__(self, kind=None, id=None, outcome=PASS, annotations={}):
620 assert type(key)
in types.StringTypes
624 assert type(key)
in types.StringTypes
625 assert type(value)
in types.StringTypes
649 """Validate the output of the program. 650 'stdout' -- A string containing the data written to the standard output 652 'stderr' -- A string containing the data written to the standard error 654 'result' -- A 'Result' object. It may be used to annotate 655 the outcome according to the content of stderr. 656 returns -- A list of strings giving causes of failure.""" 661 causes.append(self.
cause)
667 """Compare 's1' and 's2', ignoring line endings. 670 returns -- True if 's1' and 's2' are the same, ignoring 671 differences in line endings.""" 675 to_ignore = re.compile(
676 r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*')
678 def keep_line(l):
return not to_ignore.match(l)
679 return filter(keep_line, s1.splitlines()) == filter(keep_line, s2.splitlines())
681 return s1.splitlines() == s2.splitlines()
687 """ Base class for a callable that takes a file and returns a modified 702 if hasattr(input,
"__iter__"):
706 lines = input.splitlines()
710 output =
'\n'.join(output)
741 if line.find(s) >= 0:
757 if self.
start in line:
760 elif self.
end in line:
771 when = re.compile(when)
775 if isinstance(rhs, RegexpReplacer):
777 res._operations = self.
_operations + rhs._operations
779 res = FilePreprocessor.__add__(self, rhs)
784 if w
is None or w.search(line):
785 line = o.sub(r, line)
792 "[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9] [0-9]{4}[-/][01][0-9][-/][0-3][0-9][ A-Z]*",
793 "00:00:00 1970-01-01")
795 normalizeEOL.__processLine__ =
lambda line: str(line).rstrip() +
'\n' 799 skipEmptyLines.__processLine__ =
lambda line: (line.strip()
and line)
or None 814 line = line[:(pos + self.
siglen)]
815 lst = line[(pos + self.
siglen):].split()
817 line +=
" ".join(lst)
824 Sort group of lines matching a regular expression 828 self.
exp = exp
if hasattr(exp,
'match')
else re.compile(exp)
831 match = self.exp.match
847 normalizeExamples = maskPointers + normalizeDate
850 (
"TIMER.TIMER",
r"\s+[+-]?[0-9]+[0-9.]*",
" 0"),
851 (
"release all pending",
r"^.*/([^/]*:.*)",
r"\1"),
852 (
"^#.*file",
r"file '.*[/\\]([^/\\]*)$",
r"file '\1"),
853 (
"^JobOptionsSvc.*options successfully read in from",
854 r"read in from .*[/\\]([^/\\]*)$",
r"file \1"),
856 (
None,
r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}(?!-0{12})-[0-9A-Fa-f]{12}",
857 "00000000-0000-0000-0000-000000000000"),
859 (
"ServiceLocatorHelper::",
"ServiceLocatorHelper::(create|locate)Service",
860 "ServiceLocatorHelper::service"),
862 (
None,
r"e([-+])0([0-9][0-9])",
r"e\1\2"),
864 (
None,
r'Service reference count check:',
865 r'Looping over all active services...'),
867 (
None,
r"^(.*(DEBUG|SUCCESS) List of ALL properties of .*#properties = )\d+",
869 (
'ApplicationMgr',
r'(declareMultiSvcType|addMultiSvc): ',
''),
874 "JobOptionsSvc INFO # ",
875 "JobOptionsSvc WARNING # ",
878 "This machine has a speed",
881 "ToolSvc.Sequenc... INFO",
882 "DataListenerSvc INFO XML written to file:",
883 "[INFO]",
"[WARNING]",
884 "DEBUG No writable file catalog found which contains FID:",
885 "DEBUG Service base class initialized successfully",
887 "DEBUG Incident timing:",
891 "INFO 'CnvServices':[",
893 "DEBUG 'CnvServices':[",
898 r"^JobOptionsSvc INFO *$",
901 r"(Always|SUCCESS)\s*(Root f|[^ ]* F)ile version:",
902 r"File '.*.xml' does not exist",
903 r"INFO Refer to dataset .* by its file ID:",
904 r"INFO Referring to dataset .* by its file ID:",
905 r"INFO Disconnect from dataset",
906 r"INFO Disconnected from dataset",
907 r"INFO Disconnected data IO:",
908 r"IncidentSvc\s*(DEBUG (Adding|Removing)|VERBOSE Calling)",
910 r"^StatusCodeSvc.*listing all unchecked return codes:",
911 r"^StatusCodeSvc\s*INFO\s*$",
912 r"Num\s*\|\s*Function\s*\|\s*Source Library",
915 r"ERROR Failed to modify file: .* Errno=2 No such file or directory",
917 r"^ +[0-9]+ \|.*ROOT",
918 r"^ +[0-9]+ \|.*\|.*Dict",
920 r"StatusCodeSvc.*all StatusCode instances where checked",
922 r"EventLoopMgr.*---> Loop Finished",
923 r"HiveSlimEventLo.*---> Loop Finished",
928 r"SUCCESS\s*Booked \d+ Histogram\(s\)",
932 r"Property(.*)'Audit(Algorithm|Tool|Service)s':",
934 r"Property(.*)'AuditRe(start|initialize)':",
935 r"Property(.*)'IsIOBound':",
937 r"Property(.*)'ErrorCount(er)?':",
939 r"Property(.*)'Sequential':",
941 r"Property(.*)'FilterCircularDependencies':",
943 r"Property(.*)'IsClonable':",
945 r"Property update for OutputLevel : new value =",
946 r"EventLoopMgr\s*DEBUG Creating OutputStream",
953 r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*',
956 normalizeExamples = (lineSkipper + normalizeExamples + skipEmptyLines +
957 normalizeEOL +
LineSorter(
"Services to release : ") +
965 def __init__(self, reffile, cause, result_key, preproc=normalizeExamples):
973 if os.path.isfile(self.
reffile):
974 orig = open(self.
reffile).xreadlines()
978 result.Quote(
'\n'.join(
map(str.strip, orig)))
981 new = stdout.splitlines()
985 diffs = difflib.ndiff(orig, new, charjunk=difflib.IS_CHARACTER_JUNK)
986 filterdiffs =
map(
lambda x: x.strip(), filter(
987 lambda x: x[0] !=
" ", diffs))
989 result[self.
result_key] = result.Quote(
"\n".join(filterdiffs))
993 +) standard output of the test""")
995 result.Quote(
'\n'.join(
map(str.strip, new)))
996 causes.append(self.
cause)
1002 Scan stdout to find ROOT TTree summaries and digest them. 1004 stars = re.compile(
r"^\*+$")
1005 outlines = stdout.splitlines()
1006 nlines = len(outlines)
1012 while i < nlines
and not stars.match(outlines[i]):
1017 trees[tree[
"Name"]] = tree
1024 Check that all the keys in reference are in to_check too, with the same value. 1025 If the value is a dict, the function is called recursively. to_check can 1026 contain more keys than reference, that will not be tested. 1027 The function returns at the first difference found. 1032 ignore_re = re.compile(ignore)
1033 keys = [key
for key
in reference
if not ignore_re.match(key)]
1035 keys = reference.keys()
1039 if (
type(reference[k])
is dict)
and (
type(to_check[k])
is dict):
1043 reference[k], to_check[k], ignore)
1046 failed = to_check[k] != reference[k]
1051 fail_keys.insert(0, k)
1062 if c
is None or r
is None:
1064 return (fail_path, r, c)
1068 h_count_re = re.compile(
1069 r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+([\s\w=-]*)")
1074 Parse the TTree summary table in lines, starting from pos. 1075 Returns a tuple with the dictionary with the digested informations and the 1076 position of the first line after the summary. 1082 def splitcols(l):
return [f.strip()
for f
in l.strip(
"*\n").split(
':', 2)]
1086 cols = splitcols(ll[0])
1087 r[
"Name"], r[
"Title"] = cols[1:]
1089 cols = splitcols(ll[1])
1090 r[
"Entries"] = int(cols[1])
1092 sizes = cols[2].split()
1093 r[
"Total size"] = int(sizes[2])
1094 if sizes[-1] ==
"memory":
1097 r[
"File size"] = int(sizes[-1])
1099 cols = splitcols(ll[2])
1100 sizes = cols[2].split()
1101 if cols[0] ==
"Baskets":
1102 r[
"Baskets"] = int(cols[1])
1103 r[
"Basket size"] = int(sizes[2])
1104 r[
"Compression"] = float(sizes[-1])
1107 if i < (count - 3)
and lines[i].startswith(
"*Tree"):
1108 result = parseblock(lines[i:i + 3])
1109 result[
"Branches"] = {}
1111 while i < (count - 3)
and lines[i].startswith(
"*Br"):
1112 if i < (count - 2)
and lines[i].startswith(
"*Branch "):
1116 branch = parseblock(lines[i:i + 3])
1117 result[
"Branches"][branch[
"Name"]] = branch
1125 Extract the histograms infos from the lines starting at pos. 1126 Returns the position of the first line after the summary block. 1129 h_table_head = re.compile(
1130 r'SUCCESS\s+List of booked (1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"')
1131 h_short_summ = re.compile(
r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
1136 m = h_count_re.search(lines[pos])
1137 name = m.group(1).strip()
1138 total = int(m.group(2))
1140 for k, v
in [x.split(
"=")
for x
in m.group(3).split()]:
1143 header[
"Total"] = total
1147 m = h_table_head.search(lines[pos])
1150 t = t.replace(
" profile",
"Prof")
1157 if l.startswith(
" | ID"):
1159 titles = [x.strip()
for x
in l.split(
"|")][1:]
1161 while pos < nlines
and lines[pos].startswith(
" |"):
1163 values = [x.strip()
for x
in l.split(
"|")][1:]
1165 for i
in range(len(titles)):
1166 hcont[titles[i]] = values[i]
1167 cont[hcont[
"ID"]] = hcont
1169 elif l.startswith(
" ID="):
1170 while pos < nlines
and lines[pos].startswith(
" ID="):
1172 for x
in h_short_summ.search(lines[pos]).groups()]
1173 cont[values[0]] = values
1177 "Cannot understand line %d: '%s'" % (pos, l))
1181 summ[d][
"header"] = header
1186 summ[name] = {
"header": header}
1192 Scan stdout to find ROOT TTree summaries and digest them. 1194 outlines = stdout.splitlines()
1195 nlines = len(outlines) - 1
1203 match = h_count_re.search(outlines[pos])
1204 while pos < nlines
and not match:
1206 match = h_count_re.search(outlines[pos])
1209 summaries.update(summ)
1215 unsupported = [re.compile(x)
for x
in [str(y).strip()
1216 for y
in unsupported_platforms]
if x]
1217 for p_re
in unsupported:
1218 if p_re.search(platform):
1219 result.SetOutcome(result.UNTESTED)
1220 result[result.CAUSE] =
'Platform not supported.' 1227 Return the platform Id defined in CMTCONFIG or SCRAM_ARCH. 1231 if "BINARY_TAG" in os.environ:
1232 arch = os.environ[
"BINARY_TAG"]
1233 elif "CMTCONFIG" in os.environ:
1234 arch = os.environ[
"CMTCONFIG"]
1235 elif "SCRAM_ARCH" in os.environ:
1236 arch = os.environ[
"SCRAM_ARCH"]
1242 Return True if the current platform is Windows. 1244 This function was needed because of the change in the CMTCONFIG format, 1245 from win32_vc71_dbg to i686-winxp-vc9-dbg. 1248 return "winxp" in platform
or platform.startswith(
"win")
def PlatformIsNotSupported(self, context, result)
def __processLine__(self, line)
def __init__(self, start, end)
def __call__(self, input)
def validateWithReference(self, stdout=None, stderr=None, result=None, causes=None, preproc=None)
def __processLine__(self, line)
def cmpTreesDicts(reference, to_check, ignore=None)
def __processFile__(self, lines)
def ValidateOutput(self, stdout, stderr, result)
def read(f, regex='.*', skipevents=0)
def __processLine__(self, line)
def __processFile__(self, lines)
def __call__(self, out, result)
def findHistosSummaries(stdout)
def _parseTTreeSummary(lines, pos)
struct GAUDI_API map
Parametrisation class for map-like implementation.
def __call__(self, stdout, result)
def __processLine__(self, line)
def __init__(self, orig, repl="", when=None)
decltype(auto) range(Args &&...args)
Zips multiple containers together to form a single range.
def __init__(self, signature)
def __call__(self, input)
def sanitize_for_xml(data)
def getCmpFailingValues(reference, to_check, fail_path)
def __init__(self, members=[])
def __init__(self, strings=[], regexps=[])
def __setitem__(self, key, value)
def __init__(self, kind=None, id=None, outcome=PASS, annotations={})
def __processLine__(self, line)
def parseHistosSummary(lines, pos)
def _expandReferenceFileName(self, reffile)
def findReferenceBlock(self, reference=None, stdout=None, result=None, causes=None, signature_offset=0, signature=None, id=None)
def CheckHistosSummaries(self, stdout=None, result=None, causes=None, dict=None, ignore=None)
def __CompareText(self, s1, s2)
def __init__(self, reffile, cause, result_key, preproc=normalizeExamples)
def __getitem__(self, key)
def findTTreeSummaries(stdout)
def __init__(self, ref, cause, result_key)
def ROOT6WorkAroundEnabled(id=None)
def CheckTTreesSummaries(self, stdout=None, result=None, causes=None, trees_dict=None, ignore=r"Basket|.*size|Compression")