| 1 |
"""Typed parser errors with `file:line:col` location reporting. |
| 2 |
|
| 3 |
All subclasses of `DlmParseError` carry `(path, line, col)` so CLI error |
| 4 |
reporting can format diagnostics uniformly. |
| 5 |
""" |
| 6 |
|
| 7 |
from __future__ import annotations |
| 8 |
|
| 9 |
from pathlib import Path |
| 10 |
|
| 11 |
|
| 12 |
class DlmParseError(ValueError): |
| 13 |
"""Base class for all `.dlm` parse errors. |
| 14 |
|
| 15 |
`line` is 1-indexed. `col` is 1-indexed or 0 when column info isn't |
| 16 |
meaningful (e.g., schema validation errors on whole values). |
| 17 |
""" |
| 18 |
|
| 19 |
def __init__( |
| 20 |
self, |
| 21 |
message: str, |
| 22 |
*, |
| 23 |
path: Path | None = None, |
| 24 |
line: int = 0, |
| 25 |
col: int = 0, |
| 26 |
) -> None: |
| 27 |
self.message = message |
| 28 |
self.path = path |
| 29 |
self.line = line |
| 30 |
self.col = col |
| 31 |
super().__init__(self._format()) |
| 32 |
|
| 33 |
def _format(self) -> str: |
| 34 |
where = str(self.path) if self.path is not None else "<text>" |
| 35 |
if self.line and self.col: |
| 36 |
return f"{where}:{self.line}:{self.col}: {self.message}" |
| 37 |
if self.line: |
| 38 |
return f"{where}:{self.line}: {self.message}" |
| 39 |
return f"{where}: {self.message}" |
| 40 |
|
| 41 |
|
| 42 |
class FrontmatterError(DlmParseError): |
| 43 |
"""YAML-level errors in the frontmatter block (missing delimiters, bad YAML).""" |
| 44 |
|
| 45 |
|
| 46 |
class SchemaValidationError(DlmParseError): |
| 47 |
"""Pydantic validation failure against the frontmatter schema. |
| 48 |
|
| 49 |
Unknown keys, wrong types, out-of-range values all land here. |
| 50 |
""" |
| 51 |
|
| 52 |
|
| 53 |
class DlmVersionError(DlmParseError): |
| 54 |
"""`dlm_version` is present but isn't a version this parser implements. |
| 55 |
|
| 56 |
The migration framework is the home for promoting older |
| 57 |
documents; this error's message points there. |
| 58 |
""" |
| 59 |
|
| 60 |
|
| 61 |
class UnsupportedMigrationError(DlmVersionError): |
| 62 |
"""No migrator is registered for the requested `from_version`. |
| 63 |
|
| 64 |
Raised by `dlm.doc.migrations.dispatch.apply_pending` when the |
| 65 |
`MIGRATORS` registry lacks an entry for an intermediate version on |
| 66 |
the path from `raw.dlm_version` to `CURRENT_SCHEMA_VERSION`. The |
| 67 |
coverage test catches this at commit time; this error catches the |
| 68 |
"user forked off the migration path manually" case at runtime. |
| 69 |
""" |
| 70 |
|
| 71 |
|
| 72 |
class FenceError(DlmParseError): |
| 73 |
"""Malformed or unknown section fence in the body.""" |
| 74 |
|
| 75 |
|
| 76 |
class InstructionGrammarError(DlmParseError): |
| 77 |
"""Malformed `### Q` / `### A` structure inside an instruction section.""" |
| 78 |
|
| 79 |
|
| 80 |
class PreferenceGrammarError(DlmParseError): |
| 81 |
"""Malformed `### Prompt` / `### Chosen` / `### Rejected` structure.""" |