scripts: csv.py: Added --list-fields for quick field access

One issue I keep running into with csv.py is that it's difficult to get
started with a new/unfamiliar csv file.

csv.py itself doesn't know what to do until you start specifying fields,
but you can't start specifying fields until you know what fields there
are. Add to this the fact that our csv files have so much info shoved in
them that their "human readability" is mostly theoretical.

The --list-fields flag provides a quick solution to this:

  $ ./scripts/csv.py --list-fields test.csv
  i              int
  suite          ?
  case           ?
  SKIP_WARMUP    int
  FILE_SIZE      int
  SEED           int
  ...

csv.py doesn't have much info at this stage, but we can at least include
the best-effort type guessing we use for field exprs.
This commit is contained in:
Christopher Haster
2026-01-23 02:11:12 -06:00
parent 6c458b321c
commit 187f35df61
+59 -4
View File
@@ -1592,9 +1592,6 @@ def compile(fields_, results,
types__ = {}
for k in fields__:
# check if dependency is in original fields
#
# it's tempting to also allow enumerate fields here, but this
# currently doesn't work when hotifying
if k not in fields_:
print("error: no field %r?" % k,
file=sys.stderr)
@@ -2337,6 +2334,58 @@ def write_csv(path, Result, results, *,
separators=(',', ':'))
# some list rules
def list_fields(csv_paths, **args):
# find results
if not args.get('use', None):
# not enough info?
if not csv_paths:
print("error: no *.csv files?",
file=sys.stderr)
sys.exit(1)
# collect info
fields_, results = collect_csv(csv_paths,
**args)
else:
# use is just an alias but takes priority
fields_, results = collect_csv([args['use']],
**args)
# find best type for fields, note this matches compile behavior
types_ = {}
for k in fields_:
try:
for t in [CsvInt, CsvFloat, CsvFrac]:
for r in results:
if k in r and r[k].strip():
try:
t(r[k])
except ValueError:
break
else:
types_[k] = t
break
except AttributeError:
pass
# find widths
w = [0]
for k in fields_:
w[0] = max(w[0], len(k))
for k in fields_:
if k in types_:
t = types_[k].__name__
if t.startswith('Csv'):
t = t[len('Csv'):]
t = t.lower()
else:
t = '?'
print('%-*s %s' % (w[0], k, t))
# entry point
def main(csv_paths, *,
by=None,
fields=None,
@@ -2353,6 +2402,9 @@ def main(csv_paths, *,
# show expr help text?
if args.get('help_exprs'):
return CsvExpr.help()
# list fields?
if args.get('list_fields'):
return list_fields(csv_paths, **args)
if ((by is None or all(hidden for (k, v), hidden in by))
and (fields is None or all(hidden for (k, v), hidden in fields))):
@@ -2406,7 +2458,6 @@ def main(csv_paths, *,
children=children,
notes=notes,
**args)
else:
# use is just an alias but takes priority
fields_, results = collect_csv([args['use']],
@@ -2596,6 +2647,10 @@ if __name__ == "__main__":
'--help-exprs',
action='store_true',
help="Show what field exprs are available.")
parser.add_argument(
'--list-fields',
action='store_true',
help="List fields and inferred types before processing.")
parser.add_argument(
'-q', '--quiet',
action='store_true',