dpm-xl
A Python parser and evaluator for DPM-XL, the expression language of EBA
validation rules and similar checks. The core has no dependencies; the
optional columnar extra adds a polars evaluator for reports with millions of
open-table rows.
from dpmxl import evaluate_rule, explain
expression = (
"with {tC_01.00, c*, default: 0, interval: true}: {r0010} = {r0020} + {r0030}"
)
result = evaluate_rule(expression, provider, precondition="{v_C_01.00}")
for outcome in result.failures:
print(explain(result.evaluation, expression, outcome.key).summary)
# 1,204 should equal 1,211 (1,180 + 31): difference 7, at least 5.5 given the
# reported precision
The package does not read report files and knows nothing about a particular DPM release. Data comes through a small provider protocol (see below), so the same evaluator works against an xBRL-CSV upload, a database, or test fixtures.
dpm-xl is an independent project, not affiliated with or endorsed by the European Banking Authority.
Modules
| Module | Purpose |
|---|---|
dpmxl.parser |
Tokenizer and recursive-descent parser. Nodes keep source spans. |
dpmxl.ast |
Expression tree, selections, axis selectors and item literals. |
dpmxl.values |
Number with interval tolerance, comparison and formatting. |
dpmxl.recordset |
Keyed record sets and the natural join used by every operator. |
dpmxl.evaluator |
Evaluation, preconditions and per-record outcomes. |
dpmxl.explain |
Fills a failing record's formula with operand values. |
dpmxl.references |
Static selections (with with context applied) and {v_…} templates. |
dpmxl.provider |
DataProvider protocol and the in-memory MemoryProvider. |
dpmxl.columnar |
The same semantics on polars frames (optional columnar extra). |
dpmxl.cli |
dpm-xl check and dpm-xl eval. |
Supported language
with {context} [where …]: body, nested contexts replacing inherited parts.- Selections
{tTABLE, r…, c…, s…, default: v, interval: b}with single codes, inclusive ranges (r0010-0050), lists ((c0010, c0030)) and wildcards. {v_TABLE}filing-indicator tests and{vName}report parameters.- Literals: numbers,
4.5%,"text"/'text',#2026-12-31#,true/false,null, items[eba_qEC:qx01]. - Operators
+ - * /,= != <> < <= > >=,in {…},not,and,or,xor,if … then … [else …] endif. - Aggregates
sum,count,avg,min,max,median(and the specification'smin_aggr/max_aggrspellings) withgroup by; scalarabs,isnull,nvl,len,match,min/maxwith several operands,exp,ln,sqrt,power;filter(data, condition). - Clauses
[where …],[get key],[sub key = value],[rename a to b].
time_shift needs the previous reporting period and is reported as
unsupported. Every distinct EBA rule and precondition expression in releases
3.4–4.4 parses, except two source expressions with typos (trueendif,
ornot), which are reported as syntax errors.
Semantics
- Records and keys. A selection yields one record per data point. Its key
holds every closed axis that selects more than one coordinate (
r,c,s; an axis that is not written counts when the table has several coordinates on it) plus open key components such asqEBB. Binary operators natural-join on shared keys; a scalar broadcasts. Operands with unrelated keys are an error. - Missing facts take the selection's
default; with no default they are null. Providers must therefore return every defined cell of a closed selection, reported or not. - Interval arithmetic. With
interval: true, numbers follow the Eurofiling interval arithmetic functions (iaf:*, version 1.1.1) that EBA validation rules are based on. A fact reported withdecimals = dstands forvalue ± 0.5·10^-d(INFis exact; constants and defaults are exact). Sums and differences add thresholds; products use|A·δB| + |B·δA| + δA·δB; quotients take the largest deviation of the four endpoint quotients (skipping a zero divisor);absand negation keep the threshold;minandmaxkeep the chosen value's threshold.a = bholds when|a − b| ≤ δa + δb(the intervals overlap),a ≤ bwhena − b ≤ δa + δb,a ≥ bwhena ≥ b − (δa + δb), and likewise for the strict comparisons. Withinterval: falsefacts are exact. Explanations report each side's interval and the gap: how far apart the closest values within those intervals still are. - Null. Null propagates through arithmetic, comparisons and functions
(except
isnullandnvl).and/or/notuse three-valued (Kleene) logic. A record whose result is null is "not evaluated", never a failure. Division by zero is a run-time error, as the specification requires. - Conditionals.
iftakes theelsebranch when the condition is null; withoutelse, a Booleanthenyields true where the condition is false or null. The result has the condition's record structure; branch keys the condition lacks widen it, so each condition record applies to every branch record sharing its keys (the columnar evaluator hands that case to the reference evaluator). - Aggregates over no records are null, except
count, which is 0;countcounts non-null values.maxandminalso order dates, text and items by their text (ISO dates in date order, items by category and code), as presence checks expect; Booleans, and numbers mixed with other values in one group, are an error. - Clauses.
[get key]omits records whose fact is null; awhereorsubinside awithbody overrides the context's clause on the same key. Booleans orderfalse < true. - Key names in
where,group by,subandgetmatch with or without the DPM 2.0qprefix and ignore case (EBBfindsqEBB). - Items compare by code and category, ignoring the
eba_prefix and a category missing on either side. - Preconditions make a rule
not_applicableunless they yield at least one true record.
A RuleResult has status passed, failed, not_applicable, error
(syntax, unknown table, type mismatch) or unsupported, plus one Outcome
per record with the data points it used.
These semantics follow the EBA DPM-XL language specification v0.9
(operators and data model documents) and the Eurofiling interval arithmetic
functions. Where the specification is ambiguous or contradicts itself (sum of
no records, count over nulls, key-name aliases, the radius formulas for
products and quotients), the choice above is the one that avoids false
failures.
Columnar evaluation
dpmxl.columnar.evaluate_rule_columnar(expression, provider) evaluates a rule
on polars frames from a FrameProvider (select_frame(query) returns one row
per data point with k:<key> columns and the num, thr, txt, flag value
columns). Values are exact Decimal(38, 12), keys are joined like
recordset.combine, and a FrameCache shares selections across rules within
a row budget. Anything the module does not implement, including arithmetic
that would overflow, raises ColumnarUnsupported so the caller falls back to
the reference evaluator. tests/test_columnar.py checks both evaluators agree
case by case. PointsFrameProvider adapts any DataProvider.
Provider contract
class DataProvider(Protocol):
def select(self, query: CellQuery) -> Sequence[DataPoint]: ...
def table_reported(self, table: str) -> bool | None: ...
def parameter(self, name: str) -> Value: ...
select resolves the table and axis selectors (axis_matches and
normalize_code implement range and code matching) and returns all defined
cells of the closed axes with value=None where nothing was reported. For an
open table it returns one point per reported key combination, with the key
components in DataPoint.keys. DataPoint.ref is an opaque identity that
comes back in outcomes, so a caller can highlight cells.
CLI
dpm-xl check rules.json # parse every expression/precondition in a rule list
dpm-xl check "with {tC_01.00}: {r0010} >= 0" --references
dpm-xl eval "with {tC_01.00, c0010, default: 0, interval: true}: {r0010} = {r0020} + {r0030}" data.json
data.json holds tables (code, rows, columns, sheets, open_keys),
facts (table, row, column, sheet, keys, value, decimals,
type), reported and parameters.
Development
uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check . && uv run ty check
tests/data/eba_expressions.txt is a sample of about 300 EBA rule
expressions covering every construct above. Every line must parse.
Metadata
Release files for dpm-xl 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| dpm_xl-0.1.0.tar.gz | 56.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dpm_xl-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 117.1 kB
Release files / dpm_xl-0.1.0.tar.gz
| Download URL | dpm_xl-0.1.0.tar.gz |
|---|---|
| Size | 56.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4cb798fc72909ec1dde00e89d49f6ad91fc0df658027087a119ec5ea30a2bc9d
|
|
BLAKE2b-256 checksum How to use checksums |
ee4de1b82090c474731d448c94b01666b4995e6b58dd21b052d9ef10481b51c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / dpm_xl-0.1.0-py3-none-any.whl
| Download URL | dpm_xl-0.1.0-py3-none-any.whl |
|---|---|
| Size | 61.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
13e760071e4a280b7a3fa5316dfa2e999a2379db2a71e901561b192c9144dfbf
|
|
BLAKE2b-256 checksum How to use checksums |
9855167a59562b45ffe37a65a3b8d3b7e37aef4d146c9b6e02709aea21d7f0ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for dpm-xl 0.1.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.