save_zip can emit one companion twice under two spellings when the source folder folds case and the temp dir does not
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 72/100
Línea de trabajo
Comienza en src/sil_lift/_zip.py, en save_zip, y sigue _write_zip para entender cómo entran los archivos complementarios copiados y renderizados en el directorio de staging. Reproduce las condiciones de una fuente con case-folding y de un TMPDIR case-sensitive descritas en el Issue, y verifica después que el archivo contenga un único miembro .lift-ranges con los bytes renderizados y ninguna variante antigua de mayúsculas/minúsculas.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Lexicon.save_zip() can produce an archive containing the same .lift-ranges twice — once stale, once current — under names differing only in case.
Preconditions
Both must hold, which is why this is not seen in ordinary use:
- The LIFT folder is on a case-folding filesystem, and its
.liftand companion disagree in case (Dict.LIFTbesideDict.lift-ranges). This is what makesRangesFile.pathcarry a spelling that is not the on-disk name —_existing_filereturns the candidate's spelling when the exact stat succeeds, and on a folding filesystem it succeeds for a case variant. TMPDIRis on a case-sensitive filesystem. On macOS that is a case-sensitive APFS volume; on Linux, a LIFT folder on ciopfs / exfat / casefold-enabled ext4 with/tmpon ext4.
Normally these are the same filesystem — the divergence needs folding, and staging happens on the same machine — so the second write lands on the first file and nothing goes wrong.
Mechanism
save_zip (src/sil_lift/_zip.py) stages into a TemporaryDirectory, copies the whole source package in, then overwrites the rendered files by name:
shutil.copytree(source_root, content, dirs_exist_ok=True) # brings in Dict.lift-ranges
...
name = ranges_file.path.name if ranges_file.path is not None else Path(key).name
(content / name).write_bytes(render_ranges_document(ranges_file)) # writes Dict.LIFT-ranges
_write_zip then walks the staging directory, so members come from the filesystem rather than from the names. Where staging folds case, the second write overwrites the copied file and leaves its directory entry alone: one file, one member, correct bytes. Where staging does not fold, it creates a second file, and the archive gets both — the stale copytree'd bytes under the on-disk spelling and the rendered bytes under the href's spelling.
On read-back the duplicate is not rejected: _select_lift_member only guards against multiple .lift members, and two case-variant .lift-ranges are resolved the way any such folder is — one wins.
Fix options
- Narrow, in
save_zip: before writing, look for a file already in staging whose name folds ontonameand write to that spelling instead. Keeps the change local to packaging. - At the root, in
_resolve_ranges: handRangesFile.loadthe canonical path soRangesFile.pathalways matches what is on disk. Tempting one-liner, but the canonical path is absolute and symlink-collapsed, so it changes a public attribute's value and whatRangesFile.save()targets for every caller — a much wider blast radius than the defect.
The first looks right; the second is worth ruling out deliberately rather than by omission.
Notes
Pre-existing: _resolve_ranges passed the candidate's spelling to RangesFile.load before #19 as well. Split out of review discussion on #19, which kept its scope to companion resolution.
- Lenguaje dominante
- Python
- Estrellas
- 1
- Forks
- 0
- Merge medio
- 11 d 8 h
- PR fusionados (30 d)
- 3
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de sillsdev/python-sil-lift
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
sillsdev/python-sil-lift#15 ·
-
bug
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
sillsdev/python-sil-lift#45 ·
-
bug
Dificultad 3/5 1-2 días Aptitud para principiantes 72/100
sillsdev/python-sil-lift#33 ·
-
Evaluate full API surface for anything unnecessaryPosiblemente ocupada @imnasnainaec la tomó hace 18 días. Abierto
Dificultad 5/5 Más de una semana Aptitud para principiantes 25/100
sillsdev/python-sil-lift#30 · 1 comentario · 1 asignado ·
-
bug
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
sillsdev/python-sil-lift#29 · 1 comentario ·
Todos los issues de sillsdev/python-sil-lift
Issues similares
-
fix(accounts): Balance Sheet and Profit and Loss crash on PostgreSQL due to MySQL force_indexAbierto
Dificultad 1/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
Discord webhook notifications fail with HTTP 403 (Cloudflare blocks default urllib User-Agent)Abierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 85/100
tharje/spooler-v2#20 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
Los mantenedores suelen responder en 1 día
-
Bump version of azure-servicebusPosiblemente ocupada @joostlek la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
home-assistant/core#186275 ·
Los mantenedores suelen responder en 1 día
-
documentation
Dificultad 1/5 Menos de una hora Aptitud para principiantes 72/100