scripts: Consistent table renderer, cycle detection optional

The fact that our scripts' table renderer was slightly different for
recursive scripts (stack.py, perf.py) and non-recursive scripts
(code.py, structs.py) was a ticking time bomb, one innocent edit away
from breaking half the scripts.

The makes the table renderer consistent across all scripts, allowing for
easy copy-pasting when editing at the cost of some unused code in
scripts.

One hiccup with this though is the difference in cycle detection
behavior between scripts:

- stack.py:

    lfsr_bd_sync
    '-> lfsr_bd_prog
        '-> lfsr_bd_sync  <-- cycle!

- structs.py:

    lfsr_bshrub_t
    '-> u
        '-> bsprout
            '-> u  <-- not a cycle!

To solve this the table renderer now accepts a simple detect_cycles
flag, which can be set per-script.
This commit is contained in:
Christopher Haster
2024-11-27 01:25:55 -06:00
parent 7c8afd26cf
commit e00db216c1
8 changed files with 421 additions and 42 deletions

View File

@ -469,8 +469,9 @@ def table(Result, results, diff_results=None, *,
all=False,
compare=None,
summary=False,
depth=None,
depth=1,
hot=None,
detect_cycles=True,
**_):
all_, all = all, __builtins__.all
@ -504,12 +505,13 @@ def table(Result, results, diff_results=None, *,
for k in it.chain(hot, [None])))
# found a cycle?
if id(r) in seen:
if (detect_cycles
and tuple(getattr(r, k) for k in Result._by) in seen):
return []
return [r._replace(children=[])] + rec_hot(
r.children,
seen | {id(r)})
seen | {tuple(getattr(r, k) for k in Result._by)})
results = [r._replace(children=rec_hot(r.children)) for r in results]
@ -654,7 +656,7 @@ def table(Result, results, diff_results=None, *,
getattr(diff_r, k, None)))))
return entry
# recursive entry helper
# recursive entry helper, only used by some scripts
def recurse(results_, depth_, seen=set(),
prefixes=('', '', '', '')):
# build the children table at each layer
@ -689,19 +691,19 @@ def table(Result, results, diff_results=None, *,
# add prefixes
line[0] = (prefixes[0+is_last] + line[0][0], line[0][1])
# add cycle detection
if id(r) in seen:
if detect_cycles and name in seen:
line[-1] = (line[-1][0], line[-1][1] + ['cycle detected'])
lines.append(line)
# found a cycle?
if id(r) in seen:
if detect_cycles and name in seen:
continue
# recurse?
if depth_ > 1:
recurse(r.children,
depth_-1,
seen | {id(r)},
seen | {name},
(prefixes[2+is_last] + "|-> ",
prefixes[2+is_last] + "'-> ",
prefixes[2+is_last] + "| ",
@ -721,7 +723,7 @@ def table(Result, results, diff_results=None, *,
if name in table and depth > 1:
recurse(table[name].children,
depth-1,
{id(r)},
{name},
("|-> ",
"'-> ",
"| ",
@ -863,6 +865,7 @@ def main(obj_paths, *,
by=by if by is not None else ['struct'],
fields=fields,
sort=sort,
detect_cycles=False,
**args)