You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ValueError on ephemeral zipfile after exception #106763
draft @ cat zip-tb.txt
>>> import zipfile
>>> import io
>>> zf = zipfile.ZipFile(io.BytesIO(), 'w')
>>> zf.foo # doctest: +ELLIPSIS
Traceback (most recent call last):
...
AttributeError: ...
draft @ py -3.12 -m doctest zip-tb.txt
Exception ignored in: <function ZipFile.__del__ at 0x100f36b60>
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/zipfile/__init__.py", line 1916, in __del__
self.close()
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/zipfile/__init__.py", line 1933, in close
self.fp.seek(self.start_dir)
ValueError: I/O operation on closed file.
The test runs fine, but triggers a ValueErroratexit.
The issue only arises if the last expression in the doctest triggered a traceback and the zipfile was present in the traceback. Changing zf.foo to io.foo or adding >>> pass bypasses the error.
I suspect there's a flaw in doctest that leaves resources unclosed if the last expression is handling a traceback.
$ python3 zip-tb.py
Exception ignored in: <function ZipFile.__del__ at 0x7fbc3b4484a0>
Traceback (most recent call last):
File "/usr/local/lib/python3.11/zipfile.py", line 1874, in __del__
self.close()
File "/usr/local/lib/python3.11/zipfile.py", line 1891, in close
self.fp.seek(self.start_dir)
ValueError: I/O operation on closed file.
There seems to be a bug in the garbage collector. The BytesIO object is removed before the ZipFile object.
The bug affects versions starting from 3.8.17.
changed the title [-]ValueError running doctests on zipfile with exception last[/-][+]ValueError on ephemeral zipfile after exception[/+]on Jul 18, 2023
Assigning the exception to a local variable creates a reference cycle, which means the objects' finalizers are called in essentially arbitrary order. Code that relies on objects' finalizer ordering must ensure they are not involved in cycles. However, relying on finalizers is (AIUI) generally considered a bad idea if it can possibly be avoided.
As @thatch suggested, using context managers would be a good way to deal with this:
Perhaps ZipFile should be more resilient to the inner file object having been closed?
The bug affects versions starting from 3.8.17.
Does that imply that the behavior does not occur on Python 3.8.16 or 3.7, or are you saying you tested to Pythons as old as 3.8 (and happened to test on 3.8.17)?
Perhaps ZipFile should be more resilient to the inner file object having been closed?
We can't just ignore errors from the underlying file: if it has been closed prior to the zip file that is a serious bug. It is the user's responsibility to ensure this cannot happen, whether through context managers or more esoteric means. We must raise the error or risk silent data loss and corruption!
Activity
psvi commentedon Jul 18, 2023
This isn't a flaw in
doctest.Let's consider the following script:
It raises the same exception:
There seems to be a bug in the garbage collector. The BytesIO object is removed before the ZipFile object.
The bug affects versions starting from 3.8.17.
[-]ValueError running doctests on zipfile with exception last[/-][+]ValueError on ephemeral zipfile after exception[/+]thatch commentedon May 20, 2025
Does this work better when using the ZipFile as a context manager?
duaneg commentedon May 23, 2025
Assigning the exception to a local variable creates a reference cycle, which means the objects' finalizers are called in essentially arbitrary order. Code that relies on objects' finalizer ordering must ensure they are not involved in cycles. However, relying on finalizers is (AIUI) generally considered a bad idea if it can possibly be avoided.
As @thatch suggested, using context managers would be a good way to deal with this:
That ensures everything is cleaned up correctly, without relying on GC behaviour.
jaraco commentedon May 25, 2025
Perhaps ZipFile should be more resilient to the inner file object having been closed?
Does that imply that the behavior does not occur on Python 3.8.16 or 3.7, or are you saying you tested to Pythons as old as 3.8 (and happened to test on 3.8.17)?
duaneg commentedon May 26, 2025
We can't just ignore errors from the underlying file: if it has been closed prior to the zip file that is a serious bug. It is the user's responsibility to ensure this cannot happen, whether through context managers or more esoteric means. We must raise the error or risk silent data loss and corruption!
serhiy-storchaka commentedon May 18, 2026
This virtually a duplicate of #81954.