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]

Reply via email to