Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ it releases that version ([RELEASING.md](https://github.com/energy-models/mathsp
- feat(language)!: `missing:` says what a missing row means, a table short of a row is refused unless the file says otherwise, and a variable's `absence:` is now `missing:` ([#810](https://github.com/energy-models/mathspec/pull/810))
- docs(pypsa): a risk preference with weight zero builds the CVaR variables and refuses quadratic costs, as in PyPSA ([#849](https://github.com/energy-models/mathspec/pull/849))
- docs(pypsa): storage that retires before the last counted snapshot closes a global limit at its last active level ([#850](https://github.com/energy-models/mathspec/pull/850))
- refactor(api): the top level holds what you call, and spec, program and errors hold what you get back or catch ([#837](https://github.com/energy-models/mathspec/pull/837))
- refactor(program)!: the node a use of a named expression stands as is `NamedExpression`, beside `NamedMask`, and `Named` is gone ([#822](https://github.com/energy-models/mathspec/pull/822))
- docs(pypsa): the pypsa spec names its repeated row conditions as masks, and its topic files read them under `given: masks:` ([#825](https://github.com/energy-models/mathspec/pull/825))
- feat(language): a where predicate is named once under `masks:`, and another file reads it under `given: masks:` ([#821](https://github.com/energy-models/mathspec/pull/821))
Expand Down
18 changes: 17 additions & 1 deletion docs/about/what-counts-as-public-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ A function may join the public API when both of these hold:
Wherever a feature can be a key in the file, it is one: a key shows up in a git
diff, the typesetter prints it, and an engine in another language reads it.

## Where a name lives

The top level holds what you call. Every other public name lives in one of
three modules, and the module follows from how you get the name:

| You get it | It lives in | Such as |
| --------------------------- | ------------------ | ---------------------------------------- |
| by calling a function | `mathspec` | `to_spec`, `merge`, `typeset`, `advice` |
| from what a `Spec` holds | `mathspec.spec` | `Spec`, `VariableBlock`, `BUILTIN_NAMES` |
| from what a `Program` holds | `mathspec.program` | `Program`, `Sum`, `Mask`, `Advice` |
| by catching it | `mathspec.errors` | `LanguageError`, `SchemaError` |

`SymbolTable` and `FormatName` are at the top level too: you build or name
them to pass them to the typesetter. `tests/test_public_surface.py` holds each
module to its rule.

## What every function keeps

- **No state.** No registry, no plugin, and no setting that changes what a
Expand All @@ -32,7 +48,7 @@ diff, the typesetter prints it, and an engine in another language reads it.
talks about a file the language accepts, and changes nothing.
- **Nothing is written out unasked.** A `piecewise:` or `sos:` block stays the
block until a caller calls
[`spec.expand()`](../reference/api.md#mathspec.Spec.expand).
[`spec.expand()`](../reference/spec.md#mathspec.spec.Spec.expand).

What a solver or file format can take, how the numbers attach to the names, and
which solver runs are each engine's to decide
Expand Down
2 changes: 1 addition & 1 deletion docs/howto/compare.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ lists what the form sorts and what it keeps.
```

5. **Compare from Python** where the comparison is one step of a longer
script. [`to_yaml`](../reference/api.md#mathspec.Spec.to_yaml) writes the
script. [`to_yaml`](../reference/spec.md#mathspec.spec.Spec.to_yaml) writes the
same text with `canonical=True`:

```python
Expand Down
2 changes: 1 addition & 1 deletion docs/howto/see-an-expansion.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ the set out too.
<!-- expansion:curve:end -->
<!-- prettier-ignore-end -->

[`Spec.expand()`](../reference/api.md#mathspec.Spec.expand) lists what
[`Spec.expand()`](../reference/spec.md#mathspec.spec.Spec.expand) lists what
the call accepts, and
[writing a formulation out](../reference/language/piecewise.md#writing-a-formulation-out)
says what each block emits.
68 changes: 12 additions & 56 deletions docs/reference/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,24 @@ SPDX-License-Identifier: CC-BY-4.0
# Python API

This page documents every name that `import mathspec` exports, grouped by
task.
task. The top level holds what you call. Three modules hold the rest:

| Module | Holds | Documented on |
| ------------------ | ------------------------------------------------------- | ------------------------- |
| `mathspec.spec` | what the file says: `Spec` and its blocks | [Spec API](spec.md) |
| `mathspec.program` | what the file means: `Program`, its nodes, and `Advice` | [Program API](program.md) |
| `mathspec.errors` | what you catch: the error tree, and `did_you_mean` | [Errors](#errors) below |

<!-- prettier-ignore-start -->

## Loading

The module `mathspec.program` holds the classes a `Program` is made of. The
[Program API](program.md) documents them.

::: mathspec.to_spec
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3

::: mathspec.Spec
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3

## Composing

[Compose a spec from several files](../howto/compose.md) shows both in use.
Expand Down Expand Up @@ -75,7 +72,7 @@ The module `mathspec.program` holds the classes a `Program` is made of. The
show_root_toc_entry: true
heading_level: 3

::: mathspec.FORMATS
::: mathspec.FormatName
options:
show_root_heading: true
show_root_toc_entry: true
Expand All @@ -89,62 +86,21 @@ The module `mathspec.program` holds the classes a `Program` is made of. The

## Advice

::: mathspec.advice
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3

::: mathspec.Advice
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3
`advice` returns a tuple of [`Advice`](program.md#mathspec.program.Advice).

::: mathspec.AdviceKind
::: mathspec.advice
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3

## Errors

::: mathspec.MathSpecError
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3

::: mathspec.LanguageError
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3

::: mathspec.SchemaError
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3

::: mathspec.DimensionError
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3

## Names

::: mathspec.BUILTIN_NAMES
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3

::: mathspec.did_you_mean
::: mathspec.errors
options:
show_root_heading: true
show_root_toc_entry: true
heading_level: 3
members_order: source

<!-- prettier-ignore-end -->
2 changes: 1 addition & 1 deletion docs/reference/language/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Check for typos, or ensure 'p_charge' is declared.

## What `advice` warns about

`ms.advice(spec)` returns a tuple of `ms.Advice`, one per warning, and
`ms.advice(spec)` returns a tuple of `mathspec.program.Advice`, one per warning, and
`python -m mathspec check spec.yaml` prints them. Advice is a warning: the file
loads.

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/language/piecewise.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ expansion writes that the file already declares is refused at load too.
## Writing a formulation out

Writing a formulation out replaces the block with the variables and constraints
it states. [`Spec.expand()`](../api.md#mathspec.Spec.expand) is the
it states. [`Spec.expand()`](../spec.md#mathspec.spec.Spec.expand) is the
call, and [see what a curve or a set expands to](../../howto/see-an-expansion.md)
shows a spec before and after.

Expand Down
4 changes: 2 additions & 2 deletions docs/reference/program.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ SPDX-License-Identifier: CC-BY-4.0
# Program API

This page documents every name that `mathspec.program` exports: the
declarations, the expression and predicate nodes, and the reports a program
answers. [Reading a spec and its program](reading.md) says how they fit together.
declarations, the expression and predicate nodes, the reports a program
answers, and the `Advice` that [`advice`](api.md#mathspec.advice) returns. [Reading a spec and its program](reading.md) says how they fit together.

<!-- prettier-ignore-start -->

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/reading.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ is one the file declared.
## Formulations written out

A program holds each curve and each set as one declaration until
[`Spec.expand()`](api.md#mathspec.Spec.expand) writes it out. An engine that
[`Spec.expand()`](spec.md#mathspec.spec.Spec.expand) writes it out. An engine that
builds rows reads the program of `spec.expand('piecewise')` if it takes a set,
and the program of `spec.expand()` if it does not. The program of an expansion
holds no curve:
Expand Down
23 changes: 23 additions & 0 deletions docs/reference/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!--
SPDX-FileCopyrightText: mathspec contributors
SPDX-License-Identifier: CC-BY-4.0
-->

# Spec API

This page documents every name that `mathspec.spec` exports: `Spec`, the
blocks its sections hold, and the operator names an expression may call.
[`to_spec`](api.md#mathspec.to_spec) returns a `Spec`.
[Reading a spec and its program](reading.md) says how a spec and its program
fit together.

<!-- prettier-ignore-start -->

::: mathspec.spec
options:
show_root_heading: false
show_root_toc_entry: false
heading_level: 2
members_order: alphabetical

<!-- prettier-ignore-end -->
2 changes: 1 addition & 1 deletion docs/reference/typeset.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ a flag. The [Python API](api.md#typesetting) gives each signature.
- The spec's `description:` opens the document.
- A `piecewise:` block prints as one line: the curve it states, over the frame
it states one curve per coordinate of. To print its rows, print
[`spec.expand()`](api.md#mathspec.Spec.expand) or pass `--expand`
[`spec.expand()`](spec.md#mathspec.spec.Spec.expand) or pass `--expand`
([see an expansion](../howto/see-an-expansion.md)).
- An [`assumptions:`](language/assumptions.md) entry prints under an
**Assumptions** heading, last, beside what each curve assumes of its
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ nav:
- Development:
- Building on mathspec:
- Reading a spec and its program: reference/reading.md
- Spec API: reference/spec.md
- Program API: reference/program.md
- What counts as language: about/what-counts-as-language.md
- Contributing:
Expand Down
2 changes: 1 addition & 1 deletion schema/mathspec.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,7 @@
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "The declared math \u2014 one YAML file, or one dict, validated. Nothing here has seen data.\n\nA ``Spec`` that exists has passed the whole language: constructing one by\nany route \u2014 ``to_spec``, [`model_validate`][], the constructor \u2014 runs\nevery load-time check, expression pass included, and raises\n[`LanguageError`][] on a spec the language refuses.\nHolding one is the proof, so nothing downstream checks it again.\n\nThe API is the thirteen declaration sections plus ``version`` and\n``description``, three ways back out \u2014 [`to_dict`][] for the spec as\ndata, [`to_yaml`][] for the file a reviewer reads, [`expand`][] for the\nspec with its formulations written out as plain rows \u2014 and [`program`][], the\nspec typed, which every reader after load walks. Everything else on this\nclass is pydantic's, not a contract this package keeps.",
"description": "The declared math \u2014 one YAML file, or one dict, validated. Nothing here has seen data.\n\nA ``Spec`` that exists has passed the whole language: constructing one by\nany route \u2014 ``to_spec``, [`model_validate`][], the constructor \u2014 runs\nevery load-time check, expression pass included, and raises\n[`LanguageError`][mathspec.errors.LanguageError] on a spec the language refuses.\nHolding one is the proof, so nothing downstream checks it again.\n\nThe API is the thirteen declaration sections plus ``version`` and\n``description``, three ways back out \u2014 [`to_dict`][] for the spec as\ndata, [`to_yaml`][] for the file a reviewer reads, [`expand`][] for the\nspec with its formulations written out as plain rows \u2014 and [`program`][], the\nspec typed, which every reader after load walks. Everything else on this\nclass is pydantic's, not a contract this package keeps.",
"properties": {
"assumptions": {
"additionalProperties": {
Expand Down
38 changes: 18 additions & 20 deletions src/mathspec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,26 @@

"""The language: what a YAML file may say, and what it means.

Two public states — a [`Spec`][] is what the file *says*,
and its [`program`][mathspec.spec.Spec.program] is what it *means* — and
[`to_spec`][], the one door to both. Everything between them — both grammars
and the tree they build — is package-private, because a consumer reads a
program instead.
The top level is what a consumer calls: [`to_spec`][], the one door to a
file, and the verbs over what it returns. The rest of the surface is three
modules, each with one rule:

- [`mathspec.spec`][] is what the file *says*: [`Spec`][mathspec.spec.Spec]
and the blocks it holds.
- [`mathspec.program`][] is what the file *means*: the
[`Program`][mathspec.program.Program] a spec lowers to, its nodes, and the
[`Advice`][mathspec.program.Advice] the language gives about it.
- [`mathspec.errors`][] is what a consumer catches.

Everything between them — both grammars and the tree they build — is
package-private, because a consumer reads a program instead.
"""

from mathspec import program
from mathspec import errors, program, spec
from mathspec.advising import advice
from mathspec.composition import merge, override
from mathspec.errors import Advice, AdviceKind, DimensionError, LanguageError, MathSpecError, SchemaError, did_you_mean
from mathspec.operators import BUILTIN_NAMES
from mathspec.spec import Spec
from mathspec.typesetting import (
FORMATS,
FormatName,
SymbolTable,
to_latex,
to_markdown,
Expand All @@ -29,21 +34,14 @@
from mathspec.validation import to_spec

__all__ = [
'BUILTIN_NAMES',
'FORMATS',
'Advice',
'AdviceKind',
'DimensionError',
'LanguageError',
'MathSpecError',
'SchemaError',
'Spec',
'FormatName',
'SymbolTable',
'advice',
'did_you_mean',
'errors',
'merge',
'override',
'program',
'spec',
'to_latex',
'to_markdown',
'to_spec',
Expand Down
3 changes: 1 addition & 2 deletions src/mathspec/advising.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
from typing import TYPE_CHECKING

from mathspec.boundedness import unbounded_notes
from mathspec.errors import Advice
from mathspec.program import Join, Program, walk
from mathspec.program import Advice, Join, Program, walk
from mathspec.validation import to_spec

if TYPE_CHECKING:
Expand Down
2 changes: 1 addition & 1 deletion src/mathspec/boundedness.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

from typing import TYPE_CHECKING, Literal, assert_never

from mathspec.errors import Advice
from mathspec.program import (
Add,
Advice,
Cases,
Constant,
Divide,
Expand Down
32 changes: 4 additions & 28 deletions src/mathspec/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,21 @@
#
# SPDX-License-Identifier: MIT

"""What the language says back about a file: the errors it raises, and the advice it gives."""
"""What the language raises: the error tree, and the one wording a consumer's own refusals share."""

from __future__ import annotations

import difflib
from dataclasses import dataclass
from typing import TYPE_CHECKING, Literal
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from collections.abc import Iterable

from pydantic import ValidationError


#: Which pass an [`Advice`][] comes from. Closed, like the operator set: a
#: consumer filtering on it can enumerate every value.
AdviceKind = Literal['never-an-axis', 'given', 'unbounded']


@dataclass(frozen=True)
class Advice:
"""One thing the language advises about a file it accepts.

Never an error: each is what a half-written spec looks like too. A
consumer prints it, or filters on ``kind`` and ``subject``; the text is the
language's, so no consumer writes its own.

Attributes:
kind: The pass that said it.
subject: The declaration it is about — a dimension name, a variable name.
text: The sentence, naming the rewrite.
"""

kind: AdviceKind
subject: str
text: str

def __str__(self) -> str:
return self.text
#: What ``mathspec.errors`` promises a consumer.
__all__ = ['DimensionError', 'LanguageError', 'MathSpecError', 'SchemaError', 'did_you_mean']


class MathSpecError(ValueError):
Expand Down
Loading
Loading