Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

add_ranges_file appends a second header reference when the companion spells the range id in another normalization

オープン
#33 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
72/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
静か
技術スタック
python
領域
backend

調査の方向性

src/sil_lift/_model.py:825-829 の add_ranges_file から始め、その参照検索を src/sil_lift/_validate.py:432 の NFC 解決クロージャと比較します。NFC/NFD の範囲 ID 用に手書きのリグレッションケースを追加し、既存のヘッダーの綴りを維持しながら重複した参照を避けます。save() が 1 つの参照を書き込み、検証動作が変わらなければ完了です。

索引モデルが issue の本文から書いたものです。

説明

bug

Summary

Lexicon.add_ranges_file decides which ranges the header already references by exact string
comparison, so a header that spells a range id in NFC and a companion that spells it in NFD
are treated as unrelated ranges: a second <range> header reference is appended for the
same conceptual range, and save() writes both. FLEx mixes normalizations between an id and
the references to it (#14, #27), so a lexicon derived from a real export is exactly where
the two spellings meet.

Reproduction

import sil_lift, unicodedata

nfd = lambda s: unicodedata.normalize("NFD", s)
name = "Catégorie"

lex = sil_lift.Lexicon()
lex.header.ranges.append(sil_lift.Range(id=name, href="dup.lift-ranges"))  # NFC in the header
ranges = sil_lift.RangesFile()
ranges.add_range(nfd(name)).add_element("Nom")                             # NFD in the companion
lex.add_ranges_file(ranges, href="dup.lift-ranges")

print([ascii(r.id) for r in lex.header.ranges])
lex.save("dup.lift")
["'Cat\\xe9gorie'", "'Cate\\u0301gorie'"]

and in the saved .lift:

<ranges>
  <range id="Catégorie" href="dup.lift-ranges"/>
  <range id="Catégorie" href="dup.lift-ranges"/>
</ranges>

Two references, rendering identically, differing only by normalization — one of which the
caller never asked for.

Cause

src/sil_lift/_model.py:825-829:

referenced = {range_.id for range_ in self.header.ranges}
for range_ in ranges_file.ranges:
    if range_.id not in referenced:
        self.header.ranges.append(Range(id=range_.id, href=href))
        referenced.add(range_.id)

The membership test is exact, so the two spellings of one name are different keys.

Expected

Resolve referenced the way the validator resolves a name to an id — exact spelling first,
then NFC — and append nothing when the header already references the range under either
spelling. The existing header id must not be rewritten: whichever spelling the document came
with is the one it keeps.

Scope

Write path only. Validation of such a document is already correct as of #28, which resolves
a header range/@id against a companion's range id under NFC and reports the split as a
normalization-mismatch warning. This is about what save() then writes.

Distinct from #29: that is the merged read view (all_ranges()) dropping a range when two
companions define the same id. Same underlying theme — ids compared as exact strings — but a
different code path and a different symptom. See also the all_ranges() NFC-keying note on
that issue.

Notes

Pre-existing; not introduced by #28.

Reachability is narrow: it needs a header that already references the range under one
spelling plus a call to add_ranges_file with a companion spelling it the other way — the
documented "call again to reference ranges added later" flow on a FLEx-derived lexicon. No
corpus fixture exercises it, so this needs a hand-authored case.

One design decision comes with it: the package's only NFC machinery is three closures inside
_semantic_problems (src/sil_lift/_validate.py:432). Fixing this needs either a second
unicodedata.normalize call in _model.py or promoting nfc/resolve into a shared
private module. The latter is probably right if any other module ever needs it, but it is
worth deciding deliberately rather than inlining a call a later refactor has to undo.

主要言語
Python
スター
1
フォーク
0
平均マージ
11日 8時間
マージ済み PR(30日)
3

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

sillsdev/python-sil-lift のほかの issue

sillsdev/python-sil-lift の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。