Add parse_statements() for multi-statement / balance-only files (#107)#125
Conversation
A single parse() merges concatenated statements into one Transactions and keeps only the last block's statement-level data (balances), so balance-only multi-block files lose all but the last block. parse_statements() splits the input on :20: boundaries and returns one Transactions per statement, each with its own balances. The default parse() behaviour is unchanged (verified against the full fixture set). Refactors the read/decode into a shared _read() helper; documents reading balances and multi-statement files in the README.
There was a problem hiding this comment.
Code Review
This pull request introduces the parse_statements function to support parsing MT940 files with multiple statement blocks, ensuring each block's statement-level data (such as balances) is preserved individually rather than merged. Documentation and unit tests have been added to support this feature. The feedback suggests improving the statement block validation in mt940/parser.py by replacing the substring check ':20:' not in block with a more robust startswith check to avoid false positives from metadata or headers.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
Adds an opt-in parsing API to correctly handle MT940 inputs that concatenate multiple statement blocks, especially “balance-only” statements with no :61: transactions, while keeping existing mt940.parse() behavior unchanged.
Changes:
- Introduces
mt940.parse_statements()to split input on:20:statement boundaries and return oneTransactionsper block. - Refactors input reading/decoding into a shared
_read()helper used by bothparse()andparse_statements(). - Updates README with balance-reading guidance and multi-statement parsing examples; adds targeted tests for issue #107 behavior and backwards-compatibility.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| README.md | Documents statement-level balances and the new multi-statement parsing API. |
| mt940/parser.py | Refactors read/decode and adds parse_statements() implementation. |
| mt940/init.py | Exports parse_statements as part of the public package API. |
| mt940_tests/test_issues/test_issue107_statements.py | Adds regression tests for multi-block/balance-only parsing and BC behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: edd1eb664b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- parser: use block.strip().startswith(':20:') instead of a substring check so
a header containing ':20:' can't be mistaken for a statement (gemini).
- docs: document that parse_statements splits on every :20: and is mutually
exclusive with transaction_boundary={'transaction_reference_number'} (codex).
- README: make the balance/parse_statements examples self-contained (copilot).
Addresses #107. An MT940 file can concatenate several statement blocks (each starting with
:20:), including balance-only blocks with no:61:transactions.transactions.data. This was mostly a docs gap (now documented).scope=Transactions→ the single globalself.data, so only the last block survived.Change (opt-in, fully backwards-compatible)
mt940.parse_statements(src, ...) -> list[Transactions]: splits the input on:20:boundaries and parses each block into its ownTransactions, each with its own balances + transactions.mt940.parse()is unchanged (still merges) — verified against the full fixture set with the parse-diff harness: zero change to existing output. (abnamro.staalready has 2:20:blocks and still parses to one merged collection by default;parse_statements()returns 2.)_read()helper (no behaviour change toparse()).Verification
toxgreen: py310–313, lint, pyright, mypy, pyrefly, docs, coverage 100%, codespell, repo-review.Fixes #107