diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index 708db4d4387df5..19a823802584b0 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -4337,6 +4337,26 @@ def test_closed_zip_raises_ValueError(self): f.write('zipfile test data') self.assertRaises(ValueError, zipf.write, TESTFN) + def test_closed_zip_extract_raises_ValueError(self): + with zipfile.ZipFile(io.BytesIO(), mode="w") as zipf: + pass + self.assertRaises(ValueError, zipf.testzip) + + with zipfile.ZipFile(io.BytesIO(), mode="w") as zipf: + zipf.writestr("dir/", b"") + zipf.writestr("file.txt", b"data") + self.assertRaises(ValueError, zipf.testzip) + + with temp_dir() as dest: + self.assertRaises(ValueError, zipf.extract, "dir/", dest) + self.assertEqual(os.listdir(dest), []) + self.assertRaises(ValueError, zipf.extract, "file.txt", dest) + self.assertEqual(os.listdir(dest), []) + self.assertRaises(ValueError, zipf.extractall, dest) + self.assertEqual(os.listdir(dest), []) + self.assertRaises(ValueError, zipf.extractall, dest, ["dir/"]) + self.assertEqual(os.listdir(dest), []) + def test_bad_constructor_mode(self): """Check that bad modes passed to ZipFile constructor are caught.""" self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q") diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index d817bdd8769e7d..696fcc2b97a276 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -2146,6 +2146,9 @@ def testzip(self): Return None if all files could be read successfully, or the name of the offending file otherwise.""" + if not self.fp: + raise ValueError( + "Attempt to use ZIP archive that was already closed") chunk_size = 2 ** 20 for zinfo in self.filelist: try: @@ -2352,6 +2355,9 @@ def extract(self, member, path=None, pwd=None): specify a different directory using 'path'. You can specify the password to decrypt the file using 'pwd'. """ + if not self.fp: + raise ValueError( + "Attempt to use ZIP archive that was already closed") if path is None: path = os.getcwd() else: @@ -2366,6 +2372,9 @@ def extractall(self, path=None, members=None, pwd=None): by namelist(). You can specify the password to decrypt all files using 'pwd'. """ + if not self.fp: + raise ValueError( + "Attempt to use ZIP archive that was already closed") if members is None: members = self.namelist() diff --git a/Misc/NEWS.d/next/Library/2026-09-21-13-00-00.gh-issue-157893.zfCl0s.rst b/Misc/NEWS.d/next/Library/2026-09-21-13-00-00.gh-issue-157893.zfCl0s.rst new file mode 100644 index 00000000000000..83d7c372dc9f4b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-21-13-00-00.gh-issue-157893.zfCl0s.rst @@ -0,0 +1,3 @@ +:meth:`zipfile.ZipFile.testzip`, :meth:`~zipfile.ZipFile.extract` and +:meth:`~zipfile.ZipFile.extractall` now consistently raise :exc:`ValueError` +on a closed :class:`~zipfile.ZipFile` before touching the filesystem.