Skip to content

dpmxl.provider

The data contract between the evaluator and a report. See Connect your data for a walkthrough of every method, optional hooks included.

dpmxl.provider

Data access contract between the evaluator and a report.

The evaluator never reads report files. It asks a DataProvider for the data points a selection covers and for report-level facts such as filing indicators. A provider is responsible for:

  • resolving table codes and axis selectors against the table definitions;
  • returning every defined cell of a closed selection, with value=None when the report has no fact for it, so default and isnull work;
  • returning one data point per reported key combination for open axes, with the open key components in DataPoint.keys.

MemoryProvider is a complete in-memory implementation used by the tests and by tools that build reports programmatically.

CELL_TYPES module-attribute

CELL_TYPES = frozenset(
    {
        "boolean",
        "date",
        "integer",
        "item",
        "number",
        "string",
    }
)

Data types a TableLayout gives its cells.

CellQuery dataclass

CellQuery(
    table: str,
    rows: AxisSelector | None = None,
    columns: AxisSelector | None = None,
    sheets: AxisSelector | None = None,
)

A fully resolved selection: table plus optional axis selectors.

The evaluator passes one to DataProvider.select after applying the with context. Match coordinates with axis_matcher.

Attributes:

  • table (str) –

    The table code, such as C_01.00.

  • rows (AxisSelector | None) –

    The row selector; None when the selection names no rows, which selects every row.

  • columns (AxisSelector | None) –

    The column selector, likewise.

  • sheets (AxisSelector | None) –

    The sheet selector, likewise.

table instance-attribute

table: str

rows class-attribute instance-attribute

rows: AxisSelector | None = None

columns class-attribute instance-attribute

columns: AxisSelector | None = None

sheets class-attribute instance-attribute

sheets: AxisSelector | None = None

DataPoint dataclass

DataPoint(
    table: str,
    row: str | None = None,
    column: str | None = None,
    sheet: str | None = None,
    keys: Mapping[str, Value] = dict(),
    value: Value = None,
    ref: Hashable | None = None,
)

One selected cell, and for open tables one reported key combination.

ref is an opaque provider identity (for example a cell ID) that is passed back in evaluation results so callers can highlight the cell.

Attributes:

  • table (str) –

    The table code.

  • row (str | None) –

    The row code, or None for a table without rows.

  • column (str | None) –

    The column code, or None.

  • sheet (str | None) –

    The sheet code, or None.

  • keys (Mapping[str, Value]) –

    Open key components by name (such as {"qCPI": "LEI-A"}); empty for closed tables.

  • value (Value) –

    The reported value, or None when nothing was reported.

  • ref (Hashable | None) –

    Any hashable identity of the cell; points read by time_shift from another report carry an EarlierRef.

table instance-attribute

table: str

row class-attribute instance-attribute

row: str | None = None

column class-attribute instance-attribute

column: str | None = None

sheet class-attribute instance-attribute

sheet: str | None = None

keys class-attribute instance-attribute

keys: Mapping[str, Value] = field(default_factory=dict)

value class-attribute instance-attribute

value: Value = None

ref class-attribute instance-attribute

ref: Hashable | None = None

coordinate

coordinate(axis: str) -> str | None

Return the closed coordinate for r, c or s.

Returns:

  • str | None –

    str | None: The coordinate code, or None when absent.

Raises:

  • KeyError –

    For another axis.

label

label() -> str

Render a compact DPM-XL style location.

Returns:

  • str ( str ) –

    For example {tC_01.00, r0010, c0010}.

Examples:

>>> DataPoint("C_01.00", row="0010", column="0010").label()
'{tC_01.00, r0010, c0010}'
>>> DataPoint("C_27.00", column="0010", keys={"qCPI": "LEI-B"}).label()
'{tC_27.00, c0010, qCPI=LEI-B}'

DataProvider

Bases: Protocol

Source of report data for one evaluation.

Three methods are required. select resolves a CellQuery against the table definitions (axis_matcher and normalize_code implement range and code matching) and returns:

  • for a closed table, every defined cell the selection covers, with value=None where the report has no fact, so default and isnull work;
  • for an open table, one point per reported key combination (times the selected closed coordinates), with the key components in DataPoint.keys;

and raises LookupError for a table the release does not define (the rule's status is then error). table_reported answers {v_TABLE} and parameter answers {vName} and {p_Name, type}.

A provider may also define these optional methods:

  • open_keys(table: str) -> Sequence[str]: the open key names of a table. Without it, an open table with no reported rows yields a selection without keys; with it, the selection keeps the table's key structure and is empty, as the specification requires.
  • table_group(code: str) -> Sequence[str] | None: the table codes of a table group, for {g_Group, ...} selections.
  • operation(code: str) -> str | None: the expression of another operation, for {o_Code, ...} references.
  • earlier_report(period: str, count: int) -> DataProvider | None: the report count periods earlier (later when negative), where period is A, S, Q, M, W or D, for time_shift.

Without the optional method, or when it returns None, rules that need it are unsupported. DataProvider is runtime_checkable: isinstance checks the three required methods only.

A provider shared by several threads must be safe to read concurrently.

select

select(query: CellQuery) -> Sequence[DataPoint]

Return the data points covered by a selection.

Raises:

  • LookupError –

    If the table does not exist in the release.

table_reported

table_reported(table: str) -> bool | None

Return whether the report's filing indicators include table.

table is the code written in {v_...}, usually a template code such as C_01.00. Returns None when the provider cannot tell, which makes {v_...} null.

parameter

parameter(name: str) -> Value

Return a report-level parameter such as refPeriod.

Returns None when the parameter is unknown. Typed parameters ({p_Name, type}) convert the value: Python numbers become Number, text becomes an item or date for those types, and a list or set becomes a set.

EarlierRef dataclass

EarlierRef(period: str, count: int, ref: Hashable)

The ref of a data point that time_shift read from another report.

It wraps the earlier report's own ref with the shift, so the point stays distinct from the current report's cell with the same ref.

Attributes:

  • period (str) –

    The time_shift period, such as Q.

  • count (int) –

    The number of periods.

  • ref (Hashable) –

    The earlier report's own ref for the cell.

period instance-attribute

period: str

count instance-attribute

count: int

ref instance-attribute

ref: Hashable

EarlierReport

EarlierReport(
    provider: DataProvider, period: str, count: int
)

The report time_shift reads, marking its points' refs.

Every data point it selects carries an EarlierRef instead of the provider's ref (points without a ref keep None); every other method, optional ones included, is the wrapped provider's.

Wrap a provider.

Parameters:

  • provider (DataProvider) –

    The earlier report.

  • period (str) –

    The time_shift period, such as Q.

  • count (int) –

    The number of periods.

provider instance-attribute

provider = provider

period instance-attribute

period = period

count instance-attribute

count = count

select

select(query: CellQuery) -> Sequence[DataPoint]

Return the earlier report's points with marked refs.

Returns:

  • Sequence[DataPoint] –

    Sequence[DataPoint]: The points.

table_reported

table_reported(table: str) -> bool | None

Return the earlier report's filing indicator.

Returns:

  • bool | None –

    bool | None: Filing-indicator state.

parameter

parameter(name: str) -> Value

Return an earlier report parameter.

Returns:

  • Value ( Value ) –

    The parameter or None.

__getattr__

__getattr__(name: str) -> Any

Delegate optional provider methods.

Returns:

  • Any ( Any ) –

    The wrapped provider's attribute.

AxisMatcher dataclass

AxisMatcher(
    codes: frozenset[str],
    numeric_ranges: tuple[tuple[int, int], ...],
    text_ranges: tuple[tuple[str, str], ...],
    wildcard: bool = False,
)

A compiled axis selector: exact codes plus inclusive ranges.

Build one with axis_matcher and call it with a coordinate code.

Attributes:

  • codes (frozenset[str]) –

    Normalized single codes.

  • numeric_ranges (tuple[tuple[int, int], ...]) –

    Inclusive ranges with numeric bounds.

  • text_ranges (tuple[tuple[str, str], ...]) –

    Inclusive ranges with text bounds, compared as text.

  • wildcard (bool) –

    Whether every coordinate is selected.

codes instance-attribute

codes: frozenset[str]

numeric_ranges instance-attribute

numeric_ranges: tuple[tuple[int, int], ...]

text_ranges instance-attribute

text_ranges: tuple[tuple[str, str], ...]

wildcard class-attribute instance-attribute

wildcard: bool = False

single property

single: str | None

The only selected code, when the selector names exactly one.

__call__

__call__(code: str | None) -> bool

Return whether a coordinate is selected.

Returns:

  • bool ( bool ) –

    Whether code is covered.

TableLayout dataclass

TableLayout(
    code: str,
    rows: tuple[str, ...] = (),
    columns: tuple[str, ...] = (),
    sheets: tuple[str, ...] = (),
    open_keys: tuple[str, ...] = (),
    template: str | None = None,
    open_axes: tuple[AxisName, ...] = (),
    data_types: Mapping[
        tuple[str | None, str | None, str | None], str
    ] = dict(),
)

The coordinates, open keys and cell data types of one table.

MemoryProvider serves cells from layouts, and diagnose checks selections against them. An axis is closed (its rows, columns or sheets codes), open (named in open_axes: its rows, columns or sheets are identified by the open_keys, not by codes) or absent (neither).

When a table has been split into several, as when C_02.00 became C_02.00.a and C_02.00.b, rules may still select the original, abstract code; describe it with the union of its parts' coordinates.

Attributes:

  • code (str) –

    The table code, such as C_01.00.

  • rows (tuple[str, ...]) –

    Row codes; empty for a table without closed rows.

  • columns (tuple[str, ...]) –

    Column codes; empty for a table without closed columns.

  • sheets (tuple[str, ...]) –

    Sheet codes; empty for a table without closed sheets.

  • open_keys (tuple[str, ...]) –

    Open key names, such as ("qCPI",); empty for a closed table.

  • template (str | None) –

    The template code when it differs from code, so {v_TEMPLATE} holds when any of its tables was reported.

  • open_axes (tuple[AxisName, ...]) –

    The open axes among rows, columns and sheets. Optional: without it, an axis without codes of a table with open keys is taken to be open or absent, and selections of it are not checked. What a provider selects for a code on an open or absent axis is up to it: a MemoryProvider ignores the selector, other providers may select nothing. diagnose reports such a code as a warning.

  • data_types (Mapping[tuple[str | None, str | None, str | None], str]) –

    The data type of each cell by its (row, column, sheet), with None for an open or absent axis: number, integer, string, date, boolean or item. Optional; diagnose uses it to find operands of the wrong type. It may be partial: a selection that covers a cell without a type has no known type, so it is not checked. It takes no part in equality.

Raises:

  • ValueError –

    For an unknown axis in open_axes, an open axis with codes, or an unknown data type.

Examples:

>>> layout = TableLayout(
...     "C_27.00",
...     columns=("0010", "0020"),
...     open_keys=("qCPI",),
...     open_axes=("rows",),
...     data_types={(None, "0010", None): "number"},
... )
>>> layout.open_axes, layout.data_types[None, "0010", None]
(('rows',), 'number')

code instance-attribute

code: str

rows class-attribute instance-attribute

rows: tuple[str, ...] = ()

columns class-attribute instance-attribute

columns: tuple[str, ...] = ()

sheets class-attribute instance-attribute

sheets: tuple[str, ...] = ()

open_keys class-attribute instance-attribute

open_keys: tuple[str, ...] = ()

template class-attribute instance-attribute

template: str | None = None

open_axes class-attribute instance-attribute

open_axes: tuple[AxisName, ...] = ()

data_types class-attribute instance-attribute

data_types: Mapping[
    tuple[str | None, str | None, str | None], str
] = field(default_factory=dict, compare=False, repr=False)

__post_init__

__post_init__() -> None

Check the open axes and data types.

Raises:

  • ValueError –

    For an unknown or coded open axis, or an unknown data type.

coordinates

coordinates() -> Iterable[
    tuple[str | None, str | None, str | None]
]

Yield every closed (row, column, sheet) combination.

Yields:

  • Iterable[tuple[str | None, str | None, str | None]] –

    tuple[str | None, str | None, str | None]: One cell position.

MemoryProvider

MemoryProvider(
    tables: Iterable[TableLayout],
    values: Mapping[CellKey, Value],
    *,
    reported: Iterable[str] | None = None,
    parameters: Mapping[str, Value] | None = None,
    table_groups: Mapping[str, Sequence[str]] | None = None,
    operations: Mapping[str, str] | None = None,
    earlier_reports: Mapping[tuple[str, int], DataProvider]
    | None = None,
)

Provider over in-memory table layouts and values.

Values are keyed by (table, row, column, sheet, keys) where keys is a tuple of (name, value) pairs for open tables (empty for closed ones). Absent tables in reported are treated as not reported. Every optional DataProvider method is implemented. Each point's ref is its value key.

A MemoryProvider is safe to read from several threads at once.

Examples:

>>> from dpmxl import CellQuery, Number
>>> closed = TableLayout("C_01.00", rows=("0010", "0020"), columns=("0010",))
>>> open_ = TableLayout("C_27.00", columns=("0010",), open_keys=("qCPI",))
>>> provider = MemoryProvider(
...     [closed, open_],
...     {
...         ("C_01.00", "0010", "0010", None, ()): Number.of(100, 0),
...         ("C_27.00", None, "0010", None, (("qCPI", "A"),)): Number.of(1, 0),
...     },
...     parameters={"refPeriod": "2026-12-31"},
... )
>>> [(p.row, p.value) for p in provider.select(CellQuery("C_01.00"))][1]
('0020', None)
>>> [p.keys for p in provider.select(CellQuery("C_27.00"))]
[{'qCPI': 'A'}]
>>> provider.table_reported("C_27.00"), provider.parameter("refPeriod")
(True, '2026-12-31')

Index tables and values.

Parameters:

  • tables (Iterable[TableLayout]) –

    Table layouts.

  • values (Mapping[CellKey, Value]) –

    Cell values; see the class docstring for the key shape.

  • reported (Iterable[str] | None, default: None ) –

    Template or table codes with a positive filing indicator; defaults to every table that has a value.

  • parameters (Mapping[str, Value] | None, default: None ) –

    Report parameters such as refPeriod.

  • table_groups (Mapping[str, Sequence[str]] | None, default: None ) –

    Table codes by table group code.

  • operations (Mapping[str, str] | None, default: None ) –

    Operation expressions by operation code.

  • earlier_reports (Mapping[tuple[str, int], DataProvider] | None, default: None ) –

    Reports of other periods by (period, count), such as ("A", 1) for the previous year.

earlier_reports instance-attribute

earlier_reports = dict(earlier_reports or {})

table_groups instance-attribute

table_groups = dict(table_groups or {})

operations instance-attribute

operations = dict(operations or {})

tables instance-attribute

tables = {table.code: table for table in tables}

values instance-attribute

values = dict(values)

parameters instance-attribute

parameters = dict(parameters or {})

open_rows instance-attribute

open_rows: dict[str, set[tuple[Any, ...]]] = {}

reported instance-attribute

reported = (
    set(reported)
    if reported is not None
    else {key[0] for key in self.values}
)

select

select(query: CellQuery) -> Sequence[DataPoint]

Return closed cells, and one point per reported open key set.

Returns:

  • Sequence[DataPoint] –

    Sequence[DataPoint]: Selected data points.

Raises:

  • LookupError –

    If the table is unknown.

open_keys

open_keys(table: str) -> tuple[str, ...]

Return a table's open key names.

Returns:

  • tuple[str, ...] –

    tuple[str, ...]: Key names; empty for closed or unknown tables.

table_reported

table_reported(table: str) -> bool | None

Return whether a table or its template was reported.

Returns:

  • bool | None –

    bool | None: Filing-indicator state.

parameter

parameter(name: str) -> Value

Return a report parameter.

Returns:

  • Value ( Value ) –

    The parameter or None.

table_group

table_group(code: str) -> Sequence[str] | None

Return the table codes of a table group.

Returns:

  • Sequence[str] | None –

    Sequence[str] | None: The tables, or None for an unknown group.

earlier_report

earlier_report(
    period: str, count: int
) -> DataProvider | None

Return the report count periods earlier.

Returns:

  • DataProvider | None –

    DataProvider | None: The report, or None when not given.

operation

operation(code: str) -> str | None

Return the expression of an operation.

Returns:

  • str | None –

    str | None: The expression, or None for an unknown operation.

normalize_code cached

normalize_code(code: str) -> str

Normalize an axis code for comparison.

Numeric codes are compared as numbers padded to four digits, so r310 in an older rule matches row 0310. Results are memoized: a report's rules normalize the same few thousand codes millions of times.

Returns:

  • str ( str ) –

    A canonical code.

Examples:

>>> normalize_code("310"), normalize_code("0310"), normalize_code("x01")
('0310', '0310', 'X01')

axis_matcher cached

axis_matcher(selector: AxisSelector | None) -> AxisMatcher

Compile a selector once for repeated matching.

Returns:

  • AxisMatcher ( AxisMatcher ) –

    A callable matcher; None and wildcards select all.

Examples:

>>> from dpmxl import AxisSelector
>>> rows = axis_matcher(AxisSelector(parts=(("0010", "0050"), "0100")))
>>> rows("0030"), rows("100"), rows("0060"), rows(None)
(True, True, False, False)

axis_matches

axis_matches(
    code: str | None, selector: AxisSelector | None
) -> bool

Return whether a coordinate is selected.

None selectors and wildcards select every coordinate. Ranges are inclusive and compare numerically when both bounds are numeric.

Returns:

  • bool ( bool ) –

    Whether code is covered by selector.

Examples:

>>> from dpmxl import AxisSelector
>>> axis_matches("0020", AxisSelector(parts=("0010", "0020")))
True
>>> axis_matches("0020", None)
True