From 9a224a1c5209e53ceaa9de6b37b969ef0a75288b Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 29 Jan 2026 22:15:20 -0600 Subject: [PATCH] scripts: csv.py: Fixed incorrect fold type when type changes csv.py's -L/--list-computed was returning some confusing types: $ ./scripts/csv.py /dev/null -fa='float(1)' -L a int sum ^-- huh!? Turns out csv.py's fold typechecking was all broken. Folds can change the type, but only at the invocation: $ ./scripts/csv.py /dev/null -fa='sum(float(1))' -L a int sum $ ./scripts/csv.py /dev/null -fa='avg(int(1))' -L a float avg $ ./scripts/csv.py /dev/null -fa='int(avg(1))' -L a float avg This is maybe defensible for explicit folds, since their evaluation is also lifted, but not so much for things like literals/fields/etc. --- Fixed by allowing None to indicate a generic fold, and allowing types to be lazily figured out in csv.compile. --- scripts/csv.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/scripts/csv.py b/scripts/csv.py index 1b3aa8ee..0cb04ae2 100755 --- a/scripts/csv.py +++ b/scripts/csv.py @@ -554,7 +554,7 @@ class CsvExpr: return CsvInt def fold(self, types={}): - return CsvSum, CsvInt + return CsvSum, None def eval(self, fields={}, state=None): return self.a @@ -567,7 +567,7 @@ class CsvExpr: return CsvFloat def fold(self, types={}): - return CsvSum, CsvFloat + return CsvSum, None def eval(self, fields={}, state=None): return self.a @@ -585,7 +585,7 @@ class CsvExpr: def fold(self, types={}): if self.a not in types: raise CsvExpr.Error("unfoldable field? %s" % self.a) - return CsvSum, types[self.a] + return CsvSum, None def eval(self, fields={}, state=None): if self.a not in fields: @@ -646,7 +646,7 @@ class CsvExpr: """Find the sum of this column or fields""" def fold(self, types={}): if len(self) == 1: - return CsvSum, self.a.type(types) + return CsvSum, None else: return self.a.fold(types) @@ -661,7 +661,7 @@ class CsvExpr: """Find the product of this column or fields""" def fold(self, types={}): if len(self) == 1: - return Prod, self.a.type(types) + return Prod, None else: return self.a.fold(types) @@ -676,7 +676,7 @@ class CsvExpr: """Find the minimum of this column or fields""" def fold(self, types={}): if len(self) == 1: - return CsvMin, self.a.type(types) + return CsvMin, None else: return self.a.fold(types) @@ -691,7 +691,7 @@ class CsvExpr: """Find the maximum of this column or fields""" def fold(self, types={}): if len(self) == 1: - return CsvMax, self.a.type(types) + return CsvMax, None else: return self.a.fold(types) @@ -796,7 +796,7 @@ class CsvExpr: return CsvInt def fold(self, types={}): - return CsvSum, CsvInt + return CsvSum, None def eval(self, fields={}, state=None): if state is None: @@ -1622,7 +1622,8 @@ def compile(fields_, results, folds___ = {k: (CsvSum, t) for k, v in types__.items()} for k, expr in exprs.items(): folds___[k] = expr.fold(types__) - folds___ = {k: (f(), t) for k, (f, t) in folds___.items()} + # instantiate folds and resolve fold types + folds___ = {k: (f(), t or types___[k]) for k, (f, t) in folds___.items()} # create result class def __new__(cls, _state=None, **r):