Skip to content

Commit d87b105

Browse files
zmwangxpfmoore
authored andcommitted
bpo-31638: Add compression support to zipapp (GH-3819)
Add optional argument `compressed` to `zipapp.create_archive`, and add option `--compress` to the command line interface of `zipapp`.
1 parent 6fb0e4a commit d87b105

File tree

5 files changed

+44
-7
lines changed

5 files changed

+44
-7
lines changed

Doc/library/zipapp.rst

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ The following options are understood:
7979

8080
:option:`--main` cannot be specified when copying an archive.
8181

82+
.. cmdoption:: -c, --compress
83+
84+
Compress files with the deflate method, reducing the size of the output
85+
file. By default, files are stored uncompressed in the archive.
86+
87+
:option:`--compress` has no effect when copying an archive.
88+
8289
.. cmdoption:: --info
8390

8491
Display the interpreter embedded in the archive, for diagnostic purposes. In
@@ -98,8 +105,7 @@ Python API
98105
The module defines two convenience functions:
99106

100107

101-
.. function:: create_archive(source, target=None, interpreter=None, main=None,
102-
filter=None)
108+
.. function:: create_archive(source, target=None, interpreter=None, main=None, filter=None, compressed=False)
103109

104110
Create an application archive from *source*. The source can be any
105111
of the following:
@@ -149,6 +155,11 @@ The module defines two convenience functions:
149155
(relative to the source directory). It should return ``True`` if the
150156
file is to be added.
151157

158+
The optional *compressed* argument determines whether files are
159+
compressed. If set to ``True``, files in the archive are compressed
160+
with the deflate method; otherwise, files are stored uncompressed.
161+
This argument has no effect when copying an existing archive.
162+
152163
If a file object is specified for *source* or *target*, it is the
153164
caller's responsibility to close it after calling create_archive.
154165

@@ -159,7 +170,7 @@ The module defines two convenience functions:
159170
needed by that class.
160171

161172
.. versionadded:: 3.7
162-
Added the *filter* argument.
173+
Added the *filter* and *compressed* arguments.
163174

164175
.. function:: get_interpreter(archive)
165176

Doc/whatsnew/3.7.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,13 @@ zipapp
285285
------
286286

287287
Function :func:`zipapp.create_archive` now accepts an optional *filter*
288-
argument, to allow the user to select which files should be included in the
288+
argument to allow the user to select which files should be included in the
289+
archive, and an optional *compressed* argument to generate a compressed
289290
archive.
290291

292+
A command line option ``--compress`` has also been added to support
293+
compression.
294+
291295

292296
Optimizations
293297
=============

Lib/test/test_zipapp.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ def test_create_archive_default_target(self):
100100
expected_target = self.tmpdir / 'source.pyz'
101101
self.assertTrue(expected_target.is_file())
102102

103+
def test_create_archive_with_compression(self):
104+
# Test packing a directory into a compressed archive.
105+
source = self.tmpdir / 'source'
106+
source.mkdir()
107+
(source / '__main__.py').touch()
108+
(source / 'test.py').touch()
109+
target = self.tmpdir / 'source.pyz'
110+
111+
zipapp.create_archive(source, target, compressed=True)
112+
with zipfile.ZipFile(target, 'r') as z:
113+
for name in ('__main__.py', 'test.py'):
114+
self.assertEqual(z.getinfo(name).compress_type,
115+
zipfile.ZIP_DEFLATED)
116+
103117
def test_no_main(self):
104118
# Test that packing a directory with no __main__.py fails.
105119
source = self.tmpdir / 'source'

Lib/zipapp.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def _copy_archive(archive, new_archive, interpreter=None):
7474

7575

7676
def create_archive(source, target=None, interpreter=None, main=None,
77-
filter=None):
77+
filter=None, compressed=False):
7878
"""Create an application archive from SOURCE.
7979
8080
The SOURCE can be the name of a directory, or a filename or a file-like
@@ -133,7 +133,9 @@ def create_archive(source, target=None, interpreter=None, main=None,
133133

134134
with _maybe_open(target, 'wb') as fd:
135135
_write_file_prefix(fd, interpreter)
136-
with zipfile.ZipFile(fd, 'w') as z:
136+
compression = (zipfile.ZIP_DEFLATED if compressed else
137+
zipfile.ZIP_STORED)
138+
with zipfile.ZipFile(fd, 'w', compression=compression) as z:
137139
for child in source.rglob('*'):
138140
arcname = child.relative_to(source)
139141
if filter is None or filter(arcname):
@@ -170,6 +172,9 @@ def main(args=None):
170172
parser.add_argument('--main', '-m', default=None,
171173
help="The main function of the application "
172174
"(default: use an existing __main__.py).")
175+
parser.add_argument('--compress', '-c', action='store_true',
176+
help="Compress files with the deflate method. "
177+
"Files are stored uncompressed by default.")
173178
parser.add_argument('--info', default=False, action='store_true',
174179
help="Display the interpreter from the archive.")
175180
parser.add_argument('source',
@@ -193,7 +198,8 @@ def main(args=None):
193198
raise SystemExit("Cannot change the main function when copying")
194199

195200
create_archive(args.source, args.output,
196-
interpreter=args.python, main=args.main)
201+
interpreter=args.python, main=args.main,
202+
compressed=args.compress)
197203

198204

199205
if __name__ == '__main__':
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add optional argument ``compressed`` to ``zipapp.create_archive``, and add
2+
option ``--compress`` to the command line interface of ``zipapp``.

0 commit comments

Comments
 (0)