From 6341b835b34ecc1af4d5292356d15ee116be5c71 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 28 Mar 2022 20:14:53 +0100 Subject: [PATCH 1/8] Add ZipFile.mkdir --- Doc/library/zipfile.rst | 12 +++++++++ Lib/test/test_zipfile.py | 32 ++++++++++++++++++++++++ Lib/zipfile.py | 53 +++++++++++++++++++++++++++------------- a.zip | 0 4 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 a.zip diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index bfcc883de69271..5accf2d682e729 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -478,6 +478,18 @@ ZipFile Objects a closed ZipFile will raise a :exc:`ValueError`. Previously, a :exc:`RuntimeError` was raised. +.. method:: ZipFile.mkdir(zinfo_or_directory, mode=511) + + .. versionadded:: 3.11 + + Create a directory inside the archive. If *zinfo_or_directory* is a string, + a directory is created inside the archive with the mode that is specified in + the *mode* argument. If, however, *zinfo_or_directory* is + a :class:`ZipInfo` instance then the *mode* argument is ignored. + + The archive must be opened with mode ``'w'``, ``'x'`` or ``'a'``. + + The following data attributes are also available: diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 26c40457e62a05..999a2d90aeff4f 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -2637,6 +2637,38 @@ def test_writestr_dir(self): self.assertTrue(os.path.isdir(os.path.join(target, "x"))) self.assertEqual(os.listdir(target), ["x"]) + def test_mkdir(self): + with zipfile.ZipFile(TESTFN, "w") as zf: + zf.mkdir("directory") + zinfo = zf.filelist[0] + self.assertEqual(zinfo.filename, "directory/") + self.assertEqual(zinfo.external_attr, (511 << 16) | 0x10) + + zf.mkdir("directory2/") + zinfo = zf.filelist[1] + self.assertEqual(zinfo.filename, "directory2/") + self.assertEqual(zinfo.external_attr, (511 << 16) | 0x10) + + zf.mkdir("directory3", mode=777) + zinfo = zf.filelist[2] + self.assertEqual(zinfo.filename, "directory3/") + self.assertEqual(zinfo.external_attr, (777 << 16) | 0x10) + + old_zinfo = zipfile.ZipInfo("directory4/") + old_zinfo.external_attr = (511 << 16) | 0x10 + old_zinfo.CRC = 0 + old_zinfo.file_size = 0 + old_zinfo.compress_size = 0 + zf.mkdir(old_zinfo, mode=777) + new_zinfo = zf.filelist[3] + self.assertEqual(old_zinfo.filename, "directory4/") + self.assertEqual(old_zinfo.external_attr, new_zinfo.external_attr) + + target = os.path.join(TESTFN2, "target") + os.mkdir(target) + zf.extractall(target) + self.assertEqual(os.listdir(target), ["directory", "directory2", "directory3", "directory4"]) + def tearDown(self): rmtree(TESTFN2) if os.path.exists(TESTFN): diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 721834aff13a74..ff3cd64f0a9b29 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -1772,6 +1772,7 @@ def write(self, filename, arcname=None, if zinfo.is_dir(): zinfo.compress_size = 0 zinfo.CRC = 0 + self.mkdir(zinfo) else: if compress_type is not None: zinfo.compress_type = compress_type @@ -1783,23 +1784,6 @@ def write(self, filename, arcname=None, else: zinfo._compresslevel = self.compresslevel - if zinfo.is_dir(): - with self._lock: - if self._seekable: - self.fp.seek(self.start_dir) - zinfo.header_offset = self.fp.tell() # Start of header bytes - if zinfo.compress_type == ZIP_LZMA: - # Compressed data includes an end-of-stream (EOS) marker - zinfo.flag_bits |= _MASK_COMPRESS_OPTION_1 - - self._writecheck(zinfo) - self._didModify = True - - self.filelist.append(zinfo) - self.NameToInfo[zinfo.filename] = zinfo - self.fp.write(zinfo.FileHeader(False)) - self.start_dir = self.fp.tell() - else: with open(filename, "rb") as src, self.open(zinfo, 'w') as dest: shutil.copyfileobj(src, dest, 1024*8) @@ -1844,6 +1828,41 @@ def writestr(self, zinfo_or_arcname, data, with self.open(zinfo, mode='w') as dest: dest.write(data) + def mkdir(self, zinfo_or_directory_name, mode=511): + """Creates a directory inside the zip archive.""" + if isinstance(zinfo_or_directory_name, ZipInfo): + zinfo = zinfo_or_directory_name + if not zinfo.is_dir(): + raise ValueError("The given ZipInfo does not describe a directory") + elif isinstance(zinfo_or_directory_name, str): + directory_name = zinfo_or_directory_name + if not directory_name.endswith("/"): + directory_name += "/" + zinfo = ZipInfo(directory_name) + zinfo.compress_size = 0 + zinfo.CRC = 0 + zinfo.external_attr = (mode & 0xFFFF) << 16 + zinfo.file_size = 0 + zinfo.external_attr |= 0x10 + else: + raise TypeError("Expected type str or ZipInfo") + + with self._lock: + if self._seekable: + self.fp.seek(self.start_dir) + zinfo.header_offset = self.fp.tell() # Start of header bytes + if zinfo.compress_type == ZIP_LZMA: + # Compressed data includes an end-of-stream (EOS) marker + zinfo.flag_bits |= _MASK_COMPRESS_OPTION_1 + + self._writecheck(zinfo) + self._didModify = True + + self.filelist.append(zinfo) + self.NameToInfo[zinfo.filename] = zinfo + self.fp.write(zinfo.FileHeader(False)) + self.start_dir = self.fp.tell() + def __del__(self): """Call the "close()" method in case the user forgot.""" self.close() diff --git a/a.zip b/a.zip new file mode 100644 index 00000000000000..e69de29bb2d1d6 From 5c441ad73d695fc5ad58c2f6774275bfcbcd19cb Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 28 Mar 2022 20:17:16 +0100 Subject: [PATCH 2/8] Add news entry --- Misc/NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst diff --git a/Misc/NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst b/Misc/NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst new file mode 100644 index 00000000000000..8dd98e592daedf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst @@ -0,0 +1 @@ +Add ZipFile.mkdir From cc3c76eb6845961862fa2aa237ffe52bb798fc7d Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 28 Mar 2022 20:33:35 +0100 Subject: [PATCH 3/8] Remove example zip file --- a.zip | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 a.zip diff --git a/a.zip b/a.zip deleted file mode 100644 index e69de29bb2d1d6..00000000000000 From 1028df20dcc07db2cb4eb576808dd37b62578bf8 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Mon, 28 Mar 2022 20:53:24 +0100 Subject: [PATCH 4/8] Allow directory ordering to vary in tests --- Lib/test/test_zipfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 999a2d90aeff4f..cb0c18039e948b 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -2667,7 +2667,7 @@ def test_mkdir(self): target = os.path.join(TESTFN2, "target") os.mkdir(target) zf.extractall(target) - self.assertEqual(os.listdir(target), ["directory", "directory2", "directory3", "directory4"]) + self.assertEqual(set(os.listdir(target)), {"directory", "directory2", "directory3", "directory4"}) def tearDown(self): rmtree(TESTFN2) From 702f0d89492af36ba1aae8b66ac32e5acbdaebb5 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Tue, 29 Mar 2022 15:37:39 +0100 Subject: [PATCH 5/8] Move version directive to end of block --- Doc/library/zipfile.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index 5accf2d682e729..d6a1fce49c545e 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -480,8 +480,6 @@ ZipFile Objects .. method:: ZipFile.mkdir(zinfo_or_directory, mode=511) - .. versionadded:: 3.11 - Create a directory inside the archive. If *zinfo_or_directory* is a string, a directory is created inside the archive with the mode that is specified in the *mode* argument. If, however, *zinfo_or_directory* is @@ -489,6 +487,7 @@ ZipFile Objects The archive must be opened with mode ``'w'``, ``'x'`` or ``'a'``. + .. versionadded:: 3.11 The following data attributes are also available: From 4a93d152313e55819d50c6c1106c735895b70127 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Fri, 1 Apr 2022 04:40:15 +0100 Subject: [PATCH 6/8] Add fille type flag to ZipFile.mkdir, add ZipFIle.write directory tests --- Lib/test/test_zipfile.py | 33 +++++++++++++++++++++++++++------ Lib/zipfile.py | 2 +- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index cb0c18039e948b..9098e11683ae01 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -2642,24 +2642,24 @@ def test_mkdir(self): zf.mkdir("directory") zinfo = zf.filelist[0] self.assertEqual(zinfo.filename, "directory/") - self.assertEqual(zinfo.external_attr, (511 << 16) | 0x10) + self.assertEqual(zinfo.external_attr, (0o40777 << 16) | 0x10) zf.mkdir("directory2/") zinfo = zf.filelist[1] self.assertEqual(zinfo.filename, "directory2/") - self.assertEqual(zinfo.external_attr, (511 << 16) | 0x10) + self.assertEqual(zinfo.external_attr, (0o40777 << 16) | 0x10) - zf.mkdir("directory3", mode=777) + zf.mkdir("directory3", mode=0o777) zinfo = zf.filelist[2] self.assertEqual(zinfo.filename, "directory3/") - self.assertEqual(zinfo.external_attr, (777 << 16) | 0x10) + self.assertEqual(zinfo.external_attr, (0o40777 << 16) | 0x10) old_zinfo = zipfile.ZipInfo("directory4/") - old_zinfo.external_attr = (511 << 16) | 0x10 + old_zinfo.external_attr = (0o40777 << 16) | 0x10 old_zinfo.CRC = 0 old_zinfo.file_size = 0 old_zinfo.compress_size = 0 - zf.mkdir(old_zinfo, mode=777) + zf.mkdir(old_zinfo) new_zinfo = zf.filelist[3] self.assertEqual(old_zinfo.filename, "directory4/") self.assertEqual(old_zinfo.external_attr, new_zinfo.external_attr) @@ -2669,6 +2669,27 @@ def test_mkdir(self): zf.extractall(target) self.assertEqual(set(os.listdir(target)), {"directory", "directory2", "directory3", "directory4"}) + def test_create_directory_with_write(self): + with zipfile.ZipFile(TESTFN, "w") as zf: + zf.writestr(zipfile.ZipInfo('directory/'), '') + + zinfo = zf.filelist[0] + self.assertEqual(zinfo.filename, "directory/") + + directory = os.path.join(TESTFN2, "directory2") + os.mkdir(directory) + mode = os.stat(directory).st_mode + zf.write(directory, arcname="directory2/") + zinfo = zf.filelist[1] + self.assertEqual(zinfo.filename, "directory2/") + self.assertEqual(zinfo.external_attr, ((0o40000 | mode) << 16) | 0x10) + + target = os.path.join(TESTFN2, "target") + os.mkdir(target) + zf.extractall(target) + + self.assertEqual(set(os.listdir(target)), {"directory", "directory2"}) + def tearDown(self): rmtree(TESTFN2) if os.path.exists(TESTFN): diff --git a/Lib/zipfile.py b/Lib/zipfile.py index ff3cd64f0a9b29..dc02011084329a 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -1841,7 +1841,7 @@ def mkdir(self, zinfo_or_directory_name, mode=511): zinfo = ZipInfo(directory_name) zinfo.compress_size = 0 zinfo.CRC = 0 - zinfo.external_attr = (mode & 0xFFFF) << 16 + zinfo.external_attr = ((0o40000 | mode) & 0xFFFF) << 16 zinfo.file_size = 0 zinfo.external_attr |= 0x10 else: From 0b4877a43f1b70c6a17d0f51c739be92bf063730 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Fri, 1 Apr 2022 05:15:56 +0100 Subject: [PATCH 7/8] Remove redundant file type mask application --- Lib/test/test_zipfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 9098e11683ae01..17111b3a40fef9 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -2682,7 +2682,7 @@ def test_create_directory_with_write(self): zf.write(directory, arcname="directory2/") zinfo = zf.filelist[1] self.assertEqual(zinfo.filename, "directory2/") - self.assertEqual(zinfo.external_attr, ((0o40000 | mode) << 16) | 0x10) + self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10) target = os.path.join(TESTFN2, "target") os.mkdir(target) From 0387bfb3c8d73432fe49d6eaa4eb90fa1e2e7ef4 Mon Sep 17 00:00:00 2001 From: Sam Ezeh Date: Fri, 1 Apr 2022 21:42:42 +0100 Subject: [PATCH 8/8] Amend new entry --- .../NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst b/Misc/NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst index 8dd98e592daedf..7696091221cb5a 100644 --- a/Misc/NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst +++ b/Misc/NEWS.d/next/Library/2022-03-28-20-16-37.bpo-4833.2vSUE5.rst @@ -1 +1 @@ -Add ZipFile.mkdir +Add :meth:`ZipFile.mkdir` pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy