| | | -> LoaderResult | +----------+---------------+ +----------+---------------+ | | | extension dispatch (.xlsx) | v v +------+-------+ +------+-------+ | pain001 | | openpyxl | | registry | | read_only | +--------------+ +--------------+
pain001 v0.0.54 ships a formal plugin contract; this package
exposes one Python class that satisfies it. Wired into pain001 via
a single line in this package's `pyproject.toml`:
```toml
[project.entry-points."pain001.loaders"]
xlsx = "pain001_loader_xlsx.loader:XlsxLoader"
That is all the integration there is. pain001 discovers the entry
point at process start via importlib.metadata.entry_points and
dispatches by extension. There is no global state, no central
registry to update, nothing to subclass.
Layout
The first sheet of the workbook is read. Row 1 is the header (column
names become dict keys); rows 2..N are the data records. Cells are
read with openpyxl's data_only=True so formulas resolve to their
cached last-saved value — what the user sees in Excel is what
pain001 gets.
The IBAN guard, explained
Excel's "General" cell format silently coerces a numeric-looking
string like 0023012345... into the integer 23012345...,
dropping the leading zeros. This is a known data-corruption
mode in SAP / Oracle / Workday exports. To protect against it the
loader refuses any row whose debtor_account_IBAN /
creditor_account_IBAN / charge_account_IBAN cell is typed as a
number, and tells the user to re-type the column as Text:
workbook 'payments.xlsx' column 'debtor_account_IBAN' contains
a numeric value (89370400440532013000) where an IBAN string is
expected. Excel's 'General' cell format silently strips leading
zeros from IBANs; re-type the column as 'Text' (in Excel: select
the column, Format Cells -> Number -> Text) and re-export.
Caught early, the warning saves the user from wiring an IBAN with a missing digit to a bank.
Using the loader from Python
For Lambdas, ETL pipelines, or just inspecting an Excel file's
records before generation, you can use XlsxLoader directly without
going through pain001's dispatch:
from pain001_loader_xlsx import XlsxLoader
loader = XlsxLoader()
result = loader.load("payments.xlsx")
print(result.source_hint) # -> "payments.xlsx"
print(len(result.rows)) # -> 42
print(result.rows[0]["id"]) # -> "MSG-0001"
Streaming variant for batches that don't fit in memory:
for chunk in loader.load_streaming("big-payments.xlsx", chunk_size=1000):
process(chunk.rows)
The runnable version of this snippet (and a couple of others) lives
in examples/.
The pain001 suite
pain001-loader-xlsx is part of a set of independently installable
packages built around the
pain001 library —
pick whichever ones your stack needs:
| Package | Role |
|---|---|
pain001 |
Core library + CLI + FastAPI REST API |
pain001-mcp |
Model Context Protocol server (for AI agents) |
pain001-lsp |
Language Server Protocol server (for editors) |
pain001-loader-xlsx |
Excel loader plugin (this package) |
flowchart LR
A["payments.xlsx"] -->|extension dispatch| B["pain001-loader-xlsx"]
B -->|LoaderResult| C["pain001"]
C -->|render + XSD validate| D["ISO 20022 pain.001 XML"]
When not to use pain001-loader-xlsx
- You can export CSV cleanly. A
.csvround-trip skips an entire transitive dependency tree (openpyxl+ its handful of deps). pain001's built-in CSV loader is preferred when you have the choice. - You need multi-sheet support. The first sheet wins; cross-sheet payment batches need to be consolidated first.
- You need
.xls(legacy binary format). Out of scope. Convert to.xlsxfirst, or use a different loader. - Your data isn't payment-record-shaped. This loader is a thin pain001 input adapter, not a general-purpose Excel reader.
Development
pain001-loader-xlsx uses standard Python tooling — no Poetry, just
pip + pyproject.toml.
git clone https://github.com/sebastienrousseau/pain001-loader-xlsx.git
cd pain001-loader-xlsx
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
Quality gates (kept in lockstep with CI):
| Target | What it runs |
|---|---|
pytest |
Full test suite |
pytest --cov=pain001_loader_xlsx --cov-branch --cov-fail-under=100 |
100% line + branch coverage gate |
interrogate -c pyproject.toml pain001_loader_xlsx |
100% docstring coverage gate |
ruff check pain001_loader_xlsx tests |
Lint |
ruff format --check pain001_loader_xlsx tests |
Format |
mypy pain001_loader_xlsx |
Type check |
Current state (v0.0.54): 12 tests passing, 100% line + branch coverage, ruff + mypy clean, interrogate 100% docstring coverage.
Security
- No filesystem writes. The loader reads from an Excel file path and yields plain dicts; it does not create, modify, or delete files.
- No code execution.
openpyxl'sread_only=Truemode does not evaluate macros (Excel VBA is not executed).data_only=Truereturns the cached last-saved value of formulas — no formula engine runs. - IBAN safety: the loader refuses any row whose IBAN cells are numeric (see Layout), avoiding the "Excel silently dropped a leading zero" data-corruption mode.
- Dependencies are pinned via
pyproject.toml(openpyxl >= 3.1, < 4) and audited by GitHub's Dependabot.
To report a vulnerability, please use GitHub private vulnerability reporting rather than a public issue.
Documentation
- Runnable examples:
examples/ - Release history: CHANGELOG.md
- pain001 plugin contract:
docs/plugins.mdupstream - openpyxl docs: openpyxl.readthedocs.io
Contributing
Contributions are welcome — see the
contributing guide
(or the upstream pain001 contributing guide if a per-repo one has
not landed yet). Thanks to all the
contributors
who have helped build pain001-loader-xlsx.
License
Licensed under the Apache License, Version 2.0.
Built on openpyxl
and the
pain001 plugin
contract.
Any contribution submitted for inclusion shall be licensed as above, without additional terms.
pain001.com · PyPI · GitHub
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pain001_loader_xlsx-0.0.63.tar.gz.
File metadata
- Download URL: pain001_loader_xlsx-0.0.63.tar.gz
- Upload date:
- Size: 36.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
efbbf483487439ba85eeba7e2fb19fb69dac9f1dccb1e85dc60a999d66754965
|
|
| MD5 |
701ec39361c5f6983066272f39ab7fb1
|
|
| BLAKE2b-256 |
f0678d528c7e83e1eacf61679299aa7ee9d87aa46c386efe5d75d68dda6fae03
|
Provenance
The following attestation bundles were made for pain001_loader_xlsx-0.0.63.tar.gz:
Publisher:
release.yml on sebastienrousseau/pain001-loader-xlsx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pain001_loader_xlsx-0.0.63.tar.gz -
Subject digest:
efbbf483487439ba85eeba7e2fb19fb69dac9f1dccb1e85dc60a999d66754965 - Sigstore transparency entry: 2629583864
- Sigstore integration time:
-
Permalink:
sebastienrousseau/pain001-loader-xlsx@ec906ec614751163481649b3d3d2601abe8a1201 -
Branch / Tag:
refs/tags/v0.0.63 - Owner: https://github.com/sebastienrousseau
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ec906ec614751163481649b3d3d2601abe8a1201 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pain001_loader_xlsx-0.0.63-py3-none-any.whl.
File metadata
- Download URL: pain001_loader_xlsx-0.0.63-py3-none-any.whl
- Upload date:
- Size: 18.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
add389ce99787815855b5da2d8f9ea6e98f3a35ac8792f4df662879bfd5bedbd
|
|
| MD5 |
8374eddabf5c9364d8e2cd932ef4a63d
|
|
| BLAKE2b-256 |
8151879c55bb2e0f57057540e0256238d5b6a2556d5e1a77d84dbb99b46d95cd
|
Provenance
The following attestation bundles were made for pain001_loader_xlsx-0.0.63-py3-none-any.whl:
Publisher:
release.yml on sebastienrousseau/pain001-loader-xlsx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pain001_loader_xlsx-0.0.63-py3-none-any.whl -
Subject digest:
add389ce99787815855b5da2d8f9ea6e98f3a35ac8792f4df662879bfd5bedbd - Sigstore transparency entry: 2629583948
- Sigstore integration time:
-
Permalink:
sebastienrousseau/pain001-loader-xlsx@ec906ec614751163481649b3d3d2601abe8a1201 -
Branch / Tag:
refs/tags/v0.0.63 - Owner: https://github.com/sebastienrousseau
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ec906ec614751163481649b3d3d2601abe8a1201 -
Trigger Event:
push
-
Statement type: