summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2008-05-20 08:25:48 (GMT)
committerGeorg Brandl <georg@python.org>2008-05-20 08:25:48 (GMT)
commit112aa5032985925608844d7421e12703afd6ce41 (patch)
treeb7e26da27c90471192c42afea278b8b9f59ef9f3
parent4dd019fde36b53b30e69a2ce9cee3f2f9f25c2a6 (diff)
downloadcpython-112aa5032985925608844d7421e12703afd6ce41.zip
cpython-112aa5032985925608844d7421e12703afd6ce41.tar.gz
cpython-112aa5032985925608844d7421e12703afd6ce41.tar.bz2
Patch #1775025: allow opening zipfile members via ZipInfo instances.
Patch by Graham Horler.
-rw-r--r--Doc/library/zipfile.rst35
-rw-r--r--Lib/test/test_zipfile.py19
-rw-r--r--Lib/zipfile.py13
-rw-r--r--Misc/NEWS4
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