From 659eb048cc9cac73c46349eb29845bc5cd630f09 Mon Sep 17 00:00:00 2001 From: nergall2 <63109814+nergall2@users.noreply.github.com> Date: Fri, 3 Apr 2020 18:42:21 +0300 Subject: [PATCH] enhancement-40175: Add remove() in ZipInfo --- Lib/zipfile.py | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Lib/zipfile.py b/Lib/zipfile.py index c3f814cc747e06..0699d40766b048 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -17,6 +17,7 @@ import threading import time import contextlib +from operator import attrgetter try: import zlib # We may need its compression method @@ -1632,6 +1633,29 @@ def extractall(self, path=None, members=None, pwd=None): for zipinfo in members: self._extract_member(zipinfo, path, pwd) + + def remove(self, member): + """Remove a file from the archive. The archive must be open with mode 'a'""" + + if self.mode != 'a': + raise RuntimeError("remove() requires mode 'a'") + if not self.fp: + raise ValueError( + "Attempt to write to ZIP archive that was already closed") + if self._writing: + raise ValueError( + "Can't write to ZIP archive while an open writing handle exists." + ) + + # Make sure we have an info object + if isinstance(member, ZipInfo): + # 'member' is already an info object + zinfo = member + else: + # get the info object + zinfo = self.getinfo(member) + + return self._remove_member(zinfo) @classmethod def _sanitize_windows_name(cls, arcname, pathsep): @@ -1689,6 +1713,52 @@ def _extract_member(self, member, targetpath, pwd): shutil.copyfileobj(source, target) return targetpath + + def _remove_member(self, member): + # get a sorted filelist by header offset, in case the dir order + # doesn't match the actual entry order + fp = self.fp + entry_offset = 0 + filelist = sorted(self.filelist, key=attrgetter('header_offset')) + for i in range(len(filelist)): + info = filelist[i] + # find the target member + if info.header_offset < member.header_offset: + continue + + # get the total size of the entry + entry_size = None + if i == len(filelist) - 1: + entry_size = self.start_dir - info.header_offset + else: + entry_size = filelist[i + 1].header_offset - info.header_offset + + # found the member, set the entry offset + if member == info: + entry_offset = entry_size + continue + + # Move entry + # read the actual entry data + fp.seek(info.header_offset) + entry_data = fp.read(entry_size) + + # update the header + info.header_offset -= entry_offset + + # write the entry to the new position + fp.seek(info.header_offset) + fp.write(entry_data) + fp.flush() + + # update state + self.start_dir -= entry_offset + self.filelist.remove(member) + del self.NameToInfo[member.filename] + self._didModify = True + + # seek to the start of the central dir + fp.seek(self.start_dir) def _writecheck(self, zinfo): """Check for errors before writing a file to the archive."""
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: