Skip to content

Commit 46a3492

Browse files
Issue #20912: Now directories added to ZIP file have correct Unix and MS-DOS
directory attributes.
1 parent 026a399 commit 46a3492

File tree

3 files changed

+50
-5
lines changed

3 files changed

+50
-5
lines changed

Lib/test/test_zipfile.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,11 +1696,48 @@ def test_bug_6050(self):
16961696
os.mkdir(os.path.join(TESTFN2, "a"))
16971697
self.test_extract_dir()
16981698

1699-
def test_store_dir(self):
1699+
def test_write_dir(self):
1700+
dirpath = os.path.join(TESTFN2, "x")
1701+
os.mkdir(dirpath)
1702+
mode = os.stat(dirpath).st_mode & 0xFFFF
1703+
with zipfile.ZipFile(TESTFN, "w") as zipf:
1704+
zipf.write(dirpath)
1705+
zinfo = zipf.filelist[0]
1706+
self.assertTrue(zinfo.filename.endswith("/x/"))
1707+
self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
1708+
zipf.write(dirpath, "y")
1709+
zinfo = zipf.filelist[1]
1710+
self.assertTrue(zinfo.filename, "y/")
1711+
self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
1712+
with zipfile.ZipFile(TESTFN, "r") as zipf:
1713+
zinfo = zipf.filelist[0]
1714+
self.assertTrue(zinfo.filename.endswith("/x/"))
1715+
self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
1716+
zinfo = zipf.filelist[1]
1717+
self.assertTrue(zinfo.filename, "y/")
1718+
self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
1719+
target = os.path.join(TESTFN2, "target")
1720+
os.mkdir(target)
1721+
zipf.extractall(target)
1722+
self.assertTrue(os.path.isdir(os.path.join(target, "y")))
1723+
self.assertEqual(len(os.listdir(target)), 2)
1724+
1725+
def test_writestr_dir(self):
17001726
os.mkdir(os.path.join(TESTFN2, "x"))
1701-
zipf = zipfile.ZipFile(TESTFN, "w")
1702-
zipf.write(os.path.join(TESTFN2, "x"), "x")
1703-
self.assertTrue(zipf.filelist[0].filename.endswith("x/"))
1727+
with zipfile.ZipFile(TESTFN, "w") as zipf:
1728+
zipf.writestr("x/", b'')
1729+
zinfo = zipf.filelist[0]
1730+
self.assertEqual(zinfo.filename, "x/")
1731+
self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
1732+
with zipfile.ZipFile(TESTFN, "r") as zipf:
1733+
zinfo = zipf.filelist[0]
1734+
self.assertTrue(zinfo.filename.endswith("x/"))
1735+
self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
1736+
target = os.path.join(TESTFN2, "target")
1737+
os.mkdir(target)
1738+
zipf.extractall(target)
1739+
self.assertTrue(os.path.isdir(os.path.join(target, "x")))
1740+
self.assertEqual(os.listdir(target), ["x"])
17041741

17051742
def tearDown(self):
17061743
rmtree(TESTFN2)

Lib/zipfile.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,7 @@ def write(self, filename, arcname=None, compress_type=None):
13561356
zinfo.file_size = 0
13571357
zinfo.compress_size = 0
13581358
zinfo.CRC = 0
1359+
zinfo.external_attr |= 0x10 # MS-DOS directory flag
13591360
self.filelist.append(zinfo)
13601361
self.NameToInfo[zinfo.filename] = zinfo
13611362
self.fp.write(zinfo.FileHeader(False))
@@ -1416,7 +1417,11 @@ def writestr(self, zinfo_or_arcname, data, compress_type=None):
14161417
zinfo = ZipInfo(filename=zinfo_or_arcname,
14171418
date_time=time.localtime(time.time())[:6])
14181419
zinfo.compress_type = self.compression
1419-
zinfo.external_attr = 0o600 << 16
1420+
if zinfo.filename[-1] == '/':
1421+
zinfo.external_attr = 0o40775 << 16 # drwxrwxr-x
1422+
zinfo.external_attr |= 0x10 # MS-DOS directory flag
1423+
else:
1424+
zinfo.external_attr = 0o600 << 16 # ?rw-------
14201425
else:
14211426
zinfo = zinfo_or_arcname
14221427

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ Core and Builtins
1313
Library
1414
-------
1515

16+
- Issue #20912: Now directories added to ZIP file have correct Unix and MS-DOS
17+
directory attributes.
18+
1619
- Issue #21866: ZipFile.close() no longer writes ZIP64 central directory
1720
records if allowZip64 is false.
1821

0 commit comments

Comments
 (0)