Skip to content

zipfile tests: some files are left opened #137589

Closed

Description

@Rogdham

Bug report

Bug description:

Some of the tests of zipfile don't close ZipFile instances, leaving some files opened as a result.

I have identified the following, although it is possible that there are more:

  • in test_core.py:
    • test_writestr_compression
    • test_writestr_compresslevel
    • test_empty_zipfile
  • in test_path.py:
    • test_pathlike_construction
    • test_root_on_disk
    • test_pickle
    • test_extract_orig_with_implied_dirs
Here is a patch against main for illustration purposes

diff --git a/Lib/test/test_zipfile/_path/test_path.py b/Lib/test/test_zipfile/_path/test_path.py
index 958a586b0dc..e7931b6f394 100644
--- a/Lib/test/test_zipfile/_path/test_path.py
+++ b/Lib/test/test_zipfile/_path/test_path.py
@@ -274,7 +274,8 @@ def test_pathlike_construction(self, alpharep):
         """
         zipfile_ondisk = self.zipfile_ondisk(alpharep)
         pathlike = FakePath(str(zipfile_ondisk))
-        zipfile.Path(pathlike)
+        root = zipfile.Path(pathlike)
+        root.root.close()
 
     @pass_alpharep
     def test_traverse_pathlike(self, alpharep):
@@ -373,6 +374,7 @@ def test_root_on_disk(self, alpharep):
         root = zipfile.Path(self.zipfile_ondisk(alpharep))
         assert root.name == 'alpharep.zip' == root.filename.name
         assert root.stem == 'alpharep' == root.filename.stem
+        root.root.close()
 
     @pass_alpharep
     def test_suffix(self, alpharep):
@@ -574,11 +576,13 @@ def test_inheritance(self, alpharep):
     )
     def test_pickle(self, alpharep, path_type, subpath):
         zipfile_ondisk = path_type(str(self.zipfile_ondisk(alpharep)))
-
-        saved_1 = pickle.dumps(zipfile.Path(zipfile_ondisk, at=subpath))
+        root = zipfile.Path(zipfile_ondisk, at=subpath)
+        saved_1 = pickle.dumps(root)
+        root.root.close()
         restored_1 = pickle.loads(saved_1)
         first, *rest = restored_1.iterdir()
         assert first.read_text(encoding='utf-8').startswith('content of ')
+        restored_1.root.close()
 
     @pass_alpharep
     def test_extract_orig_with_implied_dirs(self, alpharep):
@@ -590,6 +594,7 @@ def test_extract_orig_with_implied_dirs(self, alpharep):
         # wrap the zipfile for its side effect
         zipfile.Path(zf)
         zf.extractall(source_path.parent)
+        zf.close()
 
     @pass_alpharep
     def test_getinfo_missing(self, alpharep):
diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py
index c033059a515..3b399f5e883 100644
--- a/Lib/test/test_zipfile/test_core.py
+++ b/Lib/test/test_zipfile/test_core.py
@@ -312,26 +312,26 @@ def test_low_compression(self):
                 self.assertEqual(openobj.read(1), b'2')
 
     def test_writestr_compression(self):
-        zipfp = zipfile.ZipFile(TESTFN2, "w")
-        zipfp.writestr("b.txt", "hello world", compress_type=self.compression)
-        info = zipfp.getinfo('b.txt')
-        self.assertEqual(info.compress_type, self.compression)
+        with zipfile.ZipFile(TESTFN2, "w") as zipfp:
+            zipfp.writestr("b.txt", "hello world", compress_type=self.compression)
+            info = zipfp.getinfo('b.txt')
+            self.assertEqual(info.compress_type, self.compression)
 
     def test_writestr_compresslevel(self):
-        zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1)
-        zipfp.writestr("a.txt", "hello world", compress_type=self.compression)
-        zipfp.writestr("b.txt", "hello world", compress_type=self.compression,
-                       compresslevel=2)
+        with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp:
+            zipfp.writestr("a.txt", "hello world", compress_type=self.compression)
+            zipfp.writestr("b.txt", "hello world", compress_type=self.compression,
+                        compresslevel=2)
 
-        # Compression level follows the constructor.
-        a_info = zipfp.getinfo('a.txt')
-        self.assertEqual(a_info.compress_type, self.compression)
-        self.assertEqual(a_info.compress_level, 1)
+            # Compression level follows the constructor.
+            a_info = zipfp.getinfo('a.txt')
+            self.assertEqual(a_info.compress_type, self.compression)
+            self.assertEqual(a_info.compress_level, 1)
 
-        # Compression level is overridden.
-        b_info = zipfp.getinfo('b.txt')
-        self.assertEqual(b_info.compress_type, self.compression)
-        self.assertEqual(b_info._compresslevel, 2)
+            # Compression level is overridden.
+            b_info = zipfp.getinfo('b.txt')
+            self.assertEqual(b_info.compress_type, self.compression)
+            self.assertEqual(b_info._compresslevel, 2)
 
     def test_read_return_size(self):
         # Issue #9837: ZipExtFile.read() shouldn't return more bytes
@@ -2256,6 +2256,7 @@ def test_empty_zipfile(self):
             zipf = zipfile.ZipFile(TESTFN, mode="r")
         except zipfile.BadZipFile:
             self.fail("Unable to create empty ZIP file in 'w' mode")
+        zipf.close()
 
         zipf = zipfile.ZipFile(TESTFN, mode="a")
         zipf.close()
@@ -2263,6 +2264,7 @@ def test_empty_zipfile(self):
             zipf = zipfile.ZipFile(TESTFN, mode="r")
         except:
             self.fail("Unable to create empty ZIP file in 'a' mode")
+        zipf.close()
 
     def test_open_empty_file(self):
         # Issue 1710703: Check that opening a file with less than 22 bytes

On Windows systems, this possibly could result in errors like the following:

PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: '@test_1812_tmpæ'

…however, I only saw the error when running the code under Windows with PyPy (which does not behaves exactly as CPython for garbage collection); but it happens every single time there.

CPython versions tested on:

3.14.0rc1 codebase but run with PyPy

Operating systems tested on:

Windows

Linked PRs

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    on Aug 9, 2025
  2. emmatyping commented on Aug 23, 2025

    @emmatyping
    Member

    @Rogdham Would you like to turn your patch into a PR? I think properly closing the files in tests is the right thing to do, even if this practically was only a problem on PyPy.

  3. Rogdham commented on Aug 23, 2025

    @Rogdham
    ContributorAuthor

    Sure, here it is: GH-138080

  4. maurycy commented on Aug 24, 2025

    @maurycy
    Contributor

    @Rogdham Out of curiosity: how did you catch this on PyPy?

    I tried ./python.exe -X dev -W error::ResourceWarning -m test test.test_zipfile --fail-env-changed -vvv --rerun and found nothing on macOS. Likely I'm doing something wrong but I can try to run the correct command over the whole test suite!

  5. Rogdham commented on Aug 25, 2025

    @Rogdham
    ContributorAuthor

    @maurycy I only saw the exception with PyPy on Windows (did not appear on Linux nor macOS). I caught this when I added support for PyPy on backports.zstd package, in which I am reusing the tests for zipfile from CPython.

  6. added a commit that references this issue on Oct 8, 2025
  7. added 2 commits that reference this issue on Oct 8, 2025
  8. added 2 commits that reference this issue on Oct 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    testsTests in the Lib/test dirtype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions