Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions Lib/test/test_zipapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,13 @@ def test_main_and_main_py(self):
source.mkdir()
(source / '__main__.py').touch()
target = self.tmpdir / 'source.pyz'
with self.assertRaises(zipapp.ZipAppError):
zipapp.create_archive(str(source), str(target), main='pkg.mod:fn')
for main in ('pkg.mod:fn', ''):
with self.subTest(main=main):
target.write_bytes(b'original content')
with self.assertRaisesRegex(zipapp.ZipAppError,
'Cannot specify entry point'):
zipapp.create_archive(str(source), str(target), main=main)
self.assertEqual(target.read_bytes(), b'original content')

def test_main_written(self):
# Test that the __main__.py is written correctly.
Expand Down Expand Up @@ -227,8 +232,10 @@ def test_main_validation(self):
]
for main in problems:
with self.subTest(main=main):
with self.assertRaises(zipapp.ZipAppError):
with self.assertRaisesRegex(zipapp.ZipAppError,
'Invalid entry point:'):
zipapp.create_archive(str(source), str(target), main=main)
self.assertFalse(target.exists())

def test_default_no_shebang(self):
# Test that no shebang line is written to the target by default.
Expand Down Expand Up @@ -328,6 +335,19 @@ def test_read_from_fileobj(self):
zipapp.create_archive(temp_archive, new_target, interpreter='python2.7')
self.assertStartsWith(new_target.getvalue(), b'#!python2.7\n')

def test_main_with_existing_archive(self):
archive = self.tmpdir / 'source.pyz'
with zipfile.ZipFile(archive, 'w') as z:
z.writestr('__main__.py', '')
target = self.tmpdir / 'target.pyz'
for main in ('pkg.mod:fn', ''):
for source in (archive, io.BytesIO(archive.read_bytes())):
with self.subTest(main=main, source=source):
target.write_bytes(b'original content')
with self.assertRaises(zipapp.ZipAppError):
zipapp.create_archive(source, target, main=main)
self.assertEqual(target.read_bytes(), b'original content')

def test_remove_shebang(self):
# Test that we can remove the shebang from a file.
source = self.tmpdir / 'source'
Expand Down Expand Up @@ -428,11 +448,14 @@ def test_cmdline_copy_change_main(self):
# Test copying an archive doesn't allow changing __main__.py.
original = self.make_archive()
target = self.tmpdir / 'target.pyz'
args = [str(original), '-o', str(target), '-m', 'foo:bar']
with self.assertRaises(SystemExit) as cm:
zipapp.main(args)
# Program should exit with a non-zero return code.
self.assertTrue(cm.exception.code)
for main in ('foo:bar', ''):
with self.subTest(main=main):
args = [str(original), '-o', str(target), '-m', main]
with self.assertRaises(SystemExit) as cm:
zipapp.main(args)
# Program should exit with a non-zero return code.
self.assertTrue(cm.exception.code)
self.assertFalse(target.exists())

@patch('sys.stdout', new_callable=io.StringIO)
def test_info_command(self, mock_stdout):
Expand Down
10 changes: 6 additions & 4 deletions Lib/zipapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,21 +103,23 @@ def create_archive(source, target=None, interpreter=None, main=None,
source_is_file = True

if source_is_file:
if main is not None:
raise ZipAppError("Cannot change the main function when copying")
_copy_archive(source, target, interpreter)
return

# We are creating a new archive from a directory.
if not source.exists():
raise ZipAppError("Source does not exist")
has_main = (source / '__main__.py').is_file()
if main and has_main:
if main is not None and has_main:
raise ZipAppError(
"Cannot specify entry point if the source has __main__.py")
if not (main or has_main):
if main is None and not has_main:
raise ZipAppError("Archive has no entry point")

main_py = None
if main:
if main is not None:
# Check that main has the right format.
mod, sep, fn = main.partition(':')
mod_ok = all(part.isidentifier() for part in mod.split('.'))
Expand Down Expand Up @@ -219,7 +221,7 @@ def main(args=None):
if args.output is None or (os.path.exists(args.output) and
os.path.samefile(args.source, args.output)):
raise SystemExit("In-place editing of archives is not supported")
if args.main:
if args.main is not None:
raise SystemExit("Cannot change the main function when copying")

create_archive(args.source, args.output,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :func:`zipapp.create_archive` to reject a ``main`` argument when copying
an existing archive, instead of silently ignoring it. An empty ``main`` is
also rejected when creating an archive from a directory.
Loading