sqlite-utils/sqlite_utils/export.py

48 lines
1.6 KiB
Python
Raw Normal View History

2026-09-13 12:11:43 -05:00
import csv
2026-09-14 12:11:00 -05:00
import itertools
from collections.abc import Mapping
2026-09-14 07:04:12 -05:00
from typing import Any, Sequence, TextIO, Type, Union
2026-09-13 12:11:43 -05:00
2026-09-14 07:04:12 -05:00
CsvDialect = Union[str, csv.Dialect, Type[csv.Dialect]]
2026-09-13 12:11:43 -05:00
def rows_to_csv_file(
cursor: Any,
file: TextIO,
*,
header: bool = True,
dialect: CsvDialect = "excel",
**writer_kwargs: Any,
) -> None:
"""Write the rows from a DB-API cursor to a CSV-compatible text stream.
``cursor`` should be a DB-API cursor whose ``description`` attribute
contains the result column metadata. Pass ``dialect="excel-tab"`` to
produce TSV output, or provide any dialect accepted by ``csv.writer``.
Additional keyword arguments are forwarded to ``csv.writer``.
"""
description: Union[Sequence[Sequence[Any]], None] = cursor.description
if description is None:
raise ValueError("Cursor does not have result columns")
columns = [column[0] for column in description]
2026-09-14 12:11:00 -05:00
row_iterator = iter(cursor)
sentinel = object()
first_row = next(row_iterator, sentinel)
if isinstance(first_row, Mapping) and len(set(columns)) != len(columns):
raise ValueError("Mapping rows cannot represent duplicate column names")
2026-09-13 12:11:43 -05:00
writer = csv.writer(file, dialect=dialect, **writer_kwargs)
if header:
writer.writerow(columns)
2026-09-14 12:11:00 -05:00
def normalize_row(row):
if isinstance(row, Mapping):
return [row[column] for column in columns]
return row
2026-09-14 12:11:00 -05:00
if first_row is not sentinel:
writer.writerows(
normalize_row(row) for row in itertools.chain((first_row,), row_iterator)
)