From 59e4779abc25626cb0ab9fa24ccb3223a00db143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 24 Jan 2009 14:10:07 +0000 Subject: 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 | 26 +++++++++++++++++++++++++- Lib/test/zipdir.zip | Bin 0 -> 374 bytes Lib/zipfile.py | 23 ++++++++++++++++++++--- Misc/NEWS | 3 +++ 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 Lib/test/zipdir.zip diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 678ac9f..3b04ce2 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -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'), @@ -1000,6 +1001,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): @@ -1114,6 +1137,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 index 0000000..ac21d7a Binary files /dev/null and b/Lib/test/zipdir.zip differ diff --git a/Lib/zipfile.py b/Lib/zipfile.py index c67bd5d..a54a354 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -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 @@ -959,11 +959,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) @@ -975,6 +975,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) @@ -1014,6 +1018,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 @@ -1022,6 +1027,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: @@ -1035,6 +1042,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 diff --git a/Misc/NEWS b/Misc/NEWS index cf9e885..9d27971 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -139,6 +139,9 @@ Core and Builtins 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. -- cgit v0.12