`ZipFile.close()` writes wrong offsets after a failed `repack()`
- Lenguaje dominante
- Python
- Estrellas
- 77.2k
- Forks
- 35.9k
- Métricas de merge de PR
- Métricas de PR pendientes
Descripción
Bug description:
_ZipRepacker.repack() updates ZipInfo.header_offset for each member before moving that member's bytes, so a failing _copy_bytes() leaves the in-memory offsets describing a layout that was never written. remove() has already set _didModify, so a later normal close() commits a central directory built from those offsets: the caller handles the OSError, closes cleanly, and gets an archive that zipfile itself cannot read.
import io, zipfile
class Flaky(io.BytesIO):
countdown = None
def write(self, b):
if self.countdown is not None:
self.countdown -= 1
if self.countdown < 0:
raise OSError(28, 'No space left on device')
return super().write(b)
buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w') as zf:
for c in 'abcd':
zf.writestr(c + '.txt', c.upper().encode() * 5000)
flaky = Flaky(buf.getvalue())
with zipfile.ZipFile(flaky, 'a') as zf:
flaky.countdown = 1
try:
zf.repack([zf.remove('b.txt')], chunk_size=4096)
except OSError as exc:
print('repack raised:', exc)
flaky.countdown = None # space freed; the caller closes normally
with zipfile.ZipFile(flaky) as zf:
print('testzip:', zf.testzip())
repack raised: [Errno 28] No space left on device
testzip: c.txt
Expected: after the caller has handled the OSError, close() should either leave a readable archive or raise, rather than committing offsets that no write produced.
remove() and repack() are new in 3.16, so no released version is affected.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
- gh-156435
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.
Línea de trabajo
Empieza por zipfile._ZipRepacker.repack(), _copy_bytes(), remove() y close(), usando el Flaky BytesIO reproducer para rastrear los offsets después de OSError. El trabajo está terminado cuando la ruta de close posterior al error deja un archivo legible o lanza una excepción, en lugar de confirmar offsets para datos no escritos; revisa el PR vinculado gh-156435 antes de empezar.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- python
- Área
- backend
- Tipo de issue
- Error
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Estado de actividad
- Estancado
- Claridad
- Bien especificado
- Aptitud para principiantes
- 25/100