diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ae6904..8341c59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/docs/en/guides/validate.md b/docs/en/guides/validate.md index 71346d1..e57bc72 100644 --- a/docs/en/guides/validate.md +++ b/docs/en/guides/validate.md @@ -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") diff --git a/src/sil_lift/__init__.py b/src/sil_lift/__init__.py index d86c110..c40d0e1 100644 --- a/src/sil_lift/__init__.py +++ b/src/sil_lift/__init__.py @@ -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 ( @@ -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 @@ -59,7 +59,6 @@ "LiftError", "LiftParseError", "LiftReader", - "LiftValidationError", "LiftWriteError", "LiftWriter", "MediaRef", @@ -87,7 +86,6 @@ "load", "open_reader", "open_writer", - "validate_file", ] diff --git a/src/sil_lift/_errors.py b/src/sil_lift/_errors.py index fb52841..6d8bc7c 100644 --- a/src/sil_lift/_errors.py +++ b/src/sil_lift/_errors.py @@ -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): @@ -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 diff --git a/src/sil_lift/_validate.py b/src/sil_lift/_validate.py index 5af34bb..8bdfce5 100644 --- a/src/sil_lift/_validate.py +++ b/src/sil_lift/_validate.py @@ -41,7 +41,7 @@ from lxml import etree -from ._errors import LiftValidationError, LiftWriteError +from ._errors import LiftWriteError from ._model import ( GrammaticalInfo, Lexicon, @@ -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") @@ -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 diff --git a/tests/test_validate.py b/tests/test_validate.py index aa1bd0a..86a8a2a 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -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" @@ -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",