From 8ed3575328b0bb2dde27055e5c304022504c7633 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" <68491+gpshead@users.noreply.github.com> Date: Sun, 1 Nov 2026 12:37:47 -0800 Subject: [PATCH] [3.15] gh-161112: Fix shutil.make_archive() adding a zip archive to itself (GH-161113) 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) --- Lib/shutil.py | 9 ++++++++- Lib/test/test_shutil.py | 15 +++++++++++++++ ...2026-10-22-05-24-21.gh-issue-161112.BFrZRw.rst | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-22-05-24-21.gh-issue-161112.BFrZRw.rst diff --git a/Lib/shutil.py b/Lib/shutil.py index 0086c1c96c137ce..414388f9e766ce7 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 02bc920702b31c2..01ed70d09fcc8a5 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 000000000000000..73dc245dcb76dd0 --- /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.