Issue #4710: Extract directories properly in the zipfile module;

allow adding directories to a zipfile.
This commit is contained in:
Martin v. Löwis 2009-01-24 14:00:33 +00:00
parent 89e759d462
commit 0dfcfc8b59
4 changed files with 48 additions and 4 deletions

View File

@ -11,9 +11,10 @@ from tempfile import TemporaryFile
from random import randint, random from random import randint, random
import test.test_support as support 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" TESTFN2 = TESTFN + "2"
TESTFNDIR = TESTFN + "d"
FIXEDTEST_SIZE = 1000 FIXEDTEST_SIZE = 1000
SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'), SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'),
@ -1011,6 +1012,28 @@ class TestsWithMultipleOpens(unittest.TestCase):
def tearDown(self): def tearDown(self):
os.remove(TESTFN2) 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): class UniversalNewlineTests(unittest.TestCase):
def setUp(self): def setUp(self):
@ -1119,6 +1142,7 @@ class UniversalNewlineTests(unittest.TestCase):
def test_main(): def test_main():
run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests, run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests,
PyZipFileTests, DecryptionTests, TestsWithMultipleOpens, PyZipFileTests, DecryptionTests, TestsWithMultipleOpens,
TestWithDirectory,
UniversalNewlineTests, TestsWithRandomBinaryFiles) UniversalNewlineTests, TestsWithRandomBinaryFiles)
if __name__ == "__main__": if __name__ == "__main__":

BIN
Lib/test/zipdir.zip Normal file

Binary file not shown.

View File

@ -2,7 +2,7 @@
Read and write ZIP files. Read and write ZIP files.
""" """
import struct, os, time, sys, shutil import struct, os, time, sys, shutil
import binascii, cStringIO import binascii, cStringIO, stat
try: try:
import zlib # We may need its compression method import zlib # We may need its compression method
@ -952,11 +952,11 @@ class ZipFile:
""" """
# build the destination pathname, replacing # build the destination pathname, replacing
# forward slashes to platform specific separators. # forward slashes to platform specific separators.
if targetpath[-1:] == "/": if targetpath[-1:] in (os.path.sep, os.path.altsep):
targetpath = targetpath[:-1] targetpath = targetpath[:-1]
# don't include leading "/" from file name if present # 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:]) targetpath = os.path.join(targetpath, member.filename[1:])
else: else:
targetpath = os.path.join(targetpath, member.filename) targetpath = os.path.join(targetpath, member.filename)
@ -968,6 +968,10 @@ class ZipFile:
if upperdirs and not os.path.exists(upperdirs): if upperdirs and not os.path.exists(upperdirs):
os.makedirs(upperdirs) os.makedirs(upperdirs)
if member.filename[-1] == '/':
os.mkdir(targetpath)
return targetpath
source = self.open(member, pwd=pwd) source = self.open(member, pwd=pwd)
target = file(targetpath, "wb") target = file(targetpath, "wb")
shutil.copyfileobj(source, target) shutil.copyfileobj(source, target)
@ -1007,6 +1011,7 @@ class ZipFile:
"Attempt to write to ZIP archive that was already closed") "Attempt to write to ZIP archive that was already closed")
st = os.stat(filename) st = os.stat(filename)
isdir = stat.S_ISDIR(st.st_mode)
mtime = time.localtime(st.st_mtime) mtime = time.localtime(st.st_mtime)
date_time = mtime[0:6] date_time = mtime[0:6]
# Create ZipInfo instance to store file information # Create ZipInfo instance to store file information
@ -1015,6 +1020,8 @@ class ZipFile:
arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
while arcname[0] in (os.sep, os.altsep): while arcname[0] in (os.sep, os.altsep):
arcname = arcname[1:] arcname = arcname[1:]
if isdir:
arcname += '/'
zinfo = ZipInfo(arcname, date_time) zinfo = ZipInfo(arcname, date_time)
zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes
if compress_type is None: if compress_type is None:
@ -1028,6 +1035,16 @@ class ZipFile:
self._writecheck(zinfo) self._writecheck(zinfo)
self._didModify = True 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") fp = open(filename, "rb")
# Must overwrite CRC and sizes with correct data later # Must overwrite CRC and sizes with correct data later
zinfo.CRC = CRC = 0 zinfo.CRC = CRC = 0

View File

@ -145,6 +145,9 @@ Core and Builtins
Library 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 - Issue #3807: _multiprocessing build fails when configure is passed
--without-threads argument. When this occurs, _multiprocessing will --without-threads argument. When this occurs, _multiprocessing will
be disabled, and not compiled. be disabled, and not compiled.