]> granicus.if.org Git - python/commitdiff
Patch #1775025: allow opening zipfile members via ZipInfo instances.
authorGeorg Brandl <georg@python.org>
Tue, 20 May 2008 08:25:48 +0000 (08:25 +0000)
committerGeorg Brandl <georg@python.org>
Tue, 20 May 2008 08:25:48 +0000 (08:25 +0000)
Patch by Graham Horler.

Doc/library/zipfile.rst
Lib/test/test_zipfile.py
Lib/zipfile.py
Misc/NEWS

index 5f509aa25bdb9e2842312b8746a1410ae36b1ea2..f87b856195c6b0d81869e1e4493fc5613639e258 100644 (file)
@@ -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()
index c83622a71904113a0eef5a7180396daada7b0e69..4bc2104d1b7c32b5fea127b3a35a66502ada8d05 100644 (file)
@@ -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)
 
index 33fcfb14f0540eecfe3626fe44eda03f26eeb859..b812a82c54b2c83713fbe9a29ded6efa90b7a863 100644 (file)
@@ -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()
index e2398ce0ae42cff188d5f6725699dd4a939c5e84..156d4bc14ff97c146a28075a9530e2f4ed54279c 100644 (file)
--- 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