https://github.com/python/cpython/commit/f9f33a61c554298d480e1017f61d94bb578bd1cf
commit: f9f33a61c554298d480e1017f61d94bb578bd1cf
branch: main
author: Brittany Reynoso <[email protected]>
committer: ambv <[email protected]>
date: 2026-10-23T11:30:02-07:00
summary:
gh-81954: Eagerly import warnings module to failure of ZipFile.__del__ during
finalization (#159419)
ZipFile.__del__ emitted its ResourceWarning before calling close().
When warnings.warn() raised, close() was skipped and the central
directory of a writable archive was never written, leaving a corrupt
file. This happens at interpreter shutdown, where resolving the lazy
"warnings" import raises ImportError, and with -W
error::ResourceWarning at any time.
files:
A Misc/NEWS.d/next/Library/2026-10-14-14-47-05.gh-issue-81954.KvM0gY.rst
M Lib/test/test_zipfile/test_core.py
M Lib/zipfile/__init__.py
diff --git a/Lib/test/test_zipfile/test_core.py
b/Lib/test/test_zipfile/test_core.py
index 708db4d4387df5..cb9061042937cf 100644
--- a/Lib/test/test_zipfile/test_core.py
+++ b/Lib/test/test_zipfile/test_core.py
@@ -26,7 +26,7 @@
from test.support import (
findfile, requires_zlib, requires_bz2, requires_lzma,
requires_zstd, captured_stdout, captured_stderr, requires_subprocess,
- cpython_only, gc_collect
+ cpython_only, gc_collect, catch_unraisable_exception
)
from test.support.os_helper import (
TESTFN, unlink, rmtree, temp_dir, temp_cwd, fd_count, FakePath,
@@ -4095,6 +4095,21 @@ def test_garbage_collection(self):
zf.close()
del zf
+ # ResourceWarning escalated to an error is reported as unraisable,
+ # and the archive is still closed.
+ buf = io.BytesIO()
+ zf = zipfile.ZipFile(buf, "w")
+ zf.writestr("f.txt", b"data")
+ with (warnings.catch_warnings(action="error",
category=ResourceWarning),
+ catch_unraisable_exception() as cm):
+ del zf
+ gc_collect()
+ self.assertIs(cm.unraisable.exc_type, ResourceWarning)
+
+ with zipfile.ZipFile(buf, "r") as zf:
+ self.assertEqual(zf.namelist(), ["f.txt"])
+ self.assertEqual(zf.read("f.txt"), b"data")
+
def test_unsupported_version(self):
# File has an extract_version of 120
data =
(b'PK\x03\x04x\x00\x00\x00\x00\x00!p\xa1@\x00\x00\x00\x00\x00\x00'
diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py
index 87337478912e57..4c5eb1d04eb4d3 100644
--- a/Lib/zipfile/__init__.py
+++ b/Lib/zipfile/__init__.py
@@ -12,7 +12,7 @@
import sys
import threading
import time
-lazy import warnings
+import warnings # must stay eager to support finalization
try:
import zlib # We may need its compression method
@@ -2646,14 +2646,18 @@ def mkdir(self, zinfo_or_directory_name, mode=511):
self.fp.write(zinfo.FileHeader(False))
self.start_dir = self.fp.tell()
- def __del__(self):
+ def __del__(self, _warn=warnings.warn):
"""Call the "close()" method in case the user forgot."""
# gh-81954: Warn if writable ZipFile is implicitly closed.
# GC cleanup order is non-deterministic and can result in data loss.
- if self.fp is not None and self.mode in ('w', 'x', 'a'):
- warnings.warn(f"unclosed ZipFile {self!r}",
- ResourceWarning, source=self, stacklevel=2)
- self.close()
+ try:
+ # Wrap within a try to ensure the file is closed even if
+ # warnings raises.
+ if self.fp is not None and self.mode in ('w', 'x', 'a'):
+ _warn(f"unclosed ZipFile {self!r}",
+ ResourceWarning, source=self, stacklevel=2)
+ finally:
+ self.close()
def close(self):
"""Close the file, and for mode 'w', 'x' and 'a' write the ending
diff --git
a/Misc/NEWS.d/next/Library/2026-10-14-14-47-05.gh-issue-81954.KvM0gY.rst
b/Misc/NEWS.d/next/Library/2026-10-14-14-47-05.gh-issue-81954.KvM0gY.rst
new file mode 100644
index 00000000000000..5ced3b94d223b2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-14-14-47-05.gh-issue-81954.KvM0gY.rst
@@ -0,0 +1,2 @@
+Fix :class:`zipfile.ZipFile` not being closed at interpreter shutdown when
+emitting its :exc:`ResourceWarning` failed with an :exc:`ImportError`.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]