https://github.com/python/cpython/commit/c0e1ba1bad08cdcdb3567e58fae35486fcc9d98b
commit: c0e1ba1bad08cdcdb3567e58fae35486fcc9d98b
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: pfmoore <[email protected]>
date: 2026-10-14T12:36:10+01:00
summary:

[3.14] gh-156568: Make zipapp archives executable with PathLike targets 
(gh-156569) (gh-159405)

files:
A Misc/NEWS.d/next/Library/2026-08-29-14-28-46.gh-issue-156568.mkxLY8.rst
M Lib/test/test_zipapp.py
M Lib/zipapp.py

diff --git a/Lib/test/test_zipapp.py b/Lib/test/test_zipapp.py
index 8fb0a68deba535..88cc55716f59a8 100644
--- a/Lib/test/test_zipapp.py
+++ b/Lib/test/test_zipapp.py
@@ -366,6 +366,24 @@ def test_shebang_is_executable(self):
         zipapp.create_archive(str(source), str(target), interpreter='python')
         self.assertTrue(target.stat().st_mode & stat.S_IEXEC)
 
+    @unittest.skipIf(sys.platform == 'win32',
+                     'Windows does not support an executable bit')
+    @os_helper.skip_unless_working_chmod
+    def test_copied_archive_with_pathlike_target_is_executable(self):
+        # Test that copying an archive to a PathLike target makes it 
executable.
+        source = self.tmpdir / 'source'
+        source.mkdir()
+        (source / '__main__.py').touch()
+        target = self.tmpdir / 'source.pyz'
+        zipapp.create_archive(source, target, interpreter='python')
+        # Copying an archive uses a different code path than creating
+        # one from scratch. Ensure that the executable bit is set
+        # even if the target is a path-like object.
+        # See https://github.com/python/cpython/issues/156568
+        new_target = self.tmpdir / 'changed.pyz'
+        zipapp.create_archive(target, new_target, interpreter='python')
+        self.assertTrue(new_target.stat().st_mode & stat.S_IEXEC)
+
     @unittest.skipIf(sys.platform == 'win32',
                      'Windows does not support an executable bit')
     def test_no_shebang_is_not_executable(self):
diff --git a/Lib/zipapp.py b/Lib/zipapp.py
index 7a4ef96ea0f077..6bbe9ee7959146 100644
--- a/Lib/zipapp.py
+++ b/Lib/zipapp.py
@@ -69,7 +69,7 @@ def _copy_archive(archive, new_archive, interpreter=None):
             dst.write(first_2)
             shutil.copyfileobj(src, dst)
 
-    if interpreter and isinstance(new_archive, str):
+    if interpreter and isinstance(new_archive, (str, os.PathLike)):
         os.chmod(new_archive, os.stat(new_archive).st_mode | stat.S_IEXEC)
 
 
diff --git 
a/Misc/NEWS.d/next/Library/2026-08-29-14-28-46.gh-issue-156568.mkxLY8.rst 
b/Misc/NEWS.d/next/Library/2026-08-29-14-28-46.gh-issue-156568.mkxLY8.rst
new file mode 100644
index 00000000000000..3efd5a0481874c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-29-14-28-46.gh-issue-156568.mkxLY8.rst
@@ -0,0 +1,2 @@
+:func:`zipapp.create_archive` now correctly sets the executable bit on the
+target archive when copying a zipapp where the target is a path-like object.

_______________________________________________
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