https://github.com/python/cpython/commit/33eb5fa40adca61b78b8f5dd0b2888bcd9b1e197
commit: 33eb5fa40adca61b78b8f5dd0b2888bcd9b1e197
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: pfmoore <[email protected]>
date: 2026-10-14T12:35:23+01:00
summary:
[3.15] gh-156568: Make zipapp archives executable with PathLike targets
(gh-156569) (gh-159404)
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 a1cef18ada9d05..39e765d1ffdc6d 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]