This probably exist but I couldn’t find it. I wanted to export a bunch of data from a Python/Django application into something a non-coder could understand. The data was not going to be a plain CSV, but a document, with various tables and explanations of what each table is. Because ReStructured Text seems to be the winning format in the Python world I decided to go with that.
Generating the text part was easy and straightforward. The question was how to export tables. I decided to represent tables as lists of dicts and thus, I ended up building this little module:
def dict_to_rst_table(data): field_names, column_widths = _get_fields(data) with StringIO() as output: output.write(_generate_header(field_names, column_widths)) for row in data: output.write(_generate_row(row, field_names, column_widths)) return output.getvalue() def _generate_header(field_names, column_widths): with StringIO() as output: for field_name in field_names: output.write(f"+-{'-' * column_widths[field_name]}-") output.write("+\n") for field_name in field_names: output.write( f"| {field_name} {' ' * (column_widths[field_name] - len(field_name))}" ) output.write("|\n") for field_name in field_names: output.write(f"+={'=' * column_widths[field_name]}=") output.write("+\n") return output.getvalue() def _generate_row(row, field_names, column_widths): with StringIO() as output: for field_name in field_names: output.write( f"| {row[field_name]}{' ' * (column_widths[field_name] - len(str(row[field_name])))} " ) output.write("|\n") for field_name in field_names: output.write(f"+-{'-' * column_widths[field_name]}-") output.write("+\n") return output.getvalue() def _get_fields(data): field_names = [] column_widths = defaultdict(lambda: 0) for row in data: for field_name in row: if field_name not in field_names: field_names.append(field_name) column_widths[field_name] = max( column_widths[field_name], len(field_name), len(str(row[field_name])) ) return field_names, column_widths
It’s straightforward and simple. It currently cannot deal very well with cases in which dicts have different set of columns.
Should this be turned into a reusable library?
I do the same thing for a project that I work on, though in my case, class and method docstrings are rewritten at import so that the Sphinx autodoc module can extract information that is always correct.
I suggest looking at the “.. csv-table::“ directive, which completely eliminates all of the length calculations and hyphen multiplication code.