summaryrefslogtreecommitdiff
path: root/Lib
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2016-02-08 00:02:25 +0200
committerSerhiy Storchaka <storchaka@gmail.com>2016-02-08 00:02:25 +0200
commit503f908090bd22df70ed21e3bb3c2513a1eee969 (patch)
tree2722994f3caf3551cf4cfe9556df3f27b4d58e78 /Lib
parent46988d365946f2b61812b6ff60cb557e036fa6b1 (diff)
downloadcpython-git-503f908090bd22df70ed21e3bb3c2513a1eee969.tar.gz
Issue #26039: Added zipfile.ZipInfo.from_file() and zipinfo.ZipInfo.is_dir().
Patch by Thomas Kluyver.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_zipfile.py15
-rw-r--r--Lib/zipfile.py75
2 files changed, 62 insertions, 28 deletions
diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py
index 2c10821a0c..8589342a80 100644
--- a/Lib/test/test_zipfile.py
+++ b/Lib/test/test_zipfile.py
@@ -3,6 +3,7 @@ import io
import os
import sys
import importlib.util
+import posixpath
import time
import struct
import zipfile
@@ -2071,5 +2072,19 @@ class LzmaUniversalNewlineTests(AbstractUniversalNewlineTests,
unittest.TestCase):
compression = zipfile.ZIP_LZMA
+class ZipInfoTests(unittest.TestCase):
+ def test_from_file(self):
+ zi = zipfile.ZipInfo.from_file(__file__)
+ self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
+ self.assertFalse(zi.is_dir())
+
+ def test_from_dir(self):
+ dirpath = os.path.dirname(os.path.abspath(__file__))
+ zi = zipfile.ZipInfo.from_file(dirpath, 'stdlib_tests')
+ self.assertEqual(zi.filename, 'stdlib_tests/')
+ self.assertTrue(zi.is_dir())
+ self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
+ self.assertEqual(zi.file_size, 0)
+
if __name__ == "__main__":
unittest.main()
diff --git a/Lib/zipfile.py b/Lib/zipfile.py
index 56a2479fb3..e0598d27ed 100644
--- a/Lib/zipfile.py
+++ b/Lib/zipfile.py
@@ -371,7 +371,7 @@ class ZipInfo (object):
result.append(' filemode=%r' % stat.filemode(hi))
if lo:
result.append(' external_attr=%#x' % lo)
- isdir = self.filename[-1:] == '/'
+ isdir = self.is_dir()
if not isdir or self.file_size:
result.append(' file_size=%r' % self.file_size)
if ((not isdir or self.compress_size) and
@@ -469,6 +469,41 @@ class ZipInfo (object):
extra = extra[ln+4:]
+ @classmethod
+ def from_file(cls, filename, arcname=None):
+ """Construct an appropriate ZipInfo for a file on the filesystem.
+
+ filename should be the path to a file or directory on the filesystem.
+
+ arcname is the name which it will have within the archive (by default,
+ this will be the same as filename, but without a drive letter and with
+ leading path separators removed).
+ """
+ st = os.stat(filename)
+ isdir = stat.S_ISDIR(st.st_mode)
+ mtime = time.localtime(st.st_mtime)
+ date_time = mtime[0:6]
+ # Create ZipInfo instance to store file information
+ if arcname is None:
+ arcname = filename
+ arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
+ while arcname[0] in (os.sep, os.altsep):
+ arcname = arcname[1:]
+ if isdir:
+ arcname += '/'
+ zinfo = cls(arcname, date_time)
+ zinfo.external_attr = (st.st_mode & 0xFFFF) << 16 # Unix attributes
+ if isdir:
+ zinfo.file_size = 0
+ zinfo.external_attr |= 0x10 # MS-DOS directory flag
+ else:
+ zinfo.file_size = st.st_size
+
+ return zinfo
+
+ def is_dir(self):
+ return self.filename[-1] == '/'
+
class _ZipDecrypter:
"""Class to handle decryption of files stored within a ZIP archive.
@@ -1389,7 +1424,7 @@ class ZipFile:
if upperdirs and not os.path.exists(upperdirs):
os.makedirs(upperdirs)
- if member.filename[-1] == '/':
+ if member.is_dir():
if not os.path.isdir(targetpath):
os.mkdir(targetpath)
return targetpath
@@ -1430,29 +1465,17 @@ class ZipFile:
raise RuntimeError(
"Attempt to write to ZIP archive that was already closed")
- st = os.stat(filename)
- isdir = stat.S_ISDIR(st.st_mode)
- mtime = time.localtime(st.st_mtime)
- date_time = mtime[0:6]
- # Create ZipInfo instance to store file information
- if arcname is None:
- arcname = filename
- arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
- while arcname[0] in (os.sep, os.altsep):
- arcname = arcname[1:]
- if isdir:
- arcname += '/'
- zinfo = ZipInfo(arcname, date_time)
- zinfo.external_attr = (st[0] & 0xFFFF) << 16 # Unix attributes
- if isdir:
- zinfo.compress_type = ZIP_STORED
- elif compress_type is None:
- zinfo.compress_type = self.compression
+ zinfo = ZipInfo.from_file(filename, arcname)
+
+ if zinfo.is_dir():
+ zinfo.compress_size = 0
+ zinfo.CRC = 0
else:
- zinfo.compress_type = compress_type
+ if compress_type is not None:
+ zinfo.compress_type = compress_type
+ else:
+ zinfo.compress_type = self.compression
- zinfo.file_size = st.st_size
- zinfo.flag_bits = 0x00
with self._lock:
if self._seekable:
self.fp.seek(self.start_dir)
@@ -1464,11 +1487,7 @@ class ZipFile:
self._writecheck(zinfo)
self._didModify = True
- if isdir:
- zinfo.file_size = 0
- zinfo.compress_size = 0
- zinfo.CRC = 0
- zinfo.external_attr |= 0x10 # MS-DOS directory flag
+ if zinfo.is_dir():
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
self.fp.write(zinfo.FileHeader(False))