From dc88d4787d9b750ff69a5eac2e7082a22b874b31 Mon Sep 17 00:00:00 2001
From: Paul Moore
Date: Wed, 9 Aug 2017 20:34:59 +0100
Subject: [PATCH 1/2] bpo-31072: Rename the new filter argument for
zipapp.create_archive.
* Rename the new argument to "filterfunc"
* Improve tests for the new functionality
* Add a "What's New" entry.
---
Doc/library/zipapp.rst | 12 ++++++++----
Doc/whatsnew/3.7.rst | 7 +++++++
Lib/test/test_zipapp.py | 31 ++++++++++++++++++++++++++-----
Lib/zipapp.py | 8 ++++----
4 files changed, 45 insertions(+), 13 deletions(-)
diff --git a/Doc/library/zipapp.rst b/Doc/library/zipapp.rst
index 993c2ccc39883a8..1cf58ebf33d12ce 100644
--- a/Doc/library/zipapp.rst
+++ b/Doc/library/zipapp.rst
@@ -99,7 +99,7 @@ The module defines two convenience functions:
.. function:: create_archive(source, target=None, interpreter=None, main=None,
- include_file=None)
+ filterfunc=None)
Create an application archive from *source*. The source can be any
of the following:
@@ -144,9 +144,10 @@ The module defines two convenience functions:
contain a ``__main__.py`` file, as otherwise the resulting archive
would not be executable.
- The *include_file* argument specifies a callback function that is passed the
- relative path to the file in order to determine which files to store when
- being called against a directory.
+ The optional *filterfunc* argument specifies a callback function that
+ is passed a Path object representing the path to the file being added
+ (relative to the source directory). It should return ``True`` if the
+ file is to be added.
If a file object is specified for *source* or *target*, it is the
caller's responsibility to close it after calling create_archive.
@@ -157,6 +158,9 @@ The module defines two convenience functions:
passed to the ``zipfile.ZipFile`` class, and must supply the methods
needed by that class.
+ .. versionadded:: 3.7
+ Added the *filterfunc* argument.
+
.. function:: get_interpreter(archive)
Return the interpreter specified in the ``#!`` line at the start of the
diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst
index 21e9c568ae42419..2337d36f8a5d78b 100644
--- a/Doc/whatsnew/3.7.rst
+++ b/Doc/whatsnew/3.7.rst
@@ -258,6 +258,13 @@ Function :func:`~uu.encode` now accepts an optional *backtick*
keyword argument. When it's true, zeros are represented by ``'`'``
instead of spaces. (Contributed by Xiang Zhang in :issue:`30103`.)
+zipapp
+------
+
+Function :func:`zipapp.create_archive` now accepts an optional *filterfunc*
+argument, to allow the user to select which files should be included in the
+archive.
+
Optimizations
=============
diff --git a/Lib/test/test_zipapp.py b/Lib/test/test_zipapp.py
index 47eed5f4a6cf83f..4dbc83ad269552c 100644
--- a/Lib/test/test_zipapp.py
+++ b/Lib/test/test_zipapp.py
@@ -53,10 +53,11 @@ def test_create_archive_with_subdirs(self):
self.assertIn('foo/', z.namelist())
self.assertIn('bar/', z.namelist())
- def test_create_archive_with_include_file(self):
- # Test packing a directory and using include_file to specify which files to include.
- def skip_pyc_files(file):
- return '.pyc' not in str(file)
+ def test_create_archive_with_filterfunc(self):
+ # Test packing a directory and using filterfunc to specify
+ # which files to include.
+ def skip_pyc_files(path):
+ return path.suffix != '.pyc'
source = self.tmpdir / 'source'
source.mkdir()
(source / '__main__.py').touch()
@@ -64,12 +65,32 @@ def skip_pyc_files(file):
(source / 'test.pyc').touch()
target = self.tmpdir / 'source.pyz'
- zipapp.create_archive(source, target, include_file=skip_pyc_files)
+ zipapp.create_archive(source, target, filterfunc=skip_pyc_files)
with zipfile.ZipFile(target, 'r') as z:
self.assertIn('__main__.py', z.namelist())
self.assertIn('test.py', z.namelist())
self.assertNotIn('test.pyc', z.namelist())
+ def test_create_archive_filterfunc_exclude_dir(self):
+ # Test packing a directory and using filterfunc to exclude a
+ # subdirectory (ensures that the path supplied to filterfunc
+ # is relative to the source location, as expected).
+ def skip_dummy_dir(path):
+ return path.parts[0] != 'dummy'
+ source = self.tmpdir / 'source'
+ source.mkdir()
+ (source / '__main__.py').touch()
+ (source / 'test.py').touch()
+ (source / 'dummy').mkdir()
+ (source / 'dummy' / 'test2.py').touch()
+ target = self.tmpdir / 'source.pyz'
+
+ zipapp.create_archive(source, target, filterfunc=skip_dummy_dir)
+ with zipfile.ZipFile(target, 'r') as z:
+ self.assertEqual(len(z.namelist()), 2)
+ self.assertIn('__main__.py', z.namelist())
+ self.assertIn('test.py', z.namelist())
+
def test_create_archive_default_target(self):
# Test packing a directory to the default name.
source = self.tmpdir / 'source'
diff --git a/Lib/zipapp.py b/Lib/zipapp.py
index bf15b6806dd217e..35ec6f3be3df660 100644
--- a/Lib/zipapp.py
+++ b/Lib/zipapp.py
@@ -74,7 +74,7 @@ def _copy_archive(archive, new_archive, interpreter=None):
def create_archive(source, target=None, interpreter=None, main=None,
- include_file=None):
+ filterfunc=None):
"""Create an application archive from SOURCE.
The SOURCE can be the name of a directory, or a filename or a file-like
@@ -135,9 +135,9 @@ def create_archive(source, target=None, interpreter=None, main=None,
_write_file_prefix(fd, interpreter)
with zipfile.ZipFile(fd, 'w') as z:
for child in source.rglob('*'):
- arcname = child.relative_to(source).as_posix()
- if include_file is None or include_file(pathlib.Path(arcname)):
- z.write(child, arcname)
+ arcname = child.relative_to(source)
+ if filterfunc is None or filterfunc(arcname):
+ z.write(child, arcname.as_posix())
if main_py:
z.writestr('__main__.py', main_py.encode('utf-8'))
From 494563b4d60317d357cf9a74ae2fd403102d7a0a Mon Sep 17 00:00:00 2001
From: Paul Moore
Date: Sat, 26 Aug 2017 17:45:21 +0100
Subject: [PATCH 2/2] Rename argument to 'filter'
---
Doc/library/zipapp.rst | 6 +++---
Doc/whatsnew/3.7.rst | 2 +-
Lib/test/test_zipapp.py | 14 +++++++-------
Lib/zipapp.py | 4 ++--
4 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/Doc/library/zipapp.rst b/Doc/library/zipapp.rst
index 1cf58ebf33d12ce..120bbbb66201f64 100644
--- a/Doc/library/zipapp.rst
+++ b/Doc/library/zipapp.rst
@@ -99,7 +99,7 @@ The module defines two convenience functions:
.. function:: create_archive(source, target=None, interpreter=None, main=None,
- filterfunc=None)
+ filter=None)
Create an application archive from *source*. The source can be any
of the following:
@@ -144,7 +144,7 @@ The module defines two convenience functions:
contain a ``__main__.py`` file, as otherwise the resulting archive
would not be executable.
- The optional *filterfunc* argument specifies a callback function that
+ The optional *filter* argument specifies a callback function that
is passed a Path object representing the path to the file being added
(relative to the source directory). It should return ``True`` if the
file is to be added.
@@ -159,7 +159,7 @@ The module defines two convenience functions:
needed by that class.
.. versionadded:: 3.7
- Added the *filterfunc* argument.
+ Added the *filter* argument.
.. function:: get_interpreter(archive)
diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst
index 2337d36f8a5d78b..22825524362f472 100644
--- a/Doc/whatsnew/3.7.rst
+++ b/Doc/whatsnew/3.7.rst
@@ -261,7 +261,7 @@ instead of spaces. (Contributed by Xiang Zhang in :issue:`30103`.)
zipapp
------
-Function :func:`zipapp.create_archive` now accepts an optional *filterfunc*
+Function :func:`zipapp.create_archive` now accepts an optional *filter*
argument, to allow the user to select which files should be included in the
archive.
diff --git a/Lib/test/test_zipapp.py b/Lib/test/test_zipapp.py
index 4dbc83ad269552c..56cf37c9afa03b4 100644
--- a/Lib/test/test_zipapp.py
+++ b/Lib/test/test_zipapp.py
@@ -53,8 +53,8 @@ def test_create_archive_with_subdirs(self):
self.assertIn('foo/', z.namelist())
self.assertIn('bar/', z.namelist())
- def test_create_archive_with_filterfunc(self):
- # Test packing a directory and using filterfunc to specify
+ def test_create_archive_with_filter(self):
+ # Test packing a directory and using filter to specify
# which files to include.
def skip_pyc_files(path):
return path.suffix != '.pyc'
@@ -65,15 +65,15 @@ def skip_pyc_files(path):
(source / 'test.pyc').touch()
target = self.tmpdir / 'source.pyz'
- zipapp.create_archive(source, target, filterfunc=skip_pyc_files)
+ zipapp.create_archive(source, target, filter=skip_pyc_files)
with zipfile.ZipFile(target, 'r') as z:
self.assertIn('__main__.py', z.namelist())
self.assertIn('test.py', z.namelist())
self.assertNotIn('test.pyc', z.namelist())
- def test_create_archive_filterfunc_exclude_dir(self):
- # Test packing a directory and using filterfunc to exclude a
- # subdirectory (ensures that the path supplied to filterfunc
+ def test_create_archive_filter_exclude_dir(self):
+ # Test packing a directory and using a filter to exclude a
+ # subdirectory (ensures that the path supplied to include
# is relative to the source location, as expected).
def skip_dummy_dir(path):
return path.parts[0] != 'dummy'
@@ -85,7 +85,7 @@ def skip_dummy_dir(path):
(source / 'dummy' / 'test2.py').touch()
target = self.tmpdir / 'source.pyz'
- zipapp.create_archive(source, target, filterfunc=skip_dummy_dir)
+ zipapp.create_archive(source, target, filter=skip_dummy_dir)
with zipfile.ZipFile(target, 'r') as z:
self.assertEqual(len(z.namelist()), 2)
self.assertIn('__main__.py', z.namelist())
diff --git a/Lib/zipapp.py b/Lib/zipapp.py
index 35ec6f3be3df660..51d0290a901ba8b 100644
--- a/Lib/zipapp.py
+++ b/Lib/zipapp.py
@@ -74,7 +74,7 @@ def _copy_archive(archive, new_archive, interpreter=None):
def create_archive(source, target=None, interpreter=None, main=None,
- filterfunc=None):
+ filter=None):
"""Create an application archive from SOURCE.
The SOURCE can be the name of a directory, or a filename or a file-like
@@ -136,7 +136,7 @@ def create_archive(source, target=None, interpreter=None, main=None,
with zipfile.ZipFile(fd, 'w') as z:
for child in source.rglob('*'):
arcname = child.relative_to(source)
- if filterfunc is None or filterfunc(arcname):
+ if filter is None or filter(arcname):
z.write(child, arcname.as_posix())
if main_py:
z.writestr('__main__.py', main_py.encode('utf-8'))