Skip to content

Exporters

Base infrastructure

ExportRecord, ExportMetadata, and WriteSpec are the data structures that flow through the export pipeline. ExportMetadata accumulates records for every EXPORT call in a script run; WriteSpec captures per-file encoding and path configuration. These are used internally by all format writers — you only need to understand them if you are writing a new exporter. See Adding Exporters for a step-by-step guide.

Base export infrastructure — metadata tracking and write specifications.

Provides:

  • :class:ExportRecord — records details of a single export operation (query name, output file, optional zip file, description, script location, database info).
  • :class:ExportMetadata — collection of :class:ExportRecord objects; can write itself as a JSON metadata file.
  • :class:WriteSpec — specification for a deferred write operation (message text, file path, encoding) used by halt/cancel hooks.

__all__ = ['ExportMetadata', 'ExportRecord', 'WriteSpec'] module-attribute

ExportMetadata()

Collection of :class:ExportRecord objects; can write itself as JSON.

Accumulates export records during a script run and provides them to the EXPORT METADATA metacommand for serialisation.

Initialise an empty record collection.

colhdrs = ['query', 'filename', 'zipfilename', 'file_path', 'description', 'script', 'script_path', 'script_line', 'script_date', 'database', 'server', 'username'] class-attribute instance-attribute

recordlist = [] instance-attribute

add(exp_record)

Append an export record to the collection.

get()

Return column headers and all not-yet-exported records, marking them exported.

get_all()

Return column headers and every record regardless of prior export state.

ExportRecord(queryname, outfile, zipfile=None, description=None)

Records the details of a single EXPORT operation for metadata tracking.

Captures the query name, output file path, optional zip file, user description, originating script location, and database connection info.

Record export details for the given query name and output file.

exported = False instance-attribute

record = [queryname, fname, zfname, fpath, description, sname, spath, lno, sdt, dbn, svr, usr] instance-attribute

WriteSpec(message, dest=None, tee=None, repeatable=False)

Specification for a deferred WRITE operation used by halt/cancel hooks.

Stores a message, optional destination file, tee flag, and repeatability setting. Resolved and executed later by the hook machinery.

msg = message instance-attribute

outfile = dest instance-attribute

repeatable = bool(repeatable) instance-attribute

tee = bool(tee) instance-attribute

written = False instance-attribute

__repr__()

write()

Execute the deferred write, expanding substitution variables first.

Exporter Protocol definitions for execsql.

Defines two @runtime_checkable Protocols that describe the two main exporter calling conventions used throughout the exporters package:

  • :class:QueryExporter — functions that accept a SQL SELECT statement and a database connection, execute the query, and write the results.
  • :class:RowsetExporter — functions that accept pre-fetched column headers and rows and write them to an output destination.

These Protocols capture the most common parameter signature. Several concrete exporters have additional keyword arguments (tablename, sheetname, template_file, write_types, and_val, etc.) that extend the base contract. Such functions remain structurally compatible: they satisfy the Protocol when called with the base arguments, and the extra parameters have defaults or are supplied by the dispatch layer.

.. note::

The io_export.py dispatch chain is not refactored here. These Protocols exist as a documentation and static-type-checking layer that formalises the implicit interface already present in the codebase.

__all__ = ['QueryExporter', 'RowsetExporter'] module-attribute

QueryExporter

Bases: Protocol

Protocol for exporters that execute a query and write output.

The canonical signature is::

def __call__(
    self,
    select_stmt: str,
    db: Any,
    outfile: str,
    append: bool = False,
    desc: str | None = None,
    zipfile: str | None = None,
) -> None: ...

Conforming functions

  • write_query_to_json
  • write_query_to_html
  • write_query_to_cgi_html
  • write_query_to_latex
  • write_query_to_values
  • prettyprint_query (adds and_val)

Functions with extended signatures

  • write_query_to_xml (adds tablename)
  • write_query_to_json_ts (adds write_types)
  • write_query_to_ods (adds sheetname, no zipfile)
  • write_query_to_hdf5 (adds table_name, no zipfile)
  • write_query_to_duckdb (adds tablename, no desc/zipfile)
  • write_query_to_sqlite (adds tablename, no desc/zipfile)
  • report_query (adds template_file)
  • write_queries_to_ods (table_list instead of select_stmt)

__call__(select_stmt, db, outfile, append=False, desc=None, zipfile=None)

RowsetExporter

Bases: Protocol

Protocol for exporters that accept pre-fetched headers and rows.

The canonical signature is::

def __call__(
    self,
    outfile: str,
    hdrs: list[str],
    rows: Any,
    append: bool = False,
    desc: str | None = None,
    zipfile: str | None = None,
) -> None: ...

Conforming functions

  • export_values

Functions with extended signatures

  • export_html (adds querytext)
  • export_cgi_html (adds querytext)
  • export_latex (adds querytext)
  • export_ods (adds querytext, sheetname, no zipfile)
  • prettyprint_rowset (uses colhdrs/output_dest, adds and_val)
  • export_duckdb (adds tablename, no desc/zipfile)
  • export_sqlite (adds tablename, no desc/zipfile)
  • write_query_to_feather (minimal: outfile, headers, rows only)
  • write_query_to_parquet (minimal: outfile, headers, rows only)
  • write_query_raw (uses rowsource + db_encoding)
  • write_query_b64 (uses rowsource only)
  • write_delimited_file (uses filefmt, column_headers, rowsource, file_encoding)

__call__(outfile, hdrs, rows, append=False, desc=None, zipfile=None)

Format writers

Each module below implements one or more output formats. Every writer follows the same signature: receive a SQL select_stmt string and a Database instance, stream rows via db.select_rowsource(), and write to a file or zip. See Adding Exporters to add a new format.

__all__ = ['LineDelimiter', 'CsvFile', 'CsvWriter', 'DelimitedWriter', 'write_delimited_file'] module-attribute

CsvFile(csvfname, file_encoding, junk_header_lines=0)

Bases: EncodedFile

Full delimited-file reader/writer with automatic format diagnosis.

Supports custom delimiters, quoting, encoding, junk-header skipping, column-type inference, and ZIP output via :class:CsvWriter.

Open a CSV file path for later reading or writing.

blank_cols = [] instance-attribute

csvfname = csvfname instance-attribute

delimiter = None instance-attribute

escapechar = None instance-attribute

junk_header_lines = junk_header_lines instance-attribute

lineformat_set = False instance-attribute

parse_errors = [] instance-attribute

quotechar = None instance-attribute

table_data = None instance-attribute

CsvLine(line_text)

Represent a single CSV line for delimiter diagnosis and parsing.

Store the raw line text and prepare delimiter-count storage.

delim_counts = {} instance-attribute

escchar = '\\' class-attribute instance-attribute

item_errors = [] instance-attribute

text = line_text instance-attribute

__str__()

count_delim(delim)

Count occurrences of the given delimiter in the line text.

delim_count(delim)

Return the previously counted occurrence total for the given delimiter.

items(delim, qchar)

Parse the line into a list of items, splitting on unquoted delimiters.

record_format_error(pos_no, errmsg)

Append a parse error message annotated with its character position.

well_quoted_line(delim, qchar)

Return a tuple of (all-well-quoted, quote-usage-count, uses-escape-char).

__repr__()

column_headers()

Return the first row of the file as a list of column header strings.

create_table(database_type, schemaname, tablename, pretty=False)

Generate a CREATE TABLE SQL statement for this file's inferred schema.

data_table_def()

Return a :class:DataTable describing the file's columns and types.

diagnose_delim(linestream, possible_delimiters=None, possible_quotechars=None)

Analyse a line stream and return the detected (delimiter, quote char, escape char) tuple.

evaluate_column_types()

Scan the file and populate table_data with inferred column types.

evaluate_line_format()

Scan the file to auto-detect the delimiter, quote character, and escape character.

lineformat(delimiter, quotechar, escapechar)

Explicitly set the delimiter, quote character, and escape character.

openclean(mode)

Return an opened file object with the configured number of junk header lines skipped.

read_and_parse_line(f)

Read and parse one line from an open file, returning a list of field values.

reader()

Yield parsed rows from the file as lists of field values.

writer(append=False)

Return a :class:CsvWriter configured with this file's format settings.

CsvWriter(filename, file_encoding, delim, quote, escchar, append=False)

Opens a named file (or stdout) and exposes a row-oriented delimited-text writing API.

Open a file and prepare a delimited writer with the given format settings.

dwriter = DelimitedWriter(self.output, delim, quote, escchar) instance-attribute

output = sys.stdout instance-attribute

close()

Close the underlying output file.

write(text_str)

Write a raw string to the output file.

writerow(datarow)

Format and write a single data row.

writerows(datarows)

Format and write each row in an iterable.

DelimitedWriter(outfile, delim, quote, escchar)

Low-level writer that formats data rows as delimited text and sends them to an open file object.

Wrap an open file object with a :class:LineDelimiter for row-by-row writing.

line_delimiter = LineDelimiter(delim, quote, escchar) instance-attribute

outfile = outfile instance-attribute

write(text_str)

Write a raw string directly to the underlying file object.

writerow(datarow)

Format one data row as delimited text and write it to the file.

writerows(datarows)

Write each row in an iterable of data rows to the file.

LineDelimiter(delim, quote, escchar)

Encapsulates delimiter, quote character, and escape rules for a single line format.

Initialise the line-format constants for a given delimiter/quote pair.

delimiter = delim instance-attribute

joinchar = delim if delim else '' instance-attribute

quote_all_text = _state.conf.quote_all_text if _state.conf else False instance-attribute

quotechar = quote instance-attribute

quotedquote = escchar + quote instance-attribute

delimited(datarow, add_newline=True)

Format a sequence of values as a single delimited text line.

write_delimited_file(outfile, filefmt, column_headers, rowsource, file_encoding='utf8', append=False, zipfile=None)

Write a query result set to a CSV, TSV, or other delimited text file.

__all__ = ['write_query_to_json', 'write_query_to_json_ts'] module-attribute

write_query_to_json(select_stmt, db, outfile, append=False, desc=None, zipfile=None)

Execute a SELECT and write the result set as a JSON array of objects.

write_query_to_json_ts(select_stmt, db, outfile, append=False, write_types=True, desc=None, zipfile=None)

Execute a SELECT and write the result set as a JSON object with a top-level field-type schema.

__all__ = ['write_query_to_xml'] module-attribute

write_query_to_xml(select_stmt, tablename, db, outfile, append=False, desc=None, zipfile=None)

Execute a SELECT and write the result set as a well-formed XML document.

__all__ = ['export_html', 'export_cgi_html', 'write_query_to_html', 'write_query_to_cgi_html'] module-attribute

export_cgi_html(outfile, hdrs, rows, append=False, querytext=None, desc=None, zipfile=None)

Write a CGI-style HTML fragment (Content-Type header + table) to a file or ZIP archive.

export_html(outfile, hdrs, rows, append=False, querytext=None, desc=None, zipfile=None)

Write a complete HTML document containing a data table to a file or ZIP archive.

write_query_to_cgi_html(select_stmt, db, outfile, append=False, desc=None, zipfile=None)

Execute a SELECT and write the result set as a CGI-style HTML fragment.

write_query_to_html(select_stmt, db, outfile, append=False, desc=None, zipfile=None)

Execute a SELECT and write the result set as a standalone HTML document.

__all__ = ['prettyprint_query', 'prettyprint_rowset'] module-attribute

prettyprint_query(select_stmt, db, outfile, append=False, and_val='', desc=None, zipfile=None)

Execute a SELECT and write the result set as a column-aligned text table.

prettyprint_rowset(colhdrs, rows, output_dest, append=False, and_val='', desc=None, zipfile=None)

Format a pre-fetched result set as a fixed-width human-readable text table and write it.

__all__ = ['write_query_raw', 'write_query_b64'] module-attribute

write_query_b64(outfile, rowsource, append=False, zipfile=None)

Decode base64 column data from a row source and write the raw bytes to a file or ZIP archive.

write_query_raw(outfile, rowsource, db_encoding, append=False, zipfile=None)

Write raw binary column data from a row source directly to a file or ZIP archive.

__all__ = ['export_values', 'write_query_to_values'] module-attribute

export_values(outfile, hdrs, rows, append=False, desc=None, zipfile=None)

Write pre-fetched rows as SQL INSERT … VALUES statements to a file or ZIP archive.

write_query_to_values(select_stmt, db, outfile, append=False, desc=None, zipfile=None)

Execute a SELECT and write the result set as SQL INSERT … VALUES statements.

__all__ = ['StrTemplateReport', 'JinjaTemplateReport', 'report_query'] module-attribute

JinjaTemplateReport(template_file)

Generates a report by rendering a Jinja2 template against the full data table.

Load and compile the Jinja2 template from the given file path.

infname = template_file instance-attribute

template = SandboxedEnvironment().from_string(fh.read()) instance-attribute

__repr__()

write_report(headers, data_dict_rows, output_dest, append=False, zipfile=None)

Render the Jinja2 template with headers and datatable context and write the output.

StrTemplateReport(template_file)

Generates a report by applying Python's :class:string.Template to each row of a data table.

Load and compile the template from the given file path.

infname = template_file instance-attribute

template = string.Template(fh.read()) instance-attribute

__repr__()

write_report(headers, data_dict_rows, output_dest, append=False, zipfile=None)

Render the template for each row in data_dict_rows and write the output.

report_query(select_stmt, db, outfile, template_file, append=False, zipfile=None)

Execute a SELECT and render the result set through a str-template or Jinja2 template file.

__all__ = ['export_latex', 'write_query_to_latex'] module-attribute

export_latex(outfile, hdrs, rows, append=False, querytext=None, desc=None, zipfile=None)

Write pre-fetched rows as a LaTeX tabular environment to a file or ZIP archive.

write_query_to_latex(select_stmt, db, outfile, append=False, desc=None, zipfile=None)

Execute a SELECT and write the result set as a LaTeX tabular table.

__all__ = ['write_query_to_markdown'] module-attribute

write_query_to_markdown(select_stmt, db, outfile, append=False, desc=None, zipfile=None)

Execute select_stmt and write the result set as a GFM pipe table.

Writes a GitHub-Flavored Markdown pipe table to outfile (or into zipfile when provided). Column widths are derived from the widest value in each column (including the header), so the table renders legibly in plain-text editors as well as in Markdown renderers.

Parameters:

Name Type Description Default
select_stmt str

SQL SELECT statement to execute.

required
db Any

Database connection object exposing select_rowsource().

required
outfile str

Destination file path, or "stdout" for console output.

required
append bool

When True open the file in append mode. A blank line is written before the table so consecutive appended tables are visually separated.

False
desc str | None

Optional human-readable description. When provided it is written as an HTML comment (<!-- desc -->), which is valid Markdown and invisible in rendered output.

None
zipfile str | None

When set, write into this ZIP archive instead of a plain file. outfile becomes the entry name inside the archive.

None

__all__ = ['OdsFile', 'export_ods', 'write_query_to_ods', 'write_queries_to_ods'] module-attribute

OdsFile()

Wrapper around the odfpy library for reading and writing OpenDocument Spreadsheet files.

Import odfpy and initialise the workbook state.

cell_style_names = [] instance-attribute

filename = None instance-attribute

wbk = None instance-attribute

__repr__()

add_row_to_sheet(datarow, of_table, header=False)

Append a data row to an ODS table, applying header or body cell styles as appropriate.

add_sheet(of_table)

Attach a prepared sheet object to the workbook's spreadsheet element.

close()

Release the workbook reference without saving.

define_body_style()

Register the body cell style in the workbook if not already defined.

define_header_style()

Register the header cell style (bottom-bordered) in the workbook if not already defined.

define_iso_date_style()

Register an ISO-8601 date number style in the workbook if not already defined.

define_iso_datetime_style()

Register an ISO-8601 datetime number style in the workbook if not already defined.

new_sheet(sheetname)

Create and return a detached sheet object that can later be added to the workbook.

open(filename)

Open an existing ODS file or create a new one at the given path.

save_close()

Serialise the workbook to disk and release all resources.

sheet_data(sheetname, junk_header_rows=0)

Return all row data from the named sheet, optionally skipping leading junk rows.

sheet_named(sheetname)

Return the sheet matching a name or 1-based integer index, or None if not found.

sheetnames()

Return a list of worksheet names in the open workbook.

export_ods(outfile, hdrs, rows, append=False, querytext=None, sheetname=None, desc=None)

Write a single-sheet ODS file from pre-fetched column headers and rows.

write_queries_to_ods(table_list, db, outfile, append=False, tee=False, desc=None)

Write multiple tables/queries to separate sheets in a single ODS workbook.

write_query_to_ods(select_stmt, db, outfile, append=False, sheetname=None, desc=None)

Execute a SELECT and write the result set as a single-sheet ODS spreadsheet.

__all__ = ['XlsFile', 'XlsxFile'] module-attribute

XlsFile()

Read-only wrapper around xlrd for importing legacy .xls spreadsheets.

Import xlrd and initialise file state; raises a fatal error if xlrd is absent.

datemode = 0 instance-attribute

encoding = None instance-attribute

errlog = self.XlsLog() instance-attribute

filename = None instance-attribute

wbk = None instance-attribute

XlsLog()

Capture xlrd warning messages as a list of strings.

Initialise an empty message list.

log_msgs = [] instance-attribute

write(msg)

Append a log message to the internal list.

__repr__()

open(filename, encoding=None, read_only=False)

Open an existing .xls file for reading; raises XlsFileError if absent.

sheet_data(sheetname, junk_header_rows=0)

Return all row data from the named sheet, optionally skipping leading junk rows.

sheet_named(sheetname)

Return the sheet matching a name or 1-based integer, raising XlsFileError if absent.

sheetnames()

Return the list of sheet objects in the open workbook.

XlsxFile()

Read/write wrapper around openpyxl for .xlsx spreadsheets.

Import openpyxl and initialise file state; raises a fatal error if openpyxl is absent.

encoding = None instance-attribute

errlog = self.XlsxLog() instance-attribute

filename = None instance-attribute

read_only = False instance-attribute

wbk = None instance-attribute

XlsxLog()

Capture openpyxl warning messages as a list of strings.

Initialise an empty message list.

log_msgs = [] instance-attribute

write(msg)

Append a log message to the internal list.

__repr__()

close()

Close the open workbook and reset all state attributes.

open(filename, encoding=None, read_only=False)

Open an existing .xlsx file for reading; raises XlsxFileError if absent.

sheet_data(sheetname, junk_header_rows=0)

Return all row data from the named sheet, optionally skipping leading junk rows.

sheet_named(sheetname)

Return the sheet matching a name or 1-based integer index.

sheetnames()

Return the list of worksheet names in the open workbook.

__all__ = ['write_query_to_xlsx', 'write_queries_to_xlsx'] module-attribute

write_queries_to_xlsx(table_list, db, outfile, append=False, tee=False, desc=None)

Write multiple tables/queries to separate worksheets in a single XLSX workbook.

Parameters:

Name Type Description Default
table_list str

Comma-separated list of table names (optionally schema-qualified).

required
db Any

An execsql database adapter with a select_rowsource() method.

required
outfile str

Destination .xlsx file path.

required
append bool

If True and outfile exists, add new sheets to the existing workbook rather than replacing it.

False
tee bool

If True, also pretty-print each query result to stdout.

False
desc str | None

Optional description(s). A single string is applied to every sheet; a comma-separated string with the same count as table_list assigns individual descriptions per sheet.

None

write_query_to_xlsx(select_stmt, db, outfile, append=False, desc=None, sheetname=None)

Execute select_stmt and write the result to a single worksheet in an XLSX file.

Parameters:

Name Type Description Default
select_stmt str

SQL SELECT statement to execute.

required
db Any

An execsql database adapter with a select_rowsource() method.

required
outfile str

Destination .xlsx file path.

required
append bool

If True and outfile exists, add a new sheet to the existing workbook. If False, overwrite any existing file.

False
desc str | None

Optional human-readable description stored in the inventory sheet.

None
sheetname str | None

Name for the new worksheet. Defaults to "Sheet1" (or "Sheet2", "Sheet3", etc. when appending to avoid name collisions).

None

__all__ = ['write_query_to_yaml'] module-attribute

write_query_to_yaml(select_stmt, db, outfile, append=False, desc=None, zipfile=None)

Execute select_stmt and write the result set to outfile as YAML.

The output is a YAML sequence of mappings — one mapping per row with column headers as keys. Python types are preserved: integers stay integers, floats stay floats, None becomes null.

Parameters:

Name Type Description Default
select_stmt str

SQL SELECT statement to execute.

required
db Any

Database connection object exposing select_rowsource().

required
outfile str

Destination file path, or "stdout".

required
append bool

When True the YAML sequence is appended to an existing file. Note that concatenating two bare YAML sequences in one file produces a multi-document stream; callers are responsible for ensuring the resulting file is valid for their use-case.

False
desc str | None

Optional description string. Ignored in plain YAML output (YAML does not have a standard metadata header), but accepted for API consistency with other exporters.

None
zipfile str | None

When provided, write outfile as a member of this zip archive instead of writing to the filesystem directly.

None

__all__ = ['write_query_to_feather', 'write_query_to_hdf5'] module-attribute

write_query_to_feather(outfile, headers, rows)

Write a row source as an Apache Arrow Feather v2 file using polars.

write_query_to_hdf5(table_name, select_stmt, db, outfile, append=False, desc=None)

Execute a SELECT and write the result set to an HDF5 file using the tables library.

__all__ = ['write_query_to_parquet'] module-attribute

write_query_to_parquet(outfile, headers, rows)

Write a row source as an Apache Parquet file using polars.

__all__ = ['export_sqlite', 'write_query_to_sqlite'] module-attribute

export_sqlite(outfile, hdrs, rows, append, tablename)

Write pre-fetched rows to a table in an SQLite database file, creating it if necessary.

write_query_to_sqlite(select_stmt, db, outfile, append, tablename)

Execute a SELECT and write the result set to a named table in an SQLite database.

__all__ = ['export_duckdb', 'write_query_to_duckdb'] module-attribute

export_duckdb(outfile, hdrs, rows, append, tablename)

Write pre-fetched rows to a named table in a DuckDB database file.

write_query_to_duckdb(select_stmt, db, outfile, append, tablename)

Execute a SELECT and write the result set to a named table in a DuckDB database.

__all__ = ['WriteableZipfile', 'ZipWriter'] module-attribute

WriteableZipfile(zipfile_name, append=False)

Thin ZipFile wrapper that accepts chunked string writes via an internal buffer.

Open (or create) a ZIP archive and allocate a write buffer.

buf = memoryview(bytearray(self.bufsize)) instance-attribute

buflen = 0 instance-attribute

bufsize = conf.zip_buffer_mb * 1024 * 1000 instance-attribute

current_handle = None instance-attribute

zf = zipfile.ZipFile(zipfile_name, mode=zmode, compression=comp, compresslevel=9) instance-attribute

__del__()

__enter__()

__exit__(exc_type, exc_val, exc_tb)

close()

Close the open member (flushing the buffer) and finalise the ZIP archive.

close_member()

Flush the buffer and close the currently open member file handle.

member_file(member_filename)

Create a new member entry in the archive and open it for writing.

write(str_data)

Buffer a UTF-8-encoded string for writing to the currently open member.

zip_buffer()

Flush any buffered bytes to the currently open zip member file.

ZipWriter(zip_fname, member_fname, append=False)

High-level write-only interface used by EXPORT metacommands to stream output into a ZIP archive.

Open the archive at zip_fname and begin a new member file named member_fname.

member = self.zwriter.member_file(member_fname) instance-attribute

member_fname = member_fname instance-attribute

zip_fname = zip_fname instance-attribute

zwriter = WriteableZipfile(self.zip_fname, append) instance-attribute

__enter__()

__exit__(exc_type, exc_val, exc_tb)

close()

Close the zip member and finalise the archive.

write(str_data)

Write a string to the current zip member.