Skip to content

dpmxl.diagnostics

Static checks for editors. See Build editor support.

dpmxl.diagnostics

Static diagnostics: positioned problems in an expression, for editors.

diagnose reports what can be found without report data. On its own it finds every syntax error, unknown functions, calls with the wrong operands and selections without a table. Given table layouts it also checks tables, rows, columns and sheets, open-table keys and, when the layouts carry cell data types, operands of the wrong type. Given the report parameters, it also reports names that resolve to nothing.

Severity module-attribute

Severity = Literal['error', 'warning']

error or warning; see Diagnostic.

LayoutLookup module-attribute

LayoutLookup = Callable[[str], TableLayout | None]

Returns a table's TableLayout, or None for a table that does not exist, such as layouts.get for a dict.

DIAGNOSTIC_CODES module-attribute

DIAGNOSTIC_CODES = frozenset(
    {
        "syntax",
        "unknown-function",
        "operand-count",
        "group-by",
        "key-operand",
        "missing-table",
        "unknown-table",
        "unknown-row",
        "unknown-column",
        "unknown-sheet",
        "missing-row",
        "missing-column",
        "missing-sheet",
        "open-row",
        "open-column",
        "open-sheet",
        "empty-range",
        "type-mismatch",
        "unknown-key",
        "unknown-name",
    }
)

Every Diagnostic.code. Codes are stable; messages may be reworded in minor releases.

Diagnostic dataclass

Diagnostic(
    severity: Severity,
    code: str,
    message: str,
    start: int,
    end: int,
    subject: str | None = None,
    suggestion: str | None = None,
)

One problem in an expression, with the source text it concerns.

Attributes:

  • severity (Severity) –

    error when evaluation would fail or select nothing as written; warning when the expression evaluates but probably does not mean what it says.

  • code (str) –

    A stable identifier from DIAGNOSTIC_CODES:

    • syntax: the text does not parse;
    • unknown-function; operand-count and group-by: a call with the wrong number of operands or a group by the function does not take; key-operand: no key name where time_shift or annualise needs one;
    • missing-table: a selection without a table;
    • unknown-table: a table that does not exist;
    • unknown-row, unknown-column and unknown-sheet: a coordinate the table does not have, an error when no part of the selector matches and a warning when another part does; empty-range: the same for a range;
    • missing-row, missing-column and missing-sheet: a code on an axis the table does not have; open-row, open-column and open-sheet: a code on an open axis, whose rows, columns or sheets have keys instead of codes. What a provider selects for such a code is up to it (a MemoryProvider ignores the selector), so these are warnings, and they are not reported for a selector a shared with context applies;
    • type-mismatch: an operand of the wrong data type;
    • unknown-key: a name in a clause that is not a key of what the clause applies to (a warning where a report parameter may stand in and the parameters are not known); unknown-name: a name that is no report parameter or earlier assignment.
  • message (str) –

    What is wrong, in plain words.

  • start (int) –

    Zero-based offset of the first character concerned.

  • end (int) –

    Offset just past the last character; equals start for a problem at a position, such as the end of the expression.

  • subject (str | None) –

    What the problem is about, when it names something: the function, table, coordinate, key or name as written, or the data type found; None otherwise.

  • suggestion (str | None) –

    A replacement for expression[start:end] that likely fixes the problem, such as sum for summ; None when there is no likely fix.

severity instance-attribute

severity: Severity

code instance-attribute

code: str

message instance-attribute

message: str

start instance-attribute

start: int

end instance-attribute

end: int

subject class-attribute instance-attribute

subject: str | None = None

suggestion class-attribute instance-attribute

suggestion: str | None = None

pointer

pointer(expression: str) -> str

Render the source line and mark the text concerned.

Parameters:

  • expression (str) –

    The expression the diagnostic was found in.

Returns:

Examples:

>>> source = "summ({tC_01.00, r0010}) > 0"
>>> print(diagnose(source)[0].pointer(source))
summ({tC_01.00, r0010}) > 0
^~~~

diagnose

diagnose(
    expression: str,
    layout: LayoutLookup | None = None,
    *,
    tables: Iterable[str] | None = None,
    parameters: Iterable[str] | None = None,
    program: bool = False,
) -> tuple[Diagnostic, ...]

Return the problems found in an expression without evaluating it.

Every syntax error is reported: after one, parsing resumes where the enclosing construct can carry on, and an error caused by an earlier one is left out. A construct that contains a syntax error is not checked further. Selections inside with are checked with their context, and a problem in the context is reported once, at the context.

Parameters:

  • expression (str) –

    A DPM-XL rule expression, or a script with program=True.

  • layout (LayoutLookup | None, default: None ) –

    Looks up a table's TableLayout, or returns None for a table that does not exist. Without it, tables, coordinates, keys and data types are not checked. Table groups and operations are never checked.

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

    Every table code, to suggest one for an unknown table.

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

    Every report parameter name. With them, a name that is neither a parameter, nor assigned by an earlier statement, nor a key in a clause is an error; without them names are not checked outside clauses.

  • program (bool, default: False ) –

    Accept a script (;, := and <-), as parse_program does; otherwise the text must be one expression, as for parse.

Returns:

  • Diagnostic –

    tuple[Diagnostic, ...]: Problems in source order; empty when none

  • ... –

    were found.

Examples:

>>> for d in diagnose("summ({tC_01.00, r0010}) > 0 and"):
...     print(d.start, d.end, d.code, d.message)
0 4 unknown-function unknown function 'summ'; did you mean 'sum'?
31 31 syntax expression ends where an operand is expected
>>> from dpmxl import TableLayout
>>> layouts = {"C_01.00": TableLayout("C_01.00", rows=("0010", "0020"))}
>>> for d in diagnose("{tC_01.00, r0011} = {tC_02.00, r0010}", layouts.get):
...     print(d.start, d.end, d.message, d.suggestion)
11 16 row 0011 is not in table C_01.00; did you mean r0010? r0010
21 29 unknown table C_02.00 None