From 98279a0b36290adfc74cadd240fcc6e423c6b117 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 22 Jan 2026 23:05:47 -0600 Subject: [PATCH] scripts: csv.py: Simplified --prefix, moved to collect_csv The current... attempt at an approach was broken and becoming horribly unmaintainable. Two issues found without even looking: 1. Field inference didn't understand prefixes, leading to duplicate by/field fields when attempting to infer by fields with --prefix. 2. Sort wasn't working for some reason, probably because they behavior of sort, defines, etc are really weird since they apply to both by fields and field fields. I considered just dropping support for --prefix completely, this really isn't worth the time, but instead found a simple solution of moving prefix handling to one of the first steps in collect_csv. This has the downside of creating conflicts when a prefixed/non-prefixed field has the same name, but I don't care. --prefix is a niche flag that shouldn't mess with the rest of the code like this, and none of the other scripts really handle field conflicts correctly anyways. --- scripts/csv.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/scripts/csv.py b/scripts/csv.py index 25d6f06b..8ebc5621 100755 --- a/scripts/csv.py +++ b/scripts/csv.py @@ -1469,7 +1469,19 @@ def collect_csv(csv_paths, *, depth=1, children=None, notes=None, + prefix=None, **_): + # useful function for stripping the optional prefix + # + # what, it's not like any of the other scripts avoided prefix + # conflicts, trying to avoid conflicts until after expr eval + # quickly became unmaintainable + def stripprefix(k): + if prefix is not None and k.startswith(prefix): + return k[len(prefix):] + else: + return k + # collect both results and fields from CSV files fields = co.OrderedDict() results = [] @@ -1483,8 +1495,12 @@ def collect_csv(csv_paths, *, if not is_json: reader = csv.DictReader(f, restval='') # collect fields - fields.update((k, True) for k in reader.fieldnames or []) + fields.update((stripprefix(k), True) + for k in reader.fieldnames or []) for r in reader: + # strip prefix early + if prefix is not None: + r = {stripprefix(k): v for k, v in r.items()} # strip and drop empty fields r_ = {k: v.strip() for k, v in r.items() @@ -1501,6 +1517,9 @@ def collect_csv(csv_paths, *, def unjsonify(results, depth_): results_ = [] for r in results: + # strip prefix early + if prefix is not None: + r = {stripprefix(k): v for k, v in r.items()} # collect fields fields.update((k, True) for k in r.keys()) # convert to strings, we'll reparse these later @@ -1544,12 +1563,7 @@ def compile(fields_, results, children=None, hot=None, notes=None, - prefix=None, **_): - # default to no prefix - if prefix is None: - prefix = '' - by = by.copy() fields = fields.copy() @@ -1580,16 +1594,16 @@ def compile(fields_, results, # # it's tempting to also allow enumerate fields here, but this # currently doesn't work when hotifying - if prefix+k not in fields_: + if k not in fields_: print("error: no field %r?" % k, file=sys.stderr) sys.exit(2) for t in [CsvInt, CsvFloat, CsvFrac]: for r in results: - if prefix+k in r and r[prefix+k].strip(): + if k in r and r[k].strip(): try: - t(r[prefix+k]) + t(r[k]) except ValueError: break else: @@ -1615,9 +1629,9 @@ def compile(fields_, results, # create result class def __new__(cls, _state=None, **r): r_ = r.copy() - # evaluate types, strip prefix + # evaluate types for k, t in types__.items(): - r_[k] = t(r[prefix+k]) if prefix+k in r else t() + r_[k] = t(r[k]) if k in r else t() r__ = r_.copy() # evaluate exprs @@ -2476,8 +2490,7 @@ def main(csv_paths, *, sort=sort, children=children, hot=hot, - notes=notes, - **args) + notes=notes) # homogenize results = homogenize(Result, results,