Skip to content
Open
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
15 changes: 7 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,13 @@ releases may contain breaking changes.
Extraction is capped at 100,000 members and 10 GiB (the whole package for a
full extraction, the `.lift` alone for a streaming one), and refuses members
whose paths escape the extraction directory.
- Validation: `validate_file()` / `iter_problems()` /
`Lexicon.iter_problems()` returning a `Problem` stream, each carrying the
file, entry, and line it concerns. RELAX NG layer with two documented
departures from strict validation (invalid `file://` hrefs downgraded to
`uri-not-rfc` warnings; legal interleaving not falsely flagged); vendored
ranges schema over companions; and thirteen semantic checks, one `Problem`
code each (with missing-id opt-in via `require_ids`). Every code is
described in `docs/en/guides/validate.md`.
- Validation: `iter_problems()` / `Lexicon.iter_problems()` returning a
`Problem` stream, each carrying the file, entry, and line it concerns. RELAX
NG layer with two documented departures from strict validation (invalid
`file://` hrefs downgraded to `uri-not-rfc` warnings; legal interleaving not
falsely flagged); vendored ranges schema over companions; and thirteen
semantic checks, one `Problem` code each (with missing-id opt-in via
`require_ids`). Every code is described in `docs/en/guides/validate.md`.
- Canonical sort: `Lexicon.sort()` / `RangesFile.sort()` (entries by
case-folded guid/id, ranges/range-elements by id, field definitions by tag;
informed by the C# LiftSorter, locale-independent) and
Expand Down
5 changes: 3 additions & 2 deletions docs/en/guides/validate.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ for problem in sil_lift.iter_problems("dictionary.lift"):
print(problem)
# error [dangling-ref] dictionary.lift:88 (entry apu): ref 'nope' matches ...

# Fail-fast: raises LiftValidationError on the first error-level problem.
sil_lift.validate_file("dictionary.lift")
# Fail-fast: stop at the first error-level problem.
if any(p.level == "error" for p in sil_lift.iter_problems("dictionary.lift")):
raise SystemExit(1)

# In-memory state (serializes first — a documented cost on large lexicons):
lex = sil_lift.load("dictionary.lift")
Expand Down
6 changes: 2 additions & 4 deletions src/sil_lift/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from typing import TYPE_CHECKING

from ._canonical import canonicalize
from ._errors import LiftError, LiftParseError, LiftValidationError, LiftWriteError
from ._errors import LiftError, LiftParseError, LiftWriteError
from ._extras import Extras
from ._header import FieldDefinition, Header, Range, RangeElement
from ._model import (
Expand All @@ -36,7 +36,7 @@
)
from ._stream import LiftReader, LiftWriter, open_reader, open_writer
from ._text import Annotation, Form, Multitext, Span, Text, Trait
from ._validate import Problem, iter_problems, validate_file
from ._validate import Problem, iter_problems

if TYPE_CHECKING:
import os
Expand All @@ -59,7 +59,6 @@
"LiftError",
"LiftParseError",
"LiftReader",
"LiftValidationError",
"LiftWriteError",
"LiftWriter",
"MediaRef",
Expand Down Expand Up @@ -87,7 +86,6 @@
"load",
"open_reader",
"open_writer",
"validate_file",
]


Expand Down
16 changes: 1 addition & 15 deletions src/sil_lift/_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from ._validate import Problem

__all__ = ["LiftError", "LiftParseError", "LiftValidationError", "LiftWriteError"]
__all__ = ["LiftError", "LiftParseError", "LiftWriteError"]


class LiftError(Exception):
Expand All @@ -25,12 +20,3 @@ class LiftParseError(LiftError):

class LiftWriteError(LiftError):
"""An in-memory document holds content that XML cannot represent, so it cannot be written."""


class LiftValidationError(LiftError):
"""Raised by the fail-fast validation wrappers on the first error-level
:class:`~sil_lift.Problem` (warnings never raise)."""

def __init__(self, problem: Problem) -> None:
super().__init__(str(problem))
self.problem = problem
11 changes: 2 additions & 9 deletions src/sil_lift/_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

from lxml import etree

from ._errors import LiftValidationError, LiftWriteError
from ._errors import LiftWriteError
from ._model import (
GrammaticalInfo,
Lexicon,
Expand All @@ -61,7 +61,7 @@
from ._header import Range
from ._model import MediaResolution

__all__ = ["Problem", "iter_problems", "validate_file"]
__all__ = ["Problem", "iter_problems"]

_T = TypeVar("_T")

Expand Down Expand Up @@ -98,13 +98,6 @@ def iter_problems(path: str | os.PathLike[str], *, require_ids: bool = False) ->
return Lexicon.load(path).iter_problems(require_ids=require_ids)


def validate_file(path: str | os.PathLike[str]) -> None:
"""Raise :class:`LiftValidationError` on the first error-level problem."""
for problem in iter_problems(path):
if problem.level == "error":
raise LiftValidationError(problem)


def iter_lexicon_problems(lexicon: Lexicon, *, require_ids: bool = False) -> Iterator[Problem]:
from ._writer import render_document, render_ranges_document

Expand Down
13 changes: 1 addition & 12 deletions tests/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pytest

import sil_lift
from sil_lift import GrammaticalInfo, LiftValidationError, Problem, Trait
from sil_lift import GrammaticalInfo, Problem, Trait

CORPUS_DIR = Path(__file__).parent / "corpus"
NEGATIVE_DIR = CORPUS_DIR / "negative"
Expand Down Expand Up @@ -519,17 +519,6 @@ def test_undefined_semantic_domain_value_is_flagged() -> None:
assert "9.9.9" in flagged[0].message


def test_validate_file_raises_on_first_error() -> None:
with pytest.raises(LiftValidationError) as info:
sil_lift.validate_file(NEGATIVE_DIR / "duplicate-guid.lift")
assert info.value.problem.code == "duplicate-guid"


def test_validate_file_passes_on_warning_only_files() -> None:
sil_lift.validate_file(NEGATIVE_DIR / "flex-quirks.lift")
sil_lift.validate_file(NEGATIVE_DIR / "duplicate-form-lang.lift")


CLEAN = [
"spec-examples/0.13/full-entry.lift",
"spec-examples/0.13/subsenses.lift",
Expand Down
Loading