]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-150077: Fix fileobj leak in `tarfile.TarFile.zstopen` on `BaseException` (GH-150080)
authorstevens <lipengyu@kylinos.cn>
Wed, 15 Jul 2026 17:53:23 +0000 (01:53 +0800)
committerGitHub <noreply@github.com>
Wed, 15 Jul 2026 17:53:23 +0000 (10:53 -0700)
This is one of those times when a bare `except` is appropriate.

Lib/tarfile.py
Lib/test/test_tarfile.py
Misc/NEWS.d/next/Library/2026-05-19-18-25-00.gh-issue-150077.zstopen-close.rst [new file with mode: 0644]

index 5d5cef2f139a42620cebc003005abee4f2a28a3e..d12bd15aa2d231906750b77d3311a03aed5987b2 100644 (file)
@@ -2129,7 +2129,7 @@ class TarFile(object):
             if mode == 'r':
                 raise ReadError("not a zstd file") from e
             raise
-        except Exception:
+        except:
             fileobj.close()
             raise
         t._extfileobj = False
index 514cfbb07cd76ada3623b6e9b8434815457a1266..c86bcb79eb85d891b2d712f640730db790395311 100644 (file)
@@ -1141,6 +1141,38 @@ class LzmaDetectReadTest(LzmaTest, DetectReadTest):
 class ZstdDetectReadTest(ZstdTest, DetectReadTest):
     pass
 
+
+@support.requires_zstd()
+class ZstdOpenTest(unittest.TestCase):
+    """
+    See: https://github.com/python/cpython/issues/150077
+    """
+    def test_zstopen_closes_fileobj_on_base_exception(self):
+        path = os_helper.TESTFN + ".tar.zst"
+        self.addCleanup(os_helper.unlink, path)
+        with tarfile.open(path, "w:zst"):
+            pass
+
+        opened = []
+        real_ZstdFile = zstd.ZstdFile
+
+        def tracking_ZstdFile(*args, **kwargs):
+            fileobj = real_ZstdFile(*args, **kwargs)
+            opened.append(fileobj)
+            return fileobj
+
+        with (
+            unittest.mock.patch("compression.zstd.ZstdFile", tracking_ZstdFile),
+            unittest.mock.patch.object(
+                tarfile.TarFile, "taropen", side_effect=KeyboardInterrupt),
+            self.assertRaises(KeyboardInterrupt),
+        ):
+            tarfile.TarFile.zstopen(path)
+
+        self.assertEqual(len(opened), 1)
+        self.assertTrue(opened[0].closed)
+
+
 class GzipBrokenHeaderCorrectException(GzipTest, unittest.TestCase):
     """
     See: https://github.com/python/cpython/issues/107396
diff --git a/Misc/NEWS.d/next/Library/2026-05-19-18-25-00.gh-issue-150077.zstopen-close.rst b/Misc/NEWS.d/next/Library/2026-05-19-18-25-00.gh-issue-150077.zstopen-close.rst
new file mode 100644 (file)
index 0000000..06d20e4
--- /dev/null
@@ -0,0 +1,3 @@
+Fix ``tarfile.TarFile.zstopen`` to close the underlying zstd file object
+when opening the tar archive is interrupted by a :exc:`BaseException`
+subclass such as :exc:`KeyboardInterrupt`.