Issue 22201: python -mzipfile fails to unzip files with folders created by zip (original) (raw)

With Python 3.4.1:

$ mkdir foo; zip -r foo{,}; python -mzipfile -e foo.zip dest adding: foo/ (stored 0%) Traceback (most recent call last): File "/usr/lib/python3.4/runpy.py", line 170, in _run_module_as_main "main", mod_spec) File "/usr/lib/python3.4/runpy.py", line 85, in _run_code exec(code, run_globals) File "/usr/lib/python3.4/zipfile.py", line 1799, in main() File "/usr/lib/python3.4/zipfile.py", line 1777, in main with open(tgt, 'wb') as fp: IsADirectoryError: [Errno 21] Is a directory: 'dest/foo/'

Note that zipfile is actually able to unzip the file:

$ python -c 'from zipfile import *; ZipFile("foo.zip").extractall("dest")'

works fine.

If the zip file is created by "python -mzipfile -c foo.zip foo" then there is no problem. Likewise, if there are no folders in the zip file (but just a collection of files) then there is no problem.

The reason behind this was that zipfile.py currently handles empty directories in zipfiles incorrectly.

On lines 1774 - 1778 in Lib/zipfile.py:

tgtdir = os.path.dirname(tgt) if not os.path.exists(tgtdir): os.makedirs(tgtdir) with open(tgt, 'wb') as fp: fp.write(zf.read(path))

In the case described above, tgt is 'dest/foo/' because the directory is empty. For non-empty directories, tgt would be a file in the directory i.e. 'dest/foo/a'. In the empty directory case, the directory will be created, but then opened as file (which will throw the error shown above).

When compressing the file with 'python -mzipfile -c', zipfile.py would not add empty directories to the zipfile. Hence, the zip file generated is empty.

This patch fixes both issues. In the decompression case, I utilize the Zipfile.extractall() function instead of extracting each file manually. The extractall() function handles empty directories correctly. For the compression case, I added a check to add an empty directory to the zip file.