How does one implement report breaks in general and in Python specifically? I'm referring to functionality that allows for inserting a title header before the record where some field changes, or a footer with totals after the change. Like in MS Access back in the day. Currently using an iterable object and lambdas to check for breaks like this: from typing import Literal, Callable class BreakResult(): def __init__(self, name, state, slice_start, data=[]): self.name = name self.state = state self.slice_start = slice_start def __bool__(self): # Return True if the list is non-empty return self.state # Define behavior when used in an integer context def __int__(self): return self.slice_start def __str__(self): return self.name if self.state else "" class BreakForIterable(): def __init__(self, name, break_lambda:Callable, where:Literal['after', 'before']='after'): self.name = name self.break_lambda = break_lambda self.where = where self._slice_start = 0 self._state = False @property def slice_start(self): pass def __call__(self, i, data): # returns true, slice_start or None, slice_end or none if self.where == "before": # return true if: # - record number is 0 # - lambda returns different values for current record and the previous one # and set the slice start to the current record if i == 0: slice_start = self._slice_start self._slice_start = i return BreakResult(self.name, True, slice_start) elif self.break_lambda(data) != self.break_lambda(data[i-1]): slice_start = self._slice_start self._slice_start = i return BreakResult(self.name, True, slice_start) else: return BreakResult(self.name, False, None) elif self.where == "after": # return true if: # - record number is equal to the last # - lambda returns different values for current record and the next one # and set the slice start to the current record if i == 0: return BreakResult(self.name, False, None) elif i == len(data) - 1: slice_start = self._slice_start self._slice_start = i + 1 return BreakResult(self.name, True, slice_start) elif self.break_lambda(data) != self.break_lambda(data[i+1]): slice_start = self._slice_start self._slice_start = i + 1 return BreakResult(self.name, True, slice_start) else: return BreakResult(self.name, False, None) return BreakResult(self.name, False, None) class IterableWithBreaks(): def __init__(self, data): self.data = data # this is the data list self.breaks = [] # this is the list of breaks. Order is important def add_break(self, name, break_lambda, where="after"): self.breaks.append(BreakForIterable(name, break_lambda, where)) return self def check_breaks(self, i, where:Literal['after', 'before']): rl = [] for b in [ b for b in self.breaks if b.where == where]: r = b(i, self.data) rl.append(r) return [ r for r in rl if r ] def __iter__(self): self._index = 0 # Initialize the index for iteration return self def __next__(self): if self._index < len(self.data): result = self.data[self._index] self._index += 1 return result else: raise StopIteration # End of the iteration def __getitem__(self, index): return self.data[index] # Define behavior for setting elements def __setitem__(self, index, value): self.data[index] = value # Define behavior for deleting elements def __delitem__(self, index): del self.data[index] but this smells to me like a naive approach - especially if I were to try adding totals to a header. Is there a better approach? Or are there libraries that do that better? Anything from reporting tools (open source) to Python libraries (better, as I'd be able to better control graphics) would work. Continue reading...