pain001.twins package#
Twins: lossless representations of the same pain.001 message (ADR-0005).
A twin carries everything the XML carries and comes back as the same
XML, element for element. The canonical twin is the ISO 20022
Registration Authority’s JSON convention of 2025
(pain001.twins.iso_json); the records twin
(pain001.twins.records) is the library’s own flat input rows with
the list of what they cannot carry. Faces, which project a message for a
compatible standard with a loss report, are a later layer.
Submodules#
pain001.twins.iso_json module#
The ISO JSON twin of a pain.001 document.
The shape follows the ISO 20022 Registration Authority’s Generation of JSON Schema Draft 2020-12 for ISO 20022:2013 (v2.0, June 2025):
keys are the XML tag names, unchanged, under a
Documentroot;an amount with a currency attribute is
{"amt": "…", "Ccy": "…"};decimals, dates and times are strings exactly as the XML spells them;
booleans are the strings
"true"and"false";namespaces and
xsihints are not represented (the edition names the schema);an empty element, which the XSD allows and the RA forbids, is
{}so the twin stays lossless; the schema reports it.
One rule is this project’s, not the RA’s: every element the schema declares repeatable is always an array, even with one occurrence, so a twin has one shape. The decoder accepts the RA’s other form (a bare value) as well.
Decoding goes through the XSD, so a twin that does not fit the edition
raises TwinError rather than producing bad XML.
- pain001.twins.iso_json.SUPPORTED_PREFIX = 'pain.001.'#
The message family the twin layer covers (ADR-0005).
- exception pain001.twins.iso_json.TwinError[source]#
Bases:
ValueErrorThe twin or the edition is not usable.
- pain001.twins.iso_json.from_iso_json(doc: dict[str, Any], version: str) str[source]#
The pain.001 document of an ISO JSON twin.
- Parameters:
doc –
{"Document": {...}}in the RA convention; repeatable elements may be arrays or bare values, in any key order.version – The edition to encode against.
- Returns:
The XML text, declared and indented, valid against the edition.
- Raises:
TwinError – If the twin has no
Documentroot, names an element the edition does not declare, or breaks a facet.
- pain001.twins.iso_json.supported(version: str) bool[source]#
True when
versionis a bundled pain.001 edition.
- pain001.twins.iso_json.to_iso_json(xml: str, version: str) dict[str, Any][source]#
The ISO JSON twin of a pain.001 document.
- Parameters:
xml – The document text.
version – Its edition, e.g.
pain.001.001.09.
- Returns:
{"Document": {...}}in the RA convention with always-array normalisation.- Raises:
TwinError – If the edition is not a bundled pain.001 edition or the document does not validate against it.
pain001.twins.records module#
The records twin: a pain.001 document as the library’s flat input rows.
The CSV pipeline renders a bundled template from flat records whose
columns the input JSON Schema names (debtor_name,
creditor_account_IBAN …). The records twin reads those columns back
out of a document, one row per transaction, so any corpus file can be
regenerated through the pipeline, and it says exactly which element
paths the pipeline cannot carry: the gap.
Which column lands on which element is not written anywhere; it is derived once per edition by rendering the template with sentinel values and reading where each sentinel ends up. That keeps the mapping true to the template the pipeline actually uses.
- class pain001.twins.records.RecordsTwin(rows: list[dict[str, str]], gap: list[str] = <factory>, missing_required: list[str] = <factory>)[source]#
Bases:
objectThe rows, what they cannot carry, and what the input schema wants.
- Variables:
rows (list[dict[str, str]]) – One flat record per transaction of the first payment block.
gap (list[str]) – Element paths present in the document that no column carries, relative to
Documentand without occurrence indexes.missing_required (list[str]) – Columns the edition’s preparer requires that the document gave no value for; with any of these the pipeline cannot regenerate the file.
- gap: list[str]#
- missing_required: list[str]#
- rows: list[dict[str, str]]#
- pain001.twins.records.column_paths(version: str) dict[str, tuple[str, ...]][source]#
Which element paths each input column renders to, per edition.
Derived by rendering the template with a sentinel per column and reading the sentinels back. Columns the template never renders map to nothing, and that is the input side of the gap.
- Parameters:
version – A bundled pain.001 edition.
- Returns:
Column to paths (relative to
Document, no indexes). A column can land on more than one element.
- pain001.twins.records.input_columns(version: str) tuple[list[str], list[str]][source]#
The pipeline’s columns and the input schema’s required columns.
The universe is the input schema’s properties plus the header of the bundled
template.csv: the older preparers require address and reference columns the schema does not list, and the sample carries them.
- pain001.twins.records.preparer_required(version: str) tuple[str, ...][source]#
The columns the edition’s preparer refuses to render without.
Learned by asking the preparer to render an empty row and reading the names it lists, so the records twin judges against the gate the pipeline really applies rather than the input schema’s list. A group of alternatives (an IBAN or an account number) is one entry, its names joined by
|.
- pain001.twins.records.to_records(xml: str, version: str) RecordsTwin[source]#
The records twin of a pain.001 document.
- Parameters:
xml – The document.
version – Its edition.
- Returns:
rows, gap and missing required columns.
- Return type:
The
RecordsTwin
pain001.twins.schema module#
JSON Schema 2020-12 for the ISO JSON twin of a pain.001 edition.
Generated from the schema inventory by the rules of the ISO 20022 Registration Authority’s Generation of JSON Schema Draft 2020-12 for ISO 20022:2013 (v2.0, June 2025):
the root requires
Document, which requires the message block;a component is an object with
additionalProperties: false,requiredfor its mandatory children andminProperties: 1when it has none;a choice is an object with exactly one property;
a repeatable element is
anyOfa single item and a bounded array, so the RA’s bare-value form and this project’s always-array twin both validate;an amount with a currency attribute is
{"amt", "Ccy"};every simple value is a string: enumerations become
enum, text lengthsminLength/maxLength, XSD patterns are anchored, decimals get a pattern built from their total and fraction digits and their lower bound, dates and times get the RA’s patterns, booleans allowtrue,false,1and0;the supplementary-data envelope is open.
Definitions are keyed by ISO type name under $defs (the RA uses
$anchor; the content is the same). No tool generates this from an
XSD; the inventory already carries every facet the rules consume.
- pain001.twins.schema.SCHEMA_DIR = PosixPath('/home/runner/work/pain001/pain001/pain001/schemas/iso-json')#
Where the committed schemas live.
- pain001.twins.schema.decimal_pattern(facets: dict[str, Any]) str[source]#
The RA-style pattern for a decimal from its facets.
At most
total_digitsdigits in all, at mostfraction_digitsof them after the point; leading and trailing zeros count, as the RA document says; a sign is allowed only when no lower bound of zero forbids it;5.and.5are accepted like the RA’s algebra.- Parameters:
facets –
total_digits,fraction_digitsand optionalmin_inclusive.- Returns:
An anchored ECMA regular expression.
- pain001.twins.schema.generate_schema(version: str) dict[str, Any][source]#
Generate the twin schema of an edition from its inventory.
- Parameters:
version – A bundled pain.001 edition.
- Returns:
The JSON Schema document; a non-pain.001 edition is refused with
TwinErrorby the shared check.
- pain001.twins.schema.iso_json_schema(version: str) dict[str, Any][source]#
The committed twin schema of an edition, or the generated one.
- Parameters:
version – A bundled pain.001 edition.
- Returns:
The JSON Schema document.