From c0a9af1e9a63e96a0bea2b4fde74ce35d29a494c Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 4 Nov 2024 17:14:16 -0600 Subject: [PATCH] scripts: Moved recursive entry generation before table rendering This fixes an issue where mixing recursive renderers (-t/--hot or -z/--depth) with defines (-Dfunction=lfsr_mount) would not account for children entry widths. An unexpected side-effect of no longer filtering the children entries. We could continue to try to estimate the width without table rendering, but it would basically need two full recursive pass at this point... Instead, I've just moved the recursive stuff before table rendering, which should remove any issues with width calculation while also deduplicating the recursive passes. It's invasive for a small change, but probably worthwhile long term. The downside is this does mean our recursive scripts now build the full table (including all recursive calls!) before they start printing. When mixed with unbounded recursive depth (-z0 or --depth=0) this can get quite large and cause quite a slow start. But I guess that was the tradeoff in adopting this sort of intermediate table rendering... At least it does make the code simpler and less bug prone... --- scripts/code.py | 1 + scripts/cov.py | 1 + scripts/data.py | 1 + scripts/perf.py | 304 ++++++++++++++++++--------------------------- scripts/perfbd.py | 304 ++++++++++++++++++--------------------------- scripts/stack.py | 304 ++++++++++++++++++--------------------------- scripts/structs.py | 1 + scripts/summary.py | 1 + 8 files changed, 377 insertions(+), 540 deletions(-) diff --git a/scripts/code.py b/scripts/code.py index 09aa9116..b920137f 100755 --- a/scripts/code.py +++ b/scripts/code.py @@ -430,6 +430,7 @@ def table(Result, results, diff_results=None, *, header.append('d'+k) lines.append(header) + # entry helper def table_entry(name, r, diff_r=None): entry = [name] if diff_results is None: diff --git a/scripts/cov.py b/scripts/cov.py index 0b83842c..a8091fb9 100755 --- a/scripts/cov.py +++ b/scripts/cov.py @@ -418,6 +418,7 @@ def table(Result, results, diff_results=None, *, header.append('d'+k) lines.append(header) + # entry helper def table_entry(name, r, diff_r=None): entry = [name] if diff_results is None: diff --git a/scripts/data.py b/scripts/data.py index ccb1dfae..7f00ad9a 100755 --- a/scripts/data.py +++ b/scripts/data.py @@ -430,6 +430,7 @@ def table(Result, results, diff_results=None, *, header.append('d'+k) lines.append(header) + # entry helper def table_entry(name, r, diff_r=None): entry = [name] if diff_results is None: diff --git a/scripts/perf.py b/scripts/perf.py index 8086f1df..01c8590c 100755 --- a/scripts/perf.py +++ b/scripts/perf.py @@ -746,6 +746,7 @@ def table(Result, results, diff_results=None, *, header.append('d'+k) lines.append(header) + # entry helper def table_entry(name, r, diff_r=None): entry = [name] if diff_results is None: @@ -790,6 +791,110 @@ def table(Result, results, diff_results=None, *, getattr(diff_r, k, None))))) return entry + # recursive entry helpers + if hot: + def recurse(results_, depth_, seen=set(), + prefixes=('', '', '', '')): + # build the children table at each layer + results_ = fold(Result, results_, by=by) + table_ = { + ','.join(str(getattr(r, k) or '') for k in by): r + for r in results_} + names_ = list(table_.keys()) + if not names_: + return + + # find the "hottest" path at each step, we use + # the sort field if requested, but ignore reversedness + name = max(names_, + key=lambda n: tuple( + tuple( + # make sure to use the rebuilt table + (getattr(table_[n], k),) + if getattr(table_.get(n), k, None) is not None + else () + for k in ([k] if k else [ + k for k in Result._sort if k in fields]) + if k in fields) + for k, reverse in it.chain( + sort or [], + [(None, False)]))) + + r = table_[name] + is_last = not r.children + + line = table_entry(name, r) + line = [x if isinstance(x, tuple) else (x, []) for x in line] + # add prefixes + line[0] = (prefixes[0+is_last] + line[0][0], line[0][1]) + # add cycle detection + if name in seen: + line[-1] = (line[-1][0], line[-1][1] + ['cycle detected']) + lines.append(line) + + # found a cycle? + if name in seen: + return + + # recurse? + if depth_ > 1: + recurse( + r.children, + depth_-1, + seen | {name}, + prefixes) + + else: + def recurse(results_, depth_, seen=set(), + prefixes=('', '', '', '')): + # build the children table at each layer + results_ = fold(Result, results_, by=by) + table_ = { + ','.join(str(getattr(r, k) or '') for k in by): r + for r in results_} + names_ = list(table_.keys()) + + # sort the children layer + names_.sort() + if sort: + for k, reverse in reversed(sort): + names_.sort( + key=lambda n: tuple( + (getattr(table_[n], k),) + if getattr(table_.get(n), k, None) is not None + else () + for k in ([k] if k else [ + k for k in Result._sort if k in fields])), + reverse=reverse ^ (not k or k in Result._fields)) + + for i, name in enumerate(names_): + r = table_[name] + is_last = (i == len(names_)-1) + + line = table_entry(name, r) + line = [x if isinstance(x, tuple) else (x, []) for x in line] + # add prefixes + line[0] = (prefixes[0+is_last] + line[0][0], line[0][1]) + # add cycle detection + if name in seen: + line[-1] = (line[-1][0], line[-1][1] + ['cycle detected']) + lines.append(line) + + # found a cycle? + if name in seen: + continue + + # recurse? + if depth_ > 1: + recurse( + r.children, + depth_-1, + seen | {name}, + (prefixes[2+is_last] + "|-> ", + prefixes[2+is_last] + "'-> ", + prefixes[2+is_last] + "| ", + prefixes[2+is_last] + " ")) + # entries if not summary: for name in names: @@ -800,6 +905,17 @@ def table(Result, results, diff_results=None, *, diff_r = diff_table.get(name) lines.append(table_entry(name, r, diff_r)) + # recursive entries + if name in table and depth > 1: + recurse( + table[name].children, + depth-1, + {name}, + ("|-> ", + "'-> ", + "| ", + " ")) + # total r = next(iter(fold(Result, results, by=[])), None) if diff_results is None: @@ -822,186 +938,14 @@ def table(Result, results, diff_results=None, *, widths[i] = max(widths[i], ((len(x[0])+1+4-1)//4)*4-1) notes[i] = max(notes[i], 1+2*len(x[1])+sum(len(n) for n in x[1])) - if not summary: - # find the actual depth - depth_ = depth - if hot: - depth_ = 2 - elif m.isinf(depth_): - def rec_depth(results_, seen=set()): - # build the children table at each layer - results_ = fold(Result, results_, by=by) - table_ = { - ','.join(str(getattr(r, k) or '') for k in by): r - for r in results_} - names_ = list(table_.keys()) - - return max( - (rec_depth(table_[name].children, seen | {name}) - for name in names_ - if name not in seen), - default=-1) + 1 - - depth_ = max( - (rec_depth(table[name].children, {name}) - for name in names - if name in table), - default=-1) + 1 - - # adjust the name width based on the call depth - widths[0] += 4*max(depth_-1, 0) - - # print the tree recursively - print('%-*s %s' % ( - widths[0], lines[0][0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') - for i, x in enumerate(lines[0][1:], 1)))) - - if not summary: - if hot: - def recurse(results_, depth_, seen=set(), - prefixes=('', '', '', '')): - # build the children table at each layer - results_ = fold(Result, results_, by=by) - table_ = { - ','.join(str(getattr(r, k) or '') for k in by): r - for r in results_} - names_ = list(table_.keys()) - if not names_: - return - - # find the "hottest" path at each step, we use - # the sort field if requested, but ignore reversedness - name = max(names_, - key=lambda n: tuple( - tuple( - # make sure to use the rebuilt table - (getattr(table_[n], k),) - if getattr(table_.get(n), k, None) is not None - else () - for k in ([k] if k else [ - k for k in Result._sort if k in fields]) - if k in fields) - for k, reverse in it.chain( - sort or [], - [(None, False)]))) - - r = table_[name] - is_last = not r.children - - line = table_entry(name, r) - line = [x if isinstance(x, tuple) else (x, []) - for x in line] - print('%s%-*s %s' % ( - prefixes[0+is_last], - widths[0] - len(prefixes[0+is_last]), line[0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], - ' (%s)' % ', '.join(it.chain( - x[1], ['cycle detected'])) - if i == len(widths)-1 and name in seen - else ' (%s)' % ', '.join(x[1]) if x[1] - else '') - for i, x in enumerate(line[1:], 1)))) - - # found a cycle? - if name in seen: - return - - # recurse? - if depth_ > 1: - recurse( - r.children, - depth_-1, - seen | {name}, - prefixes) - - else: - def recurse(results_, depth_, seen=set(), - prefixes=('', '', '', '')): - # build the children table at each layer - results_ = fold(Result, results_, by=by) - table_ = { - ','.join(str(getattr(r, k) or '') for k in by): r - for r in results_} - names_ = list(table_.keys()) - - # sort the children layer - names_.sort() - if sort: - for k, reverse in reversed(sort): - names_.sort( - key=lambda n: tuple( - (getattr(table_[n], k),) - if getattr(table_.get(n), k, None) is not None - else () - for k in ([k] if k else [ - k for k in Result._sort if k in fields])), - reverse=reverse ^ (not k or k in Result._fields)) - - for i, name in enumerate(names_): - r = table_[name] - is_last = (i == len(names_)-1) - - line = table_entry(name, r) - line = [x if isinstance(x, tuple) else (x, []) - for x in line] - print('%s%-*s %s' % ( - prefixes[0+is_last], - widths[0] - len(prefixes[0+is_last]), line[0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], - ' (%s)' % ', '.join(it.chain( - x[1], ['cycle detected'])) - if i == len(widths)-1 and name in seen - else ' (%s)' % ', '.join(x[1]) if x[1] - else '') - for i, x in enumerate(line[1:], 1)))) - - # found a cycle? - if name in seen: - continue - - # recurse? - if depth_ > 1: - recurse( - r.children, - depth_-1, - seen | {name}, - (prefixes[2+is_last] + "|-> ", - prefixes[2+is_last] + "'-> ", - prefixes[2+is_last] + "| ", - prefixes[2+is_last] + " ")) - - # the top layer is a bit of a special case - for name, line in zip(names, lines[1:-1]): - print('%-*s %s' % ( - widths[0], line[0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') - for i, x in enumerate(line[1:], 1)))) - - if name in table and depth > 1: - recurse( - table[name].children, - depth-1, - {name}, - ("|-> ", - "'-> ", - "| ", - " ")) - - print('%-*s %s' % ( - widths[0], lines[-1][0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') - for i, x in enumerate(lines[-1][1:], 1)))) + # print our table + for line in lines: + print('%-*s %s' % ( + widths[0], line[0][0], + ' '.join('%*s%-*s' % ( + widths[i], x[0], + notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') + for i, x in enumerate(line[1:], 1)))) def annotate(Result, results, *, diff --git a/scripts/perfbd.py b/scripts/perfbd.py index cfe2e8f1..1a2ba31d 100755 --- a/scripts/perfbd.py +++ b/scripts/perfbd.py @@ -710,6 +710,7 @@ def table(Result, results, diff_results=None, *, header.append('d'+k) lines.append(header) + # entry helper def table_entry(name, r, diff_r=None): entry = [name] if diff_results is None: @@ -754,6 +755,110 @@ def table(Result, results, diff_results=None, *, getattr(diff_r, k, None))))) return entry + # recursive entry helpers + if hot: + def recurse(results_, depth_, seen=set(), + prefixes=('', '', '', '')): + # build the children table at each layer + results_ = fold(Result, results_, by=by) + table_ = { + ','.join(str(getattr(r, k) or '') for k in by): r + for r in results_} + names_ = list(table_.keys()) + if not names_: + return + + # find the "hottest" path at each step, we use + # the sort field if requested, but ignore reversedness + name = max(names_, + key=lambda n: tuple( + tuple( + # make sure to use the rebuilt table + (getattr(table_[n], k),) + if getattr(table_.get(n), k, None) is not None + else () + for k in ([k] if k else [ + k for k in Result._sort if k in fields]) + if k in fields) + for k, reverse in it.chain( + sort or [], + [(None, False)]))) + + r = table_[name] + is_last = not r.children + + line = table_entry(name, r) + line = [x if isinstance(x, tuple) else (x, []) for x in line] + # add prefixes + line[0] = (prefixes[0+is_last] + line[0][0], line[0][1]) + # add cycle detection + if name in seen: + line[-1] = (line[-1][0], line[-1][1] + ['cycle detected']) + lines.append(line) + + # found a cycle? + if name in seen: + return + + # recurse? + if depth_ > 1: + recurse( + r.children, + depth_-1, + seen | {name}, + prefixes) + + else: + def recurse(results_, depth_, seen=set(), + prefixes=('', '', '', '')): + # build the children table at each layer + results_ = fold(Result, results_, by=by) + table_ = { + ','.join(str(getattr(r, k) or '') for k in by): r + for r in results_} + names_ = list(table_.keys()) + + # sort the children layer + names_.sort() + if sort: + for k, reverse in reversed(sort): + names_.sort( + key=lambda n: tuple( + (getattr(table_[n], k),) + if getattr(table_.get(n), k, None) is not None + else () + for k in ([k] if k else [ + k for k in Result._sort if k in fields])), + reverse=reverse ^ (not k or k in Result._fields)) + + for i, name in enumerate(names_): + r = table_[name] + is_last = (i == len(names_)-1) + + line = table_entry(name, r) + line = [x if isinstance(x, tuple) else (x, []) for x in line] + # add prefixes + line[0] = (prefixes[0+is_last] + line[0][0], line[0][1]) + # add cycle detection + if name in seen: + line[-1] = (line[-1][0], line[-1][1] + ['cycle detected']) + lines.append(line) + + # found a cycle? + if name in seen: + continue + + # recurse? + if depth_ > 1: + recurse( + r.children, + depth_-1, + seen | {name}, + (prefixes[2+is_last] + "|-> ", + prefixes[2+is_last] + "'-> ", + prefixes[2+is_last] + "| ", + prefixes[2+is_last] + " ")) + # entries if not summary: for name in names: @@ -764,6 +869,17 @@ def table(Result, results, diff_results=None, *, diff_r = diff_table.get(name) lines.append(table_entry(name, r, diff_r)) + # recursive entries + if name in table and depth > 1: + recurse( + table[name].children, + depth-1, + {name}, + ("|-> ", + "'-> ", + "| ", + " ")) + # total r = next(iter(fold(Result, results, by=[])), None) if diff_results is None: @@ -786,186 +902,14 @@ def table(Result, results, diff_results=None, *, widths[i] = max(widths[i], ((len(x[0])+1+4-1)//4)*4-1) notes[i] = max(notes[i], 1+2*len(x[1])+sum(len(n) for n in x[1])) - if not summary: - # find the actual depth - depth_ = depth - if hot: - depth_ = 2 - elif m.isinf(depth_): - def rec_depth(results_, seen=set()): - # build the children table at each layer - results_ = fold(Result, results_, by=by) - table_ = { - ','.join(str(getattr(r, k) or '') for k in by): r - for r in results_} - names_ = list(table_.keys()) - - return max( - (rec_depth(table_[name].children, seen | {name}) - for name in names_ - if name not in seen), - default=-1) + 1 - - depth_ = max( - (rec_depth(table[name].children, {name}) - for name in names - if name in table), - default=-1) + 1 - - # adjust the name width based on the call depth - widths[0] += 4*max(depth_-1, 0) - - # print the tree recursively - print('%-*s %s' % ( - widths[0], lines[0][0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') - for i, x in enumerate(lines[0][1:], 1)))) - - if not summary: - if hot: - def recurse(results_, depth_, seen=set(), - prefixes=('', '', '', '')): - # build the children table at each layer - results_ = fold(Result, results_, by=by) - table_ = { - ','.join(str(getattr(r, k) or '') for k in by): r - for r in results_} - names_ = list(table_.keys()) - if not names_: - return - - # find the "hottest" path at each step, we use - # the sort field if requested, but ignore reversedness - name = max(names_, - key=lambda n: tuple( - tuple( - # make sure to use the rebuilt table - (getattr(table_[n], k),) - if getattr(table_.get(n), k, None) is not None - else () - for k in ([k] if k else [ - k for k in Result._sort if k in fields]) - if k in fields) - for k, reverse in it.chain( - sort or [], - [(None, False)]))) - - r = table_[name] - is_last = not r.children - - line = table_entry(name, r) - line = [x if isinstance(x, tuple) else (x, []) - for x in line] - print('%s%-*s %s' % ( - prefixes[0+is_last], - widths[0] - len(prefixes[0+is_last]), line[0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], - ' (%s)' % ', '.join(it.chain( - x[1], ['cycle detected'])) - if i == len(widths)-1 and name in seen - else ' (%s)' % ', '.join(x[1]) if x[1] - else '') - for i, x in enumerate(line[1:], 1)))) - - # found a cycle? - if name in seen: - return - - # recurse? - if depth_ > 1: - recurse( - r.children, - depth_-1, - seen | {name}, - prefixes) - - else: - def recurse(results_, depth_, seen=set(), - prefixes=('', '', '', '')): - # build the children table at each layer - results_ = fold(Result, results_, by=by) - table_ = { - ','.join(str(getattr(r, k) or '') for k in by): r - for r in results_} - names_ = list(table_.keys()) - - # sort the children layer - names_.sort() - if sort: - for k, reverse in reversed(sort): - names_.sort( - key=lambda n: tuple( - (getattr(table_[n], k),) - if getattr(table_.get(n), k, None) is not None - else () - for k in ([k] if k else [ - k for k in Result._sort if k in fields])), - reverse=reverse ^ (not k or k in Result._fields)) - - for i, name in enumerate(names_): - r = table_[name] - is_last = (i == len(names_)-1) - - line = table_entry(name, r) - line = [x if isinstance(x, tuple) else (x, []) - for x in line] - print('%s%-*s %s' % ( - prefixes[0+is_last], - widths[0] - len(prefixes[0+is_last]), line[0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], - ' (%s)' % ', '.join(it.chain( - x[1], ['cycle detected'])) - if i == len(widths)-1 and name in seen - else ' (%s)' % ', '.join(x[1]) if x[1] - else '') - for i, x in enumerate(line[1:], 1)))) - - # found a cycle? - if name in seen: - continue - - # recurse? - if depth_ > 1: - recurse( - r.children, - depth_-1, - seen | {name}, - (prefixes[2+is_last] + "|-> ", - prefixes[2+is_last] + "'-> ", - prefixes[2+is_last] + "| ", - prefixes[2+is_last] + " ")) - - # the top layer is a bit of a special case - for name, line in zip(names, lines[1:-1]): - print('%-*s %s' % ( - widths[0], line[0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') - for i, x in enumerate(line[1:], 1)))) - - if name in table and depth > 1: - recurse( - table[name].children, - depth-1, - {name}, - ("|-> ", - "'-> ", - "| ", - " ")) - - print('%-*s %s' % ( - widths[0], lines[-1][0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') - for i, x in enumerate(lines[-1][1:], 1)))) + # print our table + for line in lines: + print('%-*s %s' % ( + widths[0], line[0][0], + ' '.join('%*s%-*s' % ( + widths[i], x[0], + notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') + for i, x in enumerate(line[1:], 1)))) def annotate(Result, results, *, diff --git a/scripts/stack.py b/scripts/stack.py index 6390721b..9501ebdf 100755 --- a/scripts/stack.py +++ b/scripts/stack.py @@ -395,6 +395,7 @@ def table(Result, results, diff_results=None, *, header.append('d'+k) lines.append(header) + # entry helper def table_entry(name, r, diff_r=None): entry = [name] if diff_results is None: @@ -439,6 +440,110 @@ def table(Result, results, diff_results=None, *, getattr(diff_r, k, None))))) return entry + # recursive entry helpers + if hot: + def recurse(results_, depth_, seen=set(), + prefixes=('', '', '', '')): + # build the children table at each layer + results_ = fold(Result, results_, by=by) + table_ = { + ','.join(str(getattr(r, k) or '') for k in by): r + for r in results_} + names_ = list(table_.keys()) + if not names_: + return + + # find the "hottest" path at each step, we use + # the sort field if requested, but ignore reversedness + name = max(names_, + key=lambda n: tuple( + tuple( + # make sure to use the rebuilt table + (getattr(table_[n], k),) + if getattr(table_.get(n), k, None) is not None + else () + for k in ([k] if k else [ + k for k in Result._sort if k in fields]) + if k in fields) + for k, reverse in it.chain( + sort or [], + [(None, False)]))) + + r = table_[name] + is_last = not r.children + + line = table_entry(name, r) + line = [x if isinstance(x, tuple) else (x, []) for x in line] + # add prefixes + line[0] = (prefixes[0+is_last] + line[0][0], line[0][1]) + # add cycle detection + if name in seen: + line[-1] = (line[-1][0], line[-1][1] + ['cycle detected']) + lines.append(line) + + # found a cycle? + if name in seen: + return + + # recurse? + if depth_ > 1: + recurse( + r.children, + depth_-1, + seen | {name}, + prefixes) + + else: + def recurse(results_, depth_, seen=set(), + prefixes=('', '', '', '')): + # build the children table at each layer + results_ = fold(Result, results_, by=by) + table_ = { + ','.join(str(getattr(r, k) or '') for k in by): r + for r in results_} + names_ = list(table_.keys()) + + # sort the children layer + names_.sort() + if sort: + for k, reverse in reversed(sort): + names_.sort( + key=lambda n: tuple( + (getattr(table_[n], k),) + if getattr(table_.get(n), k, None) is not None + else () + for k in ([k] if k else [ + k for k in Result._sort if k in fields])), + reverse=reverse ^ (not k or k in Result._fields)) + + for i, name in enumerate(names_): + r = table_[name] + is_last = (i == len(names_)-1) + + line = table_entry(name, r) + line = [x if isinstance(x, tuple) else (x, []) for x in line] + # add prefixes + line[0] = (prefixes[0+is_last] + line[0][0], line[0][1]) + # add cycle detection + if name in seen: + line[-1] = (line[-1][0], line[-1][1] + ['cycle detected']) + lines.append(line) + + # found a cycle? + if name in seen: + continue + + # recurse? + if depth_ > 1: + recurse( + r.children, + depth_-1, + seen | {name}, + (prefixes[2+is_last] + "|-> ", + prefixes[2+is_last] + "'-> ", + prefixes[2+is_last] + "| ", + prefixes[2+is_last] + " ")) + # entries if not summary: for name in names: @@ -449,6 +554,17 @@ def table(Result, results, diff_results=None, *, diff_r = diff_table.get(name) lines.append(table_entry(name, r, diff_r)) + # recursive entries + if name in table and depth > 1: + recurse( + table[name].children, + depth-1, + {name}, + ("|-> ", + "'-> ", + "| ", + " ")) + # total r = next(iter(fold(Result, results, by=[])), None) if diff_results is None: @@ -471,186 +587,14 @@ def table(Result, results, diff_results=None, *, widths[i] = max(widths[i], ((len(x[0])+1+4-1)//4)*4-1) notes[i] = max(notes[i], 1+2*len(x[1])+sum(len(n) for n in x[1])) - if not summary: - # find the actual depth - depth_ = depth - if hot: - depth_ = 2 - elif m.isinf(depth_): - def rec_depth(results_, seen=set()): - # build the children table at each layer - results_ = fold(Result, results_, by=by) - table_ = { - ','.join(str(getattr(r, k) or '') for k in by): r - for r in results_} - names_ = list(table_.keys()) - - return max( - (rec_depth(table_[name].children, seen | {name}) - for name in names_ - if name not in seen), - default=-1) + 1 - - depth_ = max( - (rec_depth(table[name].children, {name}) - for name in names - if name in table), - default=-1) + 1 - - # adjust the name width based on the call depth - widths[0] += 4*max(depth_-1, 0) - - # print the tree recursively - print('%-*s %s' % ( - widths[0], lines[0][0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') - for i, x in enumerate(lines[0][1:], 1)))) - - if not summary: - if hot: - def recurse(results_, depth_, seen=set(), - prefixes=('', '', '', '')): - # build the children table at each layer - results_ = fold(Result, results_, by=by) - table_ = { - ','.join(str(getattr(r, k) or '') for k in by): r - for r in results_} - names_ = list(table_.keys()) - if not names_: - return - - # find the "hottest" path at each step, we use - # the sort field if requested, but ignore reversedness - name = max(names_, - key=lambda n: tuple( - tuple( - # make sure to use the rebuilt table - (getattr(table_[n], k),) - if getattr(table_.get(n), k, None) is not None - else () - for k in ([k] if k else [ - k for k in Result._sort if k in fields]) - if k in fields) - for k, reverse in it.chain( - sort or [], - [(None, False)]))) - - r = table_[name] - is_last = not r.children - - line = table_entry(name, r) - line = [x if isinstance(x, tuple) else (x, []) - for x in line] - print('%s%-*s %s' % ( - prefixes[0+is_last], - widths[0] - len(prefixes[0+is_last]), line[0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], - ' (%s)' % ', '.join(it.chain( - x[1], ['cycle detected'])) - if i == len(widths)-1 and name in seen - else ' (%s)' % ', '.join(x[1]) if x[1] - else '') - for i, x in enumerate(line[1:], 1)))) - - # found a cycle? - if name in seen: - return - - # recurse? - if depth_ > 1: - recurse( - r.children, - depth_-1, - seen | {name}, - prefixes) - - else: - def recurse(results_, depth_, seen=set(), - prefixes=('', '', '', '')): - # build the children table at each layer - results_ = fold(Result, results_, by=by) - table_ = { - ','.join(str(getattr(r, k) or '') for k in by): r - for r in results_} - names_ = list(table_.keys()) - - # sort the children layer - names_.sort() - if sort: - for k, reverse in reversed(sort): - names_.sort( - key=lambda n: tuple( - (getattr(table_[n], k),) - if getattr(table_.get(n), k, None) is not None - else () - for k in ([k] if k else [ - k for k in Result._sort if k in fields])), - reverse=reverse ^ (not k or k in Result._fields)) - - for i, name in enumerate(names_): - r = table_[name] - is_last = (i == len(names_)-1) - - line = table_entry(name, r) - line = [x if isinstance(x, tuple) else (x, []) - for x in line] - print('%s%-*s %s' % ( - prefixes[0+is_last], - widths[0] - len(prefixes[0+is_last]), line[0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], - ' (%s)' % ', '.join(it.chain( - x[1], ['cycle detected'])) - if i == len(widths)-1 and name in seen - else ' (%s)' % ', '.join(x[1]) if x[1] - else '') - for i, x in enumerate(line[1:], 1)))) - - # found a cycle? - if name in seen: - continue - - # recurse? - if depth_ > 1: - recurse( - r.children, - depth_-1, - seen | {name}, - (prefixes[2+is_last] + "|-> ", - prefixes[2+is_last] + "'-> ", - prefixes[2+is_last] + "| ", - prefixes[2+is_last] + " ")) - - # the top layer is a bit of a special case - for name, line in zip(names, lines[1:-1]): - print('%-*s %s' % ( - widths[0], line[0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') - for i, x in enumerate(line[1:], 1)))) - - if name in table and depth > 1: - recurse( - table[name].children, - depth-1, - {name}, - ("|-> ", - "'-> ", - "| ", - " ")) - - print('%-*s %s' % ( - widths[0], lines[-1][0][0], - ' '.join('%*s%-*s' % ( - widths[i], x[0], - notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') - for i, x in enumerate(lines[-1][1:], 1)))) + # print our table + for line in lines: + print('%-*s %s' % ( + widths[0], line[0][0], + ' '.join('%*s%-*s' % ( + widths[i], x[0], + notes[i], ' (%s)' % ', '.join(x[1]) if x[1] else '') + for i, x in enumerate(line[1:], 1)))) def main(ci_paths, diff --git a/scripts/structs.py b/scripts/structs.py index eec1f49f..3d2a9e7f 100755 --- a/scripts/structs.py +++ b/scripts/structs.py @@ -379,6 +379,7 @@ def table(Result, results, diff_results=None, *, header.append('d'+k) lines.append(header) + # entry helper def table_entry(name, r, diff_r=None): entry = [name] if diff_results is None: diff --git a/scripts/summary.py b/scripts/summary.py index 4e70fe36..fd48f7eb 100755 --- a/scripts/summary.py +++ b/scripts/summary.py @@ -510,6 +510,7 @@ def table(Result, results, diff_results=None, *, header.append('d'+k) lines.append(header) + # entry helper def table_entry(name, r, diff_r=None): entry = [name] if diff_results is None: