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]