From 112aa5032985925608844d7421e12703afd6ce41 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 20 May 2008 08:25:48 +0000 Subject: Patch #1775025: allow opening zipfile members via ZipInfo instances. Patch by Graham Horler. --- Doc/library/zipfile.rst | 35 +++++++++++++++++++++-------------- Lib/test/test_zipfile.py | 19 +++++++++++++++++++ Lib/zipfile.py | 13 ++++++++----- Misc/NEWS | 4 ++++ 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index 5f509aa..f87b856 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -155,11 +155,11 @@ ZipFile Objects .. method:: ZipFile.open(name[, mode[, pwd]]) Extract a member from the archive as a file-like object (ZipExtFile). *name* is - the name of the file in the archive. The *mode* parameter, if included, must be - one of the following: ``'r'`` (the default), ``'U'``, or ``'rU'``. Choosing - ``'U'`` or ``'rU'`` will enable universal newline support in the read-only - object. *pwd* is the password used for encrypted files. Calling :meth:`open` - on a closed ZipFile will raise a :exc:`RuntimeError`. + the name of the file in the archive, or a :class:`ZipInfo` object. The *mode* + parameter, if included, must be one of the following: ``'r'`` (the default), + ``'U'``, or ``'rU'``. Choosing ``'U'`` or ``'rU'`` will enable universal newline + support in the read-only object. *pwd* is the password used for encrypted files. + Calling :meth:`open` on a closed ZipFile will raise a :exc:`RuntimeError`. .. note:: @@ -178,16 +178,22 @@ ZipFile Objects create a new file object that will be held by the ZipExtFile, allowing it to operate independently of the ZipFile. + .. note:: + + The :meth:`open`, :meth:`read` and :meth:`extract` methods can take a filename + or a :class:`ZipInfo` object. You will appreciate this when trying to read a + ZIP file that contains members with duplicate names. + .. versionadded:: 2.6 .. method:: ZipFile.extract(member[, path[, pwd]]) - Extract a member from the archive to the current working directory, using its - full name. Its file information is extracted as accurately as possible. - *path* specifies a different directory to extract to. *member* can be a - filename or a :class:`ZipInfo` object. *pwd* is the password used for - encrypted files. + Extract a member from the archive to the current working directory; *member* + must be its full name or a :class:`ZipInfo` object). Its file information is + extracted as accurately as possible. *path* specifies a different directory + to extract to. *member* can be a filename or a :class:`ZipInfo` object. + *pwd* is the password used for encrypted files. .. versionadded:: 2.6 @@ -216,13 +222,14 @@ ZipFile Objects .. method:: ZipFile.read(name[, pwd]) - Return the bytes of the file in the archive. The archive must be open for read - or append. *pwd* is the password used for encrypted files and, if specified, it - will override the default password set with :meth:`setpassword`. Calling + Return the bytes of the file *name* in the archive. *name* is the name of the + file in the archive, or a :class:`ZipInfo` object. The archive must be open for + read or append. *pwd* is the password used for encrypted files and, if specified, + it will override the default password set with :meth:`setpassword`. Calling :meth:`read` on a closed ZipFile will raise a :exc:`RuntimeError`. .. versionchanged:: 2.6 - *pwd* was added. + *pwd* was added, and *name* can now be a :class:`ZipInfo` object. .. method:: ZipFile.testzip() diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index c83622a..4bc2104 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -132,6 +132,25 @@ class TestsWithSourceFile(unittest.TestCase): for f in (TESTFN2, TemporaryFile(), StringIO()): self.zipOpenTest(f, zipfile.ZIP_STORED) + def testOpenViaZipInfo(self): + # Create the ZIP archive + zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) + zipfp.writestr("name", "foo") + zipfp.writestr("name", "bar") + zipfp.close() + + zipfp = zipfile.ZipFile(TESTFN2, "r") + infos = zipfp.infolist() + data = "" + for info in infos: + data += zipfp.open(info).read() + self.assert_(data == "foobar" or data == "barfoo") + data = "" + for info in infos: + data += zipfp.read(info) + self.assert_(data == "foobar" or data == "barfoo") + zipfp.close() + def zipRandomOpenTest(self, f, compression): self.makeTestArchive(f, compression) diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 33fcfb1..b812a82 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -776,10 +776,13 @@ class ZipFile: else: zef_file = open(self.filename, 'rb') - # Get info object for name - zinfo = self.getinfo(name) - - filepos = zef_file.tell() + # Make sure we have an info object + if isinstance(name, ZipInfo): + # 'name' is already an info object + zinfo = name + else: + # Get info object for name + zinfo = self.getinfo(name) zef_file.seek(zinfo.header_offset, 0) @@ -884,7 +887,7 @@ class ZipFile: if upperdirs and not os.path.exists(upperdirs): os.makedirs(upperdirs) - source = self.open(member.filename, pwd=pwd) + source = self.open(member, pwd=pwd) target = file(targetpath, "wb") shutil.copyfileobj(source, target) source.close() diff --git a/Misc/NEWS b/Misc/NEWS index e2398ce..156d4bc 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -52,6 +52,10 @@ Extension Modules Library ------- +- Issue #1775025: You can now specify zipfile members to open(), + read() or extract() via a ZipInfo instance. This allows handling + duplicate filenames in zipfiles. + - Issue #961805: Fix Text.edit_modified() in Tkinter. - Issue #1793: Function ctypes.util.find_msvcrt() added that returns -- cgit v0.12