Skip to content

math_spec.validation

The front door, and the rules a declaration is held to against the others before any expression is read.

:func:to_spec reads a model definition into a :class:~math_spec.model.Spec. :func:reference_errors holds the rules one declaration is held to against the others — a name declared once, a frame over declared dimensions, a bound naming a numeric parameter, a set over one dim of one variable, a curve through parameters carrying its breakpoints — which lowering runs before it reads any expression, since resolution assumes every one of them. :func:emitted_name_errors is read off the program instead: what a block's expansion writes is decided by the block as lowered. The rules that need a typed expression stay with the expressions in :func:~math_spec.lowering.lower: a macro formal against a dimension, a curve's links, and every dim rule.

emitted_name_errors(schema, program) #

Every name a set or curve of program would write out that schema already declares.

Read off the program rather than the file, since what a curve writes is decided by the curve as lowered — its links, its method, its mask.

Source code in src/math_spec/validation.py
def emitted_name_errors(schema: Spec, program: Program) -> list[str]:
    """Every name a set or curve of *program* would write out that *schema* already declares.

    Read off the program rather than the file, since what a curve writes is
    decided by the curve as lowered — its links, its method, its mask.
    """
    by_block = [
        *((f"Sos '{name}'", EmittedSet.of(name, block.sos_type).by_kind) for name, block in program.sos.items()),
        *((f"piecewise '{name}'", EmittedCurve.of(name, curve).by_kind) for name, curve in program.piecewise.items()),
    ]
    return [error for context, by_kind in by_block for error in _collisions(schema, context, by_kind)]

reference_errors(schema) #

Every cross-declaration rule schema breaks, collected rather than raised on the first.

Source code in src/math_spec/validation.py
def reference_errors(schema: Spec) -> list[str]:
    """Every cross-declaration rule *schema* breaks, collected rather than raised on the first."""
    return [
        *_name_collisions(schema),
        *_frame_dimensions(schema),
        *_relation_targets(schema),
        *_bound_names(schema),
        *_sos_shapes(schema),
        *_sos_bounds(schema),
        *_piecewise_references(schema),
    ]

to_spec(model) #

Load and validate a model definition — the language's front door.

Everything decidable without data is decided here: schema shape, every rule one declaration is held to against the others, every expression and where string, and every macro template.

PARAMETER DESCRIPTION
model

A YAML path — a :class:~pathlib.Path, or a str with no newline in it — the YAML text itself as a str with one, a mapping, or a loaded :class:Spec.

TYPE: str | Path | Mapping[str, object] | Spec

RETURNS DESCRIPTION
Spec

The schema as the file declares it, piecewise: intact.

RAISES DESCRIPTION
LanguageError

Anything the language does not accept, a text that is not a mapping of sections included.

FileNotFoundError

A str with no newline that names no file.

Source code in src/math_spec/validation.py
def to_spec(model: str | Path | Mapping[str, object] | Spec) -> Spec:
    """Load and validate a model definition — the language's front door.

    Everything decidable without data is decided here: schema shape, every
    rule one declaration is held to against the others, every expression and
    where string, and every macro template.

    Args:
        model: A YAML path — a :class:`~pathlib.Path`, or a ``str`` with no
            newline in it — the YAML text itself as a ``str`` with one, a
            mapping, or a loaded :class:`Spec`.

    Returns:
        The schema *as the file declares it*, ``piecewise:`` intact.

    Raises:
        LanguageError: Anything the language does not accept, a text that is
            not a mapping of sections included.
        FileNotFoundError: A ``str`` with no newline that names no file.
    """
    if isinstance(model, (list, tuple)):
        msg = 'a model is one file, one dict or one Spec, never a list of them; merge the declarations into one dict.'
        raise SchemaError(msg)
    if isinstance(model, Spec):
        return model
    return Spec.model_validate(model if isinstance(model, Mapping) else read_model(model))

undeclared_dimension(kind, name, dimension) #

The one wording for a declaration naming a dimension the file does not declare.

Source code in src/math_spec/validation.py
def undeclared_dimension(kind: str, name: str, dimension: str) -> str:
    """The one wording for a declaration naming a dimension the file does not declare."""
    return f"{kind} '{name}' references undeclared dimension '{dimension}'. Declare it under 'dimensions:'."