bankstatementparser-writer-xlsx: Excel writer for parsed bank statements
An Excel .xlsx writer for data parsed by
bankstatementparser — turn parsed transactions (a pandas
DataFrame, a list of Transaction objects, or a list of plain dicts)
into a polished workbook that accountants, auditors, and reconciliation
macros can open directly.
Latest release: v0.0.15 — single
write_xlsx(data, path, ...)function, 100% line + branch coverage, 100% docstring coverage,mypy --strictclean.
Contents
- Overview
- Install
- Quick start
- Input shapes
- Value coercion
- The summary sheet
- Examples
- When not to use this package
- Development
- Security
- Documentation
- License
- Contributing
- Acknowledgements
Overview
bankstatementparser-writer-xlsx is a small, focused companion to the
bankstatementparser library. It does one thing well: given
already-parsed bank-statement records, write a clean Excel workbook with
a bold header row, one row per transaction, auto-sized columns, and an
optional second Summary sheet.
The package consumes parsed data — it does not read PDFs, CSVs, or
XML itself. Parsing (and the security surface that comes with untrusted
input) lives upstream in the bankstatementparser core.
Install
bankstatementparser-writer-xlsx runs on macOS, Linux, and Windows and
requires Python 3.10+. It pulls in bankstatementparser,
openpyxl, and pandas automatically.
pip install bankstatementparser-writer-xlsx
Quick start
from bankstatementparser import CsvStatementParser
from bankstatementparser_writer_xlsx import write_xlsx
parser = CsvStatementParser("statement.csv")
df = parser.parse() # a pandas DataFrame
write_xlsx(df, "statement.xlsx") # one polished workbook
That's an Excel workbook ready for your accountant. Add a summary sheet in one extra argument:
from bankstatementparser import CsvStatementParser
from bankstatementparser_writer_xlsx import write_xlsx
parser = CsvStatementParser("statement.csv")
df = parser.parse()
write_xlsx(df, "statement.xlsx", summary=parser.get_summary())
Input shapes
write_xlsx(data, path, *, sheet_name="Transactions", summary=None)
accepts three input shapes and normalises each to a flat table:
| Input | Column order |
|---|---|
pandas.DataFrame (from a parser's .parse()) |
the DataFrame's own column order |
list[bankstatementparser.Transaction] |
the stable Transaction field order |
list[dict] |
the union of keys, in first-seen order |
A header row (bold) is written to the sheet_name sheet, followed by
one row per record. Columns are auto-sized to their widest cell, capped
at 60 characters so wide descriptions don't run off-screen (the cell
content itself is never truncated — only the displayed column width).
Empty input is accepted: an empty list writes an empty sheet (no
header), while an empty DataFrame that still carries column labels
writes a header-only sheet.
Value coercion
Spreadsheet cells can't hold arbitrary Python objects, so the writer coerces the rich types the parser emits:
| Python type | Written as |
|---|---|
decimal.Decimal |
float (Excel has no decimal type; floats aggregate natively) |
datetime.date / datetime.datetime |
native Excel date cell (unchanged) |
str, int, float, bool, None |
unchanged |
None / float('nan') (e.g. a missing DataFrame cell) |
blank cell |
| anything else | str(value) |
bool is preserved as a true/false cell (it is not coerced to 0/1),
and a missing list[dict] key writes a blank cell in the same way a
None value does.
Errors
write_xlsx validates its data argument and fails fast with a precise
exception rather than writing a malformed workbook:
| Condition | Raised |
|---|---|
data is neither a DataFrame nor a list/tuple (e.g. a str or int) |
TypeError |
data is a non-empty sequence whose items are neither dict records nor Transaction objects (e.g. [42]) |
ValueError |
from bankstatementparser_writer_xlsx import write_xlsx
try:
write_xlsx("not-a-table", "out.xlsx") # a bare str is not a table
except TypeError as exc:
print(f"rejected: {exc}")
try:
write_xlsx([42], "out.xlsx") # 42 is not a dict/Transaction
except ValueError as exc:
print(f"rejected: {exc}")
The summary sheet
If you pass summary= a mapping (for example a parser's
get_summary() result), the writer adds a second sheet titled
Summary with a bold Key / Value header and one row per item:
from decimal import Decimal
from bankstatementparser_writer_xlsx import write_xlsx
transactions = [
{"date": "2026-06-01", "description": "Salary", "amount": Decimal("3000.00")},
{"date": "2026-06-03", "description": "Coffee Shop", "amount": Decimal("-4.20")},
]
write_xlsx(
transactions,
"out.xlsx",
summary={
"account_id": "DE89370400440532013000",
"transaction_count": 128,
"total_amount": Decimal("12045.67"),
"currency": "EUR",
},
)
Examples
Six runnable examples live in examples/ and are
exercised in CI on every commit. Together they cover every supported
input shape, option, coercion rule, and error path of write_xlsx:
01_write_dataframe.py— write a pandasDataFrameto a single sheet.02_write_transactions.py— write a list ofTransactionobjects in stable field order.03_write_dicts.py— write a list of plaindictrecords (union of keys).04_write_with_summary.py— write a DataFrame plus a secondSummarysheet viasummary=.05_custom_sheet_name.py— rename the transactions sheet withsheet_name=.06_value_coercion_and_errors.py—Decimal/datecoercion,None/NaN/missing keys as blank cells, the 60-character column-width cap, and theTypeError/ValueErrorerror paths.
When not to use this package
- You need a custom sheet layout. The single-sheet (+ optional
Summary) structure is intentionally simple. Compose your own
openpyxlworkbook if you need pivot-ready, multi-sheet layouts. - You need
.xls(legacy binary).openpyxlwrites.xlsxonly; convert downstream if you must. - You need encrypted output. Out of scope; encrypt the produced
.xlsxdownstream with a tool likemsoffcrypto-tool. - You want to read Excel. This package is a writer.
Development
git clone https://github.com/sebastienrousseau/bankstatementparser-writer-xlsx
cd bankstatementparser-writer-xlsx
poetry env use python3.12
poetry install
poetry run pytest # 100% line + branch coverage gate
poetry run ruff check bankstatementparser_writer_xlsx tests
poetry run mypy bankstatementparser_writer_xlsx
poetry run interrogate -c pyproject.toml bankstatementparser_writer_xlsx
Security
bankstatementparser-writer-xlsx consumes already-parsed data, not raw
statement files — the PDF/CSV/XML parsing surface lives upstream in the
bankstatementparser core. Reporting practice, supported
versions, and supply-chain posture are documented in
SECURITY.md.
Documentation
README.md— this fileARCHITECTURE.md— module map and design decisionsCHANGELOG.md— release notesROADMAP.md— what's nextSECURITY.md— disclosure + supported versionsexamples/— runnable scripts, exercised in CI
License
Licensed under the Apache License, Version 2.0. Any contribution submitted for inclusion shall be licensed as above, without additional terms.
Contributing
Contributions are welcome — open an issue or PR on the repository.
Acknowledgements
Built on the bankstatementparser library and
openpyxl.
Metadata
Release files for bankstatementparser-writer-xlsx 0.0.15
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bankstatementparser_writer_xlsx-0.0.15.tar.gz | 13.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bankstatementparser_writer_xlsx-0.0.15-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.7 kB
Release files / bankstatementparser_writer_xlsx-0.0.15.tar.gz
| Download URL | bankstatementparser_writer_xlsx-0.0.15.tar.gz |
|---|---|
| Size | 13.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d32a9cbf53719acc49f562296b411df1451dfdeb2fab4ebd86aa5bffef4710ba
|
|
BLAKE2b-256 checksum How to use checksums |
94357bf1ea7ec209d3bab1574504510c63fb9d446c7645d367257c2da8ce8b9e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.
Transparency logRelease files / bankstatementparser_writer_xlsx-0.0.15-py3-none-any.whl
| Download URL | bankstatementparser_writer_xlsx-0.0.15-py3-none-any.whl |
|---|---|
| Size | 15.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6ee94a61d8f891d317554f2214fe1e206c94948e888b36534e1342448f0a52c7
|
|
BLAKE2b-256 checksum How to use checksums |
60692716e01ebc5352d25f09e1b45141cdbd7c32cebb8bc3d9a8427232f97bf4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.
Transparency log