https://github.com/python/cpython/commit/2f87f8078e3c73f45b7b5b13763dc608968a3158
commit: 2f87f8078e3c73f45b7b5b13763dc608968a3158
branch: 3.15
author: Gregory P. Smith <[email protected]>
committer: gpshead <[email protected]>
date: 2026-11-01T21:40:19Z
summary:

[3.15] gh-161112: Fix shutil.make_archive() adding a zip archive to itself 
(GH-161113) (#161140)

Skip any walked file that is the archive being written, whether it is
created inside the archived tree or a symlink in the tree points at it.

(cherry picked from commit 521e15ca76fb9af7d3408957a80916a954d8f6f8)

files:
A Misc/NEWS.d/next/Library/2026-10-22-05-24-21.gh-issue-161112.BFrZRw.rst
M Lib/shutil.py
M Lib/test/test_shutil.py

diff --git a/Lib/shutil.py b/Lib/shutil.py
index 0086c1c96c137c..414388f9e766ce 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -1101,6 +1101,7 @@ def _make_zipfile(base_name, base_dir, verbose=0, 
dry_run=0,
             if root_dir is not None:
                 base_dir = os.path.join(root_dir, base_dir)
             base_dir = os.path.normpath(base_dir)
+            zip_stat = os.stat(zip_filename)
             if arcname != os.curdir:
                 zf.write(base_dir, arcname)
                 if logger is not None:
@@ -1119,7 +1120,13 @@ def _make_zipfile(base_name, base_dir, verbose=0, 
dry_run=0,
                 for name in filenames:
                     path = os.path.join(dirpath, name)
                     path = os.path.normpath(path)
-                    if os.path.isfile(path):
+                    try:
+                        st = os.stat(path)
+                    except OSError:
+                        continue
+                    # Do not add the archive to itself.
+                    if (stat.S_ISREG(st.st_mode) and
+                            not os.path.samestat(st, zip_stat)):
                         arcname = os.path.join(arcdirpath, name)
                         zf.write(path, arcname)
                         if logger is not None:
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 02bc920702b31c..01ed70d09fcc8a 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -2072,6 +2072,21 @@ def test_make_zipfile_in_curdir(self):
             # root_dir must be None, so the archive path is relative.
             self.assertEqual(make_archive('test', 'zip'), 'test.zip')
             self.assertTrue(os.path.isfile('test.zip'))
+            # The archive does not contain itself.
+            with zipfile.ZipFile('test.zip') as zf:
+                self.assertEqual(zf.namelist(), [])
+
+    @support.requires_zlib()
+    @os_helper.skip_unless_symlink
+    def test_make_zipfile_symlink_to_archive(self):
+        root_dir, base_dir = self._create_files('')
+        os.symlink('archive.zip', os.path.join(root_dir, 'link'))
+        base_name = os.path.join(root_dir, 'archive')
+        with no_chdir:
+            archive = make_archive(base_name, 'zip', root_dir)
+        with zipfile.ZipFile(archive) as zf:
+            self.assertCountEqual(zf.namelist(),
+                    ['sub/', 'sub2/', 'file1', 'file2', 'sub/file3'])
 
     def test_make_archive_pathlike_cwd_default(self):
         called_args = []
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-22-05-24-21.gh-issue-161112.BFrZRw.rst 
b/Misc/NEWS.d/next/Library/2026-10-22-05-24-21.gh-issue-161112.BFrZRw.rst
new file mode 100644
index 00000000000000..73dc245dcb76dd
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-22-05-24-21.gh-issue-161112.BFrZRw.rst
@@ -0,0 +1,3 @@
+Fix :func:`shutil.make_archive` adding the zip archive being created to itself
+when it is written inside the directory being archived or a symbolic link in
+that directory refers to it.

_______________________________________________
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