Initial implementation#1
Draft
imnasnainaec wants to merge 34 commits into
Draft
Conversation
… skeleton) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sources pinned by SHA in PROVENANCE.md files. Corpus bytes are normalization-exempt via .gitattributes (fidelity tests depend on it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…anity tests Migration validated against the vendored RNG; four fixtures are documented as inherently RNG-invalid (lang-less etymology forms; FLEx file://C:/ range hrefs that fail libxml2's anyURI check - see PROVENANCE.md, affects design). All local checks green: ruff, ruff format, mypy --strict, 74 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 35 RNG elements as slotted dataclasses (quirks honored: two field variants, form-shaped gloss, three-way extensibility split). Parser never rejects well-formed 0.13: out-of-schema content lands in per-node opaque Extras residue (unknown attrs/elements, comments, PIs, stray text, malformed typed attributes). Version guard names the version. 128 tests, all checks green (ruff, mypy --strict). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Canonical serializer (custom indenter that never touches mixed content - lxml pretty_print corrupts <text> whitespace, caught on the Sango corpus); byte-span scanner for top-level entry/header spans; unchanged documents save byte-identically, untouched entries stay verbatim under edits, touched entries re-serialize canonically with residue re-emitted at clamped anchors. Hypothesis round-trip properties (caught the empty-fragment <text></text> vs <text/> asymmetry); no-lxml-leakage public API test; fidelity contract documented (README, docs/en/fidelity.md). 187 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RangesFile with the same passthrough fidelity as .lift (per-range verbatim reuse); companion discovery (sibling + href-basename fallback for FLEx's dangling file://C:/ hrefs); save() writes companions together; all_ranges() merged view; media_refs()/missing_media() handling backslash hrefs with spaces (WeSay). Authored lift-ranges-0.13.rng from the vendored grammar's defines - validates the hand-crafted pair; FLEx files fail it (trait/field inside range-element, third documented FLEx deviation, carried in Extras). 200 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Problem stream with file/entry/line addressing; RNG layer with two deliberate deviations from raw libxml2 (documented in _validate.py): href masking (anyURI false-flags every FLEx file; sil-lift reports uri-not-rfc warnings instead) and tag-grouped validation (libxml2 rejects legal interleave once an element type appears in >1 run - minimal repro: 'f n f n' invalid, 'f f n n' valid). Ranges-schema layer over companions; semantic checks: duplicate-guid, dangling-ref (entry id/guid/sense id targets), range-parent integrity, undefined-range-value (NFC-normalized: FLEx writes .lift in NFC but .lift-ranges in NFD), duplicate-form-lang (the RNG's Schematron rule), missing-media. Finds real defects in the Sango export (2 dangling range parents, 1 undefined POS value). Fail-fast wrappers raise on first error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Eight hand-authored fixtures, each documenting its defect and expected Problem; tests verify addressing (entry id/guid/line), warning-vs-error levels, fail-fast behavior, clean-corpus cleanliness, C# parity case (duplicate-guid), and the real Sango defects (2 dangling range parents, 1 undefined POS). 263 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native implementation mirroring C# LiftSorter's rules (consulted at libpalaso 4840de8): entries by case-insensitive guid (extended to (guid,id) casefold so guidless entries sort deterministically), header ranges/ range-elements by id, field definitions by tag, senses in file order, text/span whitespace untouched. Lexicon.sort()/RangesFile.sort() in-place; canonicalize(src, dst) for diff-ready fully-canonical output. Acceptance: idempotent, byte-deterministic, sorted output RNG-valid + semantically equal, sort+save keeps untouched entry bytes verbatim. 275 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LiftReader (iterparse; header parsed eagerly, entries lazy, clear()/sibling cleanup internal, version guard at open) and LiftWriter (canonical chunk per entry; output byte-identical to canonical_document by construction; exception leaves the file visibly unterminated). Same Entry types as full mode; full-vs-streaming model equality verified over the whole corpus; stream-copy of Sango preserves models; bounded memory measured: 400k-entry / ~340MB file stream-copied with <100MB working-set growth (40k-entry variant in the default suite). Extras anchor index excluded from equality (position hint, not content). 313 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sil-lift CLI (validate/stats/sort/check-media; stdlib argparse; exit codes 0/1/2) exercising every scope pillar, verified against the corpus incl. the filename-with-space fixture. Docs: task-oriented guides (read/edit/write, validate, large files, folder+media, CLI), fidelity page, mkdocstrings API reference; mkdocs build --strict green. Wheel built and verified in a fresh Python 3.12 venv (import, load, CLI). 323 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
export: LIFT -> CSV/TSV, one row per leaf sense (a sense with subsenses is a grouping node), streaming with two-pass language auto-detection or --langs; 6 new tests. Docs: guides/bulk-edit-glosses.md with a runnable, verified script (load -> edit glosses incl. subsenses -> validate-gate -> save) and the per-entry fidelity story; nav/index wired. 329 tests green; mkdocs build --strict green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Least-privilege workflow permissions (contents: read); one action per step, every step named; lint and format checks as separate steps; build matrix on the oldest and newest supported Pythons (3.11, 3.14). Supply-chain pinning: actions by commit SHA (checkout v4.3.1, setup-python v5.6.0), build/twine by version (1.5.0 / 6.2.0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New ruff/mypy releases change behavior and break CI on innocent code, so they are pinned and bumped deliberately; the test tools keep floating floors so a breaking pytest/hypothesis/lxml release surfaces early. Also: lxml-stubs floor (>=0.5), alphabetized dev extra / docs extra / ruff select, condensed comments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
.editorconfig: UTF-8/LF/final-newline defaults, 2-space YAML/TOML, and a do-nothing carve-out for corpus fixtures and vendored schemas (mirrors .gitattributes) so editors can't corrupt byte-exact test payloads. mkdocs.yml/crowdin.yml: dedented sequence style; plugin order now i18n first, search last (indexes localized content) - the arrangement mkdocs-static-i18n recommends. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Docs tree: long-line prose (Crowdin-friendly, per new editorconfig rule), underscore emphasis, realigned tables; index.md guide list dropped (nav covers it). CONTRIBUTING.md added (dev setup, byte-exact corpus rules, mkdocs workflow) with a README pointer. mkdocs.yml: footer social links. .editorconfig: md indent groups, docs-tree overrides (4-space list indent for python-markdown, no wrap guidance), plus .vscode markdown ruler. Root markdown wrapped at 80. Code fences verified byte-identical; strict docs build and full check suite green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nsical Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rowdin sync note Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings (PR #1): iter_problems on a loaded-then-edited lexicon validated the original source bytes, hiding problems introduced by edits and mis-addressing semantic findings once entries were added, removed, or sorted. Render through the writer instead — byte-identical to the source for untouched documents, so file line numbers still hold. Also: check-media now skips remote/absolute hrefs via _normalize_href (mirrors missing_media); save() docstring covers same-directory renames; large-files.md notes root attributes are not carried in streaming. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The sdist shipped all of tests/corpus, redistributing fixtures whose upstream repos have no license (lift-standard, LiftTweaker) or an AGPL-by-reference one (flashgrab) in the PyPI artifact. PROVENANCE.md now records the review outcome and that corpus files are not covered by the repository's MIT license. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…None lines Review round 2 (PR #1): after a relocate-save the ranges_files dict stayed keyed by the companions' pre-save paths (latent — nothing reads keys after save today); _nearest_entry stopped scanning at an entry without a sourceline instead of skipping it (unreachable on lxml-parsed input). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both were several majors behind (v4->v7, v5->v6). Checked release notes: neither breaks plain checkout/setup-python usage with no special inputs, provided the runner is >= v2.327.1 -- true unconditionally for GitHub-hosted runners. upload-pages-artifact, deploy-pages, and gh-action-pypi-publish were already on latest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings: stats undercounted media (missed variant.pronunciations, media_refs already covered it); dangling-ref missed refs nested in variant.relations; duplicate-form-lang only checked 3 of ~14 Multitext fields (replaced with a generic dataclass-tree walk, which also catches Multitext nested inside Annotation.content -- unbounded, not just the missed fields). Also: dummy GUIDs in test fixtures were structurally invalid UUIDs (wrong version/variant nibbles). Repeating-digit blocks are kept for at-a-glance obviousness, with -4444-8888- inserted at the version/variant positions -- valid-by-scheme, distinguishable from real data by any grep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… blank line Docs and comments brought in line with the code: - README lists the export subcommand; docs build uses `mkdocs build --strict` - schemas/PROVENANCE.md: lift-ranges-0.13.rng is authored, not "planned" - cli.md stats sample shows the real sense count (4541); export = "leaf sense" - validate.md snippet defines `lex` before using it - csharp-differences.md: ranges validated as companions, no standalone entry - _cli.py drops the stale "decision-document section D" reference - test_stream.py drops the stale "implementation plan" reference - check.py docstring accurate; CONTRIBUTING documents the 90% coverage floor - _canonical.canonicalize docstring notes companions are not rewritten - _model.Lexicon.load docstring: candidates are all tried, not a "fallback" - _model find/save and RangesFile.save docstrings document their ValueError Behavior and robustness: - check.py runs pytest under --cov=sil_lift --cov-fail-under=90 (CI parity) - test_public_api no longer silently skips members whose annotations use TYPE_CHECKING-only names; it checks raw annotations for etree/lxml instead - _writer._slot_bytes: touched entries/headers/ranges no longer emit a blank line in the byte-passthrough path - _validate parses internally-rendered bytes with a hardened parser (resolve_entities=False, no_network=True) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aders - Canonical serialization (canonical_document / canonical_ranges_document) now guarantees a newline separator between chunks, so byte-reused (untouched) spans no longer glue to their neighbors when a structural edit (add/remove/reorder) routes through the canonical fallback. Pure-canonical output is unchanged, since its chunks already end in a newline. - Header gains ranges_extra / fields_extra: out-of-schema residue on the <ranges>/<fields> wrapper elements (unknown attributes, stray nodes) now stays on the wrapper instead of migrating up onto <header> during canonical re-serialization. The reader test is updated to assert the new attribution. - The duplicate-<header> guard tracks whether a header has been seen instead of testing Header truthiness, so an empty first <header/> is treated as the model header and a second <header> is preserved as residue rather than silently overwriting it. - AGENTS.md documents that the hard-wrapped root docs backslash-escape bare Markdown list markers (e.g. `sort \+ save`) so a reflow cannot turn them into stray bullets, and that docs/en/ (not hard-wrapped) needs no such escaping. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`sil-lift validate` now accepts: - `--format json`: emit findings as a single JSON object (a problems array plus an errors/warnings summary) instead of human text, for machine consumption. - `--strict`: treat warnings as errors, exiting 1 on any warning. - `--no-check-media`: skip the filesystem media-presence check, suppressing missing-media findings (noise when validating a freshly generated export whose media lives elsewhere). Document the flags and a JSON example in the CLI guide, note that validate's exit codes and JSON schema are a supported, SemVer-covered interface, and drop the "demo" framing from the CLI's description. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Commit messages should describe the diff and stand on their own, without referencing conversation, tasks, audits, or review context. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Support producing and validating LIFT exports. Construction API for building LIFT from scratch: - `Lexicon.add_ranges_file()` attaches a `.lift-ranges` companion and adds the header `<range href>` references; `save()` writes a path-less companion beside the target `.lift`. - `RangesFile.add_range()` and `Range.add_element()` builder helpers. - `open_writer(..., ranges=...)` emits the companion on the streaming path. Validation: - `dangling-ranges-href` warning when a relative header `range/@href` resolves to no companion (absolute/file:// FLEx-style hrefs are exempt). - `missing-id` errors for entries without a guid or senses without an id, opt-in via `iter_problems(require_ids=True)`. CLI: - `validate --require-ids`, `validate -` (read from stdin), and `stats --format json`. Container and GitHub Action wrapping `sil-lift validate` (`Dockerfile`, `action.yml`, `docker-entrypoint.sh`, container build workflow) so a non-Python CI pipeline can run the conformance check with no local Python toolchain. Docs: a "Producing conformant LIFT" guide covering the conformance gate, the `.lift-ranges` companion shape, and the multitext and XML-escaping rules. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`* text=auto` lets a Windows checkout rewrite `docker-entrypoint.sh` with CRLF, whose `\r` shebang fails in the Linux container. Force LF for `*.sh`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
README
https://github.com/sillsdev/python-sil-lift/tree/develop#sil-lift
Devin review
https://app.devin.ai/review/sillsdev/python-sil-lift/pull/1
Summary as of e5947db
Initial implementation of sil-lift, a Python library for LIFT (Lexicon Interchange Format) 0.13. The library provides:
Key architectural decisions:
This change is