[Python-checkins] r68885 - in python/trunk: Lib/test/test_zipfile.py Lib/test/zipdir.zip Lib/zipfile.py Misc/NEWS

martin.v.loewis python-checkins at python.org
Sat Jan 24 15:00:47 CET 2009


Author: martin.v.loewis
Date: Sat Jan 24 15:00:33 2009
New Revision: 68885

Log:
Issue #4710: Extract directories properly in the zipfile module;
allow adding directories to a zipfile.


Added:
   python/trunk/Lib/test/zipdir.zip   (contents, props changed)
Modified:
   python/trunk/Lib/test/test_zipfile.py
   python/trunk/Lib/zipfile.py
   python/trunk/Misc/NEWS

Modified: python/trunk/Lib/test/test_zipfile.py
==============================================================================
--- python/trunk/Lib/test/test_zipfile.py	(original)
+++ python/trunk/Lib/test/test_zipfile.py	Sat Jan 24 15:00:33 2009
@@ -11,9 +11,10 @@
 from random import randint, random
 
 import test.test_support as support
-from test.test_support import TESTFN, run_unittest
+from test.test_support import TESTFN, run_unittest, findfile
 
 TESTFN2 = TESTFN + "2"
+TESTFNDIR = TESTFN + "d"
 FIXEDTEST_SIZE = 1000
 
 SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'),
@@ -1011,6 +1012,28 @@
     def tearDown(self):
         os.remove(TESTFN2)
 
+class TestWithDirectory(unittest.TestCase):
+    def setUp(self):
+        os.mkdir(TESTFN2)
+
+    def testExtractDir(self):
+        zipf = zipfile.ZipFile(findfile("zipdir.zip"))
+        zipf.extractall(TESTFN2)
+        self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a")))
+        self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b")))
+        self.assertTrue(os.path.exists(os.path.join(TESTFN2, "a", "b", "c")))
+
+    def testStoreDir(self):
+        os.mkdir(os.path.join(TESTFN2, "x"))
+        zipf = zipfile.ZipFile(TESTFN, "w")
+        zipf.write(os.path.join(TESTFN2, "x"), "x")
+        self.assertTrue(zipf.filelist[0].filename.endswith("x/"))
+
+    def tearDown(self):
+        shutil.rmtree(TESTFN2)
+        if os.path.exists(TESTFN):
+            os.remove(TESTFN)
+
 
 class UniversalNewlineTests(unittest.TestCase):
     def setUp(self):
@@ -1119,6 +1142,7 @@
 def test_main():
     run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests,
                  PyZipFileTests, DecryptionTests, TestsWithMultipleOpens,
+                 TestWithDirectory,
                  UniversalNewlineTests, TestsWithRandomBinaryFiles)
 
 if __name__ == "__main__":

Added: python/trunk/Lib/test/zipdir.zip
==============================================================================
Binary file. No diff available.

Modified: python/trunk/Lib/zipfile.py
==============================================================================
--- python/trunk/Lib/zipfile.py	(original)
+++ python/trunk/Lib/zipfile.py	Sat Jan 24 15:00:33 2009
@@ -2,7 +2,7 @@
 Read and write ZIP files.
 """
 import struct, os, time, sys, shutil
-import binascii, cStringIO
+import binascii, cStringIO, stat
 
 try:
     import zlib # We may need its compression method
@@ -952,11 +952,11 @@
         """
         # build the destination pathname, replacing
         # forward slashes to platform specific separators.
-        if targetpath[-1:] == "/":
+        if targetpath[-1:] in (os.path.sep, os.path.altsep):
             targetpath = targetpath[:-1]
 
         # don't include leading "/" from file name if present
-        if os.path.isabs(member.filename):
+        if member.filename[0] == '/':
             targetpath = os.path.join(targetpath, member.filename[1:])
         else:
             targetpath = os.path.join(targetpath, member.filename)
@@ -968,6 +968,10 @@
         if upperdirs and not os.path.exists(upperdirs):
             os.makedirs(upperdirs)
 
+        if member.filename[-1] == '/':
+            os.mkdir(targetpath)
+            return targetpath
+
         source = self.open(member, pwd=pwd)
         target = file(targetpath, "wb")
         shutil.copyfileobj(source, target)
@@ -1007,6 +1011,7 @@
                   "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
@@ -1015,6 +1020,8 @@
         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) << 16L      # Unix attributes
         if compress_type is None:
@@ -1028,6 +1035,16 @@
 
         self._writecheck(zinfo)
         self._didModify = True
+
+        if isdir:
+            zinfo.file_size = 0
+            zinfo.compress_size = 0
+            zinfo.CRC = 0
+            self.filelist.append(zinfo)
+            self.NameToInfo[zinfo.filename] = zinfo
+            self.fp.write(zinfo.FileHeader())
+            return
+
         fp = open(filename, "rb")
         # Must overwrite CRC and sizes with correct data later
         zinfo.CRC = CRC = 0

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Sat Jan 24 15:00:33 2009
@@ -145,6 +145,9 @@
 Library
 -------
 
+- Issue #4710: Extract directories properly in the zipfile module;
+  allow adding directories to a zipfile.
+
 - Issue #3807: _multiprocessing build fails when configure is passed 
   --without-threads argument. When this occurs, _multiprocessing will
   be disabled, and not compiled.


More information about the Python-checkins mailing list