Skip to content

Commit 76c1755

Browse files
committed
gh-131178: Add tests for zipapp command-line interface
1 parent 65f9932 commit 76c1755

2 files changed

Lines changed: 126 additions & 1 deletion

File tree

Lib/test/test_zipapp.py

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
import io
44
import pathlib
55
import stat
6+
import subprocess
67
import sys
78
import tempfile
89
import unittest
910
import zipapp
1011
import zipfile
11-
from test.support import requires_zlib
12+
from test.support import requires_zlib, requires_subprocess
1213
from test.support import os_helper
1314

1415
from unittest.mock import patch
@@ -455,5 +456,128 @@ def test_info_error(self):
455456
self.assertTrue(cm.exception.code)
456457

457458

459+
@requires_subprocess()
460+
class CommandLineTest(unittest.TestCase):
461+
"""Test the ``python -m zipapp`` command-line interface via subprocess."""
462+
463+
def setUp(self):
464+
tmpdir = tempfile.TemporaryDirectory()
465+
self.addCleanup(tmpdir.cleanup)
466+
self.tmpdir = pathlib.Path(tmpdir.name)
467+
468+
def _run(self, *args):
469+
return subprocess.run(
470+
[sys.executable, '-m', 'zipapp', *args],
471+
capture_output=True,
472+
)
473+
474+
def _make_source(self, *, main_content=b''):
475+
source = self.tmpdir / 'source'
476+
source.mkdir()
477+
(source / '__main__.py').write_bytes(main_content)
478+
return source
479+
480+
def _make_archive(self, *, interpreter=None):
481+
source = self._make_source()
482+
target = self.tmpdir / 'source.pyz'
483+
zipapp.create_archive(source, target, interpreter=interpreter)
484+
return target
485+
486+
def test_help(self):
487+
result = self._run('--help')
488+
self.assertEqual(result.returncode, 0)
489+
self.assertIn(b'usage:', result.stdout)
490+
491+
def test_create_archive(self):
492+
source = self._make_source()
493+
result = self._run(str(source))
494+
self.assertEqual(result.returncode, 0, result.stderr)
495+
target = source.with_suffix('.pyz')
496+
self.assertTrue(target.is_file())
497+
498+
def test_create_archive_with_output(self):
499+
source = self._make_source()
500+
target = self.tmpdir / 'out.pyz'
501+
result = self._run(str(source), '-o', str(target))
502+
self.assertEqual(result.returncode, 0, result.stderr)
503+
self.assertTrue(target.is_file())
504+
505+
def test_create_archive_with_interpreter(self):
506+
source = self._make_source()
507+
target = self.tmpdir / 'out.pyz'
508+
result = self._run(str(source), '-o', str(target), '-p', '/usr/bin/env python3')
509+
self.assertEqual(result.returncode, 0, result.stderr)
510+
self.assertTrue(target.is_file())
511+
self.assertEqual(zipapp.get_interpreter(target), '/usr/bin/env python3')
512+
513+
def test_create_archive_with_main(self):
514+
source = self.tmpdir / 'pkg'
515+
source.mkdir()
516+
(source / 'mod.py').write_text('def fn(): pass\n', encoding='utf-8')
517+
target = self.tmpdir / 'out.pyz'
518+
result = self._run(str(source), '-o', str(target), '-m', 'mod:fn')
519+
self.assertEqual(result.returncode, 0, result.stderr)
520+
self.assertTrue(target.is_file())
521+
with zipfile.ZipFile(target, 'r') as z:
522+
self.assertIn('__main__.py', z.namelist())
523+
524+
@requires_zlib()
525+
def test_create_archive_with_compress(self):
526+
source = self._make_source(main_content=b'print("hello")\n')
527+
target = self.tmpdir / 'out.pyz'
528+
result = self._run(str(source), '-o', str(target), '-c')
529+
self.assertEqual(result.returncode, 0, result.stderr)
530+
with zipfile.ZipFile(target, 'r') as z:
531+
self.assertEqual(z.getinfo('__main__.py').compress_type,
532+
zipfile.ZIP_DEFLATED)
533+
534+
def test_info_command(self):
535+
target = self._make_archive(interpreter='python3')
536+
result = self._run(str(target), '--info')
537+
self.assertEqual(result.returncode, 0, result.stderr)
538+
self.assertIn(b'Interpreter: python3', result.stdout)
539+
540+
def test_info_no_interpreter(self):
541+
target = self._make_archive()
542+
result = self._run(str(target), '--info')
543+
self.assertEqual(result.returncode, 0, result.stderr)
544+
self.assertIn(b'Interpreter: <none>', result.stdout)
545+
546+
def test_info_nonexistent_file(self):
547+
target = self.tmpdir / 'nonexistent.pyz'
548+
result = self._run(str(target), '--info')
549+
self.assertNotEqual(result.returncode, 0)
550+
551+
def test_copy_archive(self):
552+
original = self._make_archive()
553+
target = self.tmpdir / 'copy.pyz'
554+
result = self._run(str(original), '-o', str(target))
555+
self.assertEqual(result.returncode, 0, result.stderr)
556+
self.assertTrue(target.is_file())
557+
558+
def test_copy_archive_inplace_fails(self):
559+
original = self._make_archive()
560+
result = self._run(str(original), '-o', str(original))
561+
self.assertNotEqual(result.returncode, 0)
562+
563+
def test_copy_archive_change_main_fails(self):
564+
original = self._make_archive()
565+
target = self.tmpdir / 'copy.pyz'
566+
result = self._run(str(original), '-o', str(target), '-m', 'foo:bar')
567+
self.assertNotEqual(result.returncode, 0)
568+
569+
def test_no_source_argument(self):
570+
result = self._run()
571+
self.assertNotEqual(result.returncode, 0)
572+
self.assertIn(b'usage:', result.stderr)
573+
574+
def test_no_main_in_source(self):
575+
source = self.tmpdir / 'empty'
576+
source.mkdir()
577+
(source / 'foo.py').touch()
578+
result = self._run(str(source))
579+
self.assertNotEqual(result.returncode, 0)
580+
581+
458582
if __name__ == "__main__":
459583
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add subprocess-based tests for the :mod:`zipapp` command-line interface.

0 commit comments

Comments
 (0)