]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Merged revisions 68887 via svnmerge from
authorMartin v. Löwis <martin@v.loewis.de>
Sat, 24 Jan 2009 14:18:13 +0000 (14:18 +0000)
committerMartin v. Löwis <martin@v.loewis.de>
Sat, 24 Jan 2009 14:18:13 +0000 (14:18 +0000)
svn+ssh://pythondev@svn.python.org/python/branches/py3k

................
  r68887 | martin.v.loewis | 2009-01-24 15:10:07 +0100 (Sa, 24 Jan 2009) | 10 lines

  Merged revisions 68885 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r68885 | martin.v.loewis | 2009-01-24 15:00:33 +0100 (Sa, 24 Jan 2009) | 3 lines

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

Lib/test/test_zipfile.py
Lib/test/zipdir.zip [new file with mode: 0644]
Lib/zipfile.py
Misc/NEWS

index 9e565fb153f89930db387c21daefd716baf61b33..c85d21d0074d3fc3e760507fe4c78853ec021124 100644 (file)
@@ -9,9 +9,10 @@ from tempfile import TemporaryFile
 from random import randint, random
 
 import test.support as support
-from test.support import TESTFN, run_unittest
+from test.support import TESTFN, run_unittest, findfile
 
 TESTFN2 = TESTFN + "2"
+TESTFNDIR = TESTFN + "d"
 FIXEDTEST_SIZE = 1000
 
 SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'),
@@ -971,6 +972,28 @@ class TestsWithMultipleOpens(unittest.TestCase):
     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):
@@ -1085,6 +1108,7 @@ class UniversalNewlineTests(unittest.TestCase):
 def test_main():
     run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests,
                  PyZipFileTests, DecryptionTests, TestsWithMultipleOpens,
+                 TestWithDirectory,
                  UniversalNewlineTests, TestsWithRandomBinaryFiles)
 
 if __name__ == "__main__":
diff --git a/Lib/test/zipdir.zip b/Lib/test/zipdir.zip
new file mode 100644 (file)
index 0000000..ac21d7a
Binary files /dev/null and b/Lib/test/zipdir.zip differ
index 93543b4b06e77eda6ee8a38c98820f03ff207b38..661128ee15ca028c8693edb15cfc0a80e5262a4d 100644 (file)
@@ -4,7 +4,7 @@ Read and write ZIP files.
 XXX references to utf-8 need further investigation.
 """
 import struct, os, time, sys, shutil
-import binascii, io
+import binascii, io, stat
 
 try:
     import zlib # We may need its compression method
@@ -947,11 +947,11 @@ class ZipFile:
         """
         # 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)
@@ -963,6 +963,10 @@ class ZipFile:
         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 = open(targetpath, "wb")
         shutil.copyfileobj(source, target)
@@ -1002,6 +1006,7 @@ class ZipFile:
                   "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
@@ -1010,6 +1015,8 @@ class ZipFile:
         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 compress_type is None:
@@ -1023,6 +1030,16 @@ class ZipFile:
 
         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 = io.open(filename, "rb")
         # Must overwrite CRC and sizes with correct data later
         zinfo.CRC = CRC = 0
index 1a252d2a62faf23aa99d57343c1bb8a8b02e517b..92732cae503c4ef7ba9792778dd5891fc1480af3 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -100,6 +100,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #4710: Extract directories properly in the zipfile module;
+  allow adding directories to a zipfile.
+
 - Issue #5008: When a file is opened in append mode with the new IO library,
   do an explicit seek to the end of file (so that e.g. tell() returns the
   file size rather than 0). This is consistent with the behaviour of the