Reading an image causes workbook.save() to fail with ValueError exception
Reading a worksheet image causes workbook.save()
to fail with ValueError: I/O operation on closed file.
How to Reproduce
Use the attached sample.xlsx file and following code to demonstrate the problem.
import io
import openpyxl as pyxl
workbook = pyxl.load_workbook("./sample.xlsx")
sheet = workbook.worksheets[0]
image = sheet._images[0]
data = image._data
# Program works when the following line is commented out.
data()
workbook.save("./freddy.xlsx")
workbook.close()
Environment
- Python: 3.11
- OpenPyxl: 3.1.2
- Pillow: 10.2.0
Research
-
The PIL Image
open
method inPIL.Image.py
states, "This is a lazy operation;…the file remains open and the actual image data is not read" -
_import_image
inopenpyxl/drawing/image.py
is called three times:- Once from
Image.__init__
- Once when
Image._data
is explicitly called fromsample.py
- Once when
Image._data
is called to save the workbook.
- Once from
-
The second call to
Image._data
fails with the ValueError. -
Image.__init__
leaves the file handle open. -
Image._data
closes the file handle.
Proposed Solution
- I believe the
fp.close()
statement inImage._data
should only be called when a newBytesIO
stream is created in theelse
section of the_data
method. I've tested this and it is working in my environment but there may be side effects I don't understand.
def _data(self):
"""
Return image data, convert to supported types if necessary
"""
img = _import_image(self.ref)
# don't convert these file formats
if self.format in ['gif', 'jpeg', 'png']:
img.fp.seek(0)
fp = img.fp
data = fp.read()
else:
fp = BytesIO()
img.save(fp, format="png")
fp.seek(0)
data = fp.read()
fp.close()
return data
This seems to also fix #2100: trying to save the file twice
Version 3.2
Version 3.2 code requires the same change.
See Also: