Spaces:
Sleeping
Sleeping
Download tei_annotator/postprocessing/validator.py from cmboulanger/tei-annotator: direct link, hf CLI and curl.
- Browser
- Download file 2.12 kB
-
https://huggingface.co/spaces/cmboulanger/tei-annotator/resolve/main/tei_annotator/postprocessing/validator.py
- Command line
-
hf download hf://spaces/cmboulanger/tei-annotator/tei_annotator/postprocessing/validator.py
-
curl -L -o validator.py https://huggingface.co/spaces/cmboulanger/tei-annotator/resolve/main/tei_annotator/postprocessing/validator.py
2.12 kB
| from __future__ import annotations | |
| import difflib | |
| import re | |
| from ..models.schema import TEISchema | |
| from ..models.spans import ResolvedSpan | |
| def validate_spans( | |
| spans: list[ResolvedSpan], | |
| schema: TEISchema, | |
| source: str, | |
| ) -> list[ResolvedSpan]: | |
| """ | |
| Filter out spans that fail schema validation. | |
| Rejected when: | |
| - element is not in the schema | |
| - an attribute name is not listed for that element | |
| - an attribute value is not in the element's allowed_values (when constrained) | |
| - span bounds are out of range | |
| """ | |
| valid: list[ResolvedSpan] = [] | |
| for span in spans: | |
| # Bounds sanity check | |
| if span.start < 0 or span.end > len(source) or span.start >= span.end: | |
| continue | |
| elem = schema.get(span.element) | |
| if elem is None: | |
| continue # element not in schema | |
| allowed_names = {a.name for a in elem.attributes} | |
| attr_ok = True | |
| for attr_name, attr_value in span.attrs.items(): | |
| if attr_name not in allowed_names: | |
| attr_ok = False | |
| break | |
| attr_def = next((a for a in elem.attributes if a.name == attr_name), None) | |
| if attr_def and attr_def.allowed_values is not None: | |
| if attr_value not in attr_def.allowed_values: | |
| attr_ok = False | |
| break | |
| if not attr_ok: | |
| continue | |
| valid.append(span) | |
| return valid | |
| def validate_output(xml: str, source: str) -> None: | |
| """Raise ValueError if xml text content differs from source (tags stripped, whitespace normalised).""" | |
| def _norm(t: str) -> str: | |
| return re.sub(r'\s+', ' ', t).strip() | |
| got = _norm(re.sub(r'<[^>]+>', '', xml)) | |
| expected = _norm(source) | |
| if got != expected: | |
| diff = '\n'.join( | |
| difflib.unified_diff( | |
| expected.split(), got.split(), | |
| fromfile='source', tofile='injected', | |
| ) | |
| ) | |
| raise ValueError( | |
| f"inject_xml text content mismatch — annotated output differs from source.\n{diff}" | |
| ) | |