self.assertIn('test.py', z.namelist())
self.assertNotIn('test.pyc', z.namelist())
+ def test_create_archive_self_insertion(self):
+ # When creating an archive, we shouldn't
+ # include the archive in the list of files to add.
+ source = self.tmpdir
+ (source / '__main__.py').touch()
+ (source / 'test.py').touch()
+ target = self.tmpdir / 'target.pyz'
+
+ zipapp.create_archive(source, target)
+ 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_target_overwrites_source_file(self):
+ # The target cannot be one of the files to add.
+ source = self.tmpdir
+ (source / '__main__.py').touch()
+ target = source / 'target.pyz'
+ target.touch()
+
+ with self.assertRaises(zipapp.ZipAppError):
+ zipapp.create_archive(source, target)
+
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
elif not hasattr(target, 'write'):
target = pathlib.Path(target)
+ # Create the list of files to add to the archive now, in case
+ # the target is being created in the source directory - we
+ # don't want the target being added to itself
+ files_to_add = sorted(source.rglob('*'))
+
+ # The target cannot be in the list of files to add. If it were, we'd
+ # end up overwriting the source file and writing the archive into
+ # itself, which is an error. We therefore check for that case and
+ # provide a helpful message for the user.
+
+ # Note that we only do a simple path equality check. This won't
+ # catch every case, but it will catch the common case where the
+ # source is the CWD and the target is a file in the CWD. More
+ # thorough checks don't provide enough value to justify the extra
+ # cost.
+
+ # If target is a file-like object, it will simply fail to compare
+ # equal to any of the entries in files_to_add, so there's no need
+ # to add a special check for that.
+ if target in files_to_add:
+ raise ZipAppError(
+ f"The target archive {target} overwrites one of the source files.")
+
with _maybe_open(target, 'wb') as fd:
_write_file_prefix(fd, interpreter)
compression = (zipfile.ZIP_DEFLATED if compressed else
zipfile.ZIP_STORED)
with zipfile.ZipFile(fd, 'w', compression=compression) as z:
- for child in sorted(source.rglob('*')):
+ for child in files_to_add:
arcname = child.relative_to(source)
- if filter is None or filter(arcname) and child.resolve() != arcname.resolve():
+ 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'))