Hi ! Hi everyone!
This bug is preventing my package from migrating, so I decided to help by expanding this patch: https://salsa.debian.org/science-team/openpyxl/-/blob/d46f56cf7fc949e4e0c5ffdefef945baf68a46aa/debian/patches/openpyxl-fix-closed-image-fp.patch I uploaded it to Salsa: https://salsa.debian.org/science-team/openpyxl If anyone can check it out, I created this script that I think will help. I hope I helped Nilson F. Silva
#!/usr/bin/env python3 """ Test script for Debian bug #1132566 https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1132566 Bug: openpyxl crashes when saving a workbook multiple times with images because file handles are closed and reused. How to run: python3 test_bug_1132566.py Expected result (with patch applied): ALL tests PASS â no ValueError or OSError about closed files. Expected result (WITHOUT patch): FAIL on save #2 or later with something like: ValueError: I/O operation on closed file or: OSError: seek of closed file """ import sys import os import tempfile from io import BytesIO try: from openpyxl import Workbook from openpyxl.drawing.image import Image as XLImage from PIL import Image as PILImage except ImportError as e: print(f"[ERRO] Dependência não encontrada: {e}") print(" Instale com: pip install openpyxl pillow") sys.exit(1) PASS = "\033[92mPASS\033[0m" FAIL = "\033[91mFAIL\033[0m" results = [] def make_test_image_bytes(color=(255, 0, 0), size=(50, 50), fmt="PNG"): """Cria uma imagem PIL simples em memória e retorna como BytesIO.""" buf = BytesIO() img = PILImage.new("RGB", size, color=color) img.save(buf, format=fmt) buf.seek(0) return buf def run_test(name, fn): try: fn() print(f" [{PASS}] {name}") results.append(True) except Exception as e: print(f" [{FAIL}] {name}") print(f" {type(e).__name__}: {e}") results.append(False) # ââââââââââââââââââââââââââââââââââââââââââââââ # SCENARIO 1: Image from BytesIO # Saves the same workbook 3 times consecutively # ââââââââââââââââââââââââââââââââââââââââââââââ def test_bytesio_multiple_saves(): buf = make_test_image_bytes() img = XLImage(buf) wb = Workbook() ws = wb.active ws.add_image(img, "B2") with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f: path = f.name try: wb.save(path) # save 1 wb.save(path) # save 2 â costumava travar aqui wb.save(path) # save 3 finally: os.unlink(path) # ââââââââââââââââââââââââââââââââââââââââââââââ # SCENARIO 2: image from file on disk # ââââââââââââââââââââââââââââââââââââââââââââââ def test_file_path_multiple_saves(): with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tf: img_path = tf.name pil = PILImage.new("RGB", (50, 50), color=(0, 200, 0)) pil.save(tf, format="PNG") try: img = XLImage(img_path) wb = Workbook() ws = wb.active ws.add_image(img, "C3") with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f: xlsx_path = f.name try: wb.save(xlsx_path) wb.save(xlsx_path) wb.save(xlsx_path) finally: os.unlink(xlsx_path) finally: os.unlink(img_path) # ââââââââââââââââââââââââââââââââââââââââââââââ # SCENARIO 3: Multiple images in the same workbook # ââââââââââââââââââââââââââââââââââââââââââââââ def test_multiple_images_multiple_saves(): wb = Workbook() ws = wb.active for i, color in enumerate([(255, 0, 0), (0, 255, 0), (0, 0, 255)]): buf = make_test_image_bytes(color=color) img = XLImage(buf) ws.add_image(img, f"A{i * 15 + 1}") with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f: path = f.name try: wb.save(path) wb.save(path) wb.save(path) finally: os.unlink(path) # ââââââââââââââââââââââââââââââââââââââââââââââ # SCENARIO 4: BytesIO explicitly closed before the second save # This is the exact case reported in the bug # ââââââââââââââââââââââââââââââââââââââââââââââ def test_explicitly_closed_bytesio(): buf = make_test_image_bytes(color=(128, 128, 0)) img = XLImage(buf) wb = Workbook() ws = wb.active ws.add_image(img, "D4") with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f: path = f.name try: wb.save(path) # primeiro save â fecha o fp internamente buf.close() # simula o handle fechado wb.save(path) # segundo save â deve sobreviver com o patch finally: os.unlink(path) # ââââââââââââââââââââââââââââââââââââââââââââââ # SCENARIO 5: JPEG (does not convert to PNG) # ââââââââââââââââââââââââââââââââââââââââââââââ def test_jpeg_multiple_saves(): buf = make_test_image_bytes(color=(200, 100, 50), fmt="JPEG") img = XLImage(buf) wb = Workbook() ws = wb.active ws.add_image(img, "E5") with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f: path = f.name try: wb.save(path) wb.save(path) finally: os.unlink(path) # ââââââââââââââââââââââââââââââââââââââââââââââ # Execution # ââââââââââââââââââââââââââââââââââââââââââââââ print() print("=" * 55) print(" Teste: bug Debian #1132566 â openpyxl image crash") print("=" * 55) print() run_test("BytesIO salvo 3x consecutivas", test_bytesio_multiple_saves) run_test("Arquivo em disco salvo 3x consecutivas", test_file_path_multiple_saves) run_test("Múltiplas imagens salvas 3x", test_multiple_images_multiple_saves) run_test("BytesIO fechado explicitamente (caso exato do bug)", test_explicitly_closed_bytesio) run_test("JPEG salvo 2x consecutivas", test_jpeg_multiple_saves) print() total = len(results) passed = sum(results) failed = total - passed print(f" Resultado: {passed}/{total} testes passaram", end="") if failed == 0: print(f" \033[92mâ patch OK â bug corrigido\033[0m") else: print(f" \033[91mâ {failed} teste(s) falharam â verificar patch\033[0m") print() sys.exit(0 if failed == 0 else 1)
-- debian-science-maintainers mailing list [email protected] https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/debian-science-maintainers
