From db5fe667311745a2bc86355620354449721545f3 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 27 Dec 2008 15:50:40 +0000 Subject: [PATCH] Merged revisions 67946 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r67946 | antoine.pitrou | 2008-12-27 16:43:12 +0100 (sam., 27 déc. 2008) | 4 lines Issue #4756: zipfile.is_zipfile() now supports file-like objects. Patch by Gabriel Genellina. ........ --- Doc/library/zipfile.rst | 6 ++++-- Lib/test/test_zipfile.py | 39 ++++++++++++++++++++++++++++++++++----- Lib/zipfile.py | 26 +++++++++++++++++++------- Misc/NEWS | 3 +++ 4 files changed, 60 insertions(+), 14 deletions(-) diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index 49267c070d5785..75860cc928a146 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -64,9 +64,11 @@ The module defines the following items: .. function:: is_zipfile(filename) Returns ``True`` if *filename* is a valid ZIP file based on its magic number, - otherwise returns ``False``. This module does not currently handle ZIP files - which have appended comments. + otherwise returns ``False``. *filename* may be a file or file-like object too. + This module does not currently handle ZIP files which have appended comments. + .. versionchanged:: 2.7 + Support for file and file-like objects. .. data:: ZIP_STORED diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 9e565fb153f899..678ac9fc00f54f 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -621,20 +621,49 @@ def testCloseErroneousFile(self): def testIsZipErroneousFile(self): # This test checks that the is_zipfile function correctly identifies # a file that is not a zip file - fp = open(TESTFN, "w") - fp.write("this is not a legal zip file\n") - fp.close() + + # - passing a filename + with open(TESTFN, "w") as fp: + fp.write("this is not a legal zip file\n") chk = zipfile.is_zipfile(TESTFN) - self.assert_(chk is False) + self.assert_(not chk) + # - passing a file object + with open(TESTFN, "rb") as fp: + chk = zipfile.is_zipfile(fp) + self.assert_(not chk) + # - passing a file-like object + fp = io.BytesIO() + fp.write(b"this is not a legal zip file\n") + chk = zipfile.is_zipfile(fp) + self.assert_(not chk) + fp.seek(0,0) + chk = zipfile.is_zipfile(fp) + self.assert_(not chk) def testIsZipValidFile(self): # This test checks that the is_zipfile function correctly identifies # a file that is a zip file + + # - passing a filename zipf = zipfile.ZipFile(TESTFN, mode="w") zipf.writestr("foo.txt", b"O, for a Muse of Fire!") zipf.close() chk = zipfile.is_zipfile(TESTFN) - self.assert_(chk is True) + self.assert_(chk) + # - passing a file object + with open(TESTFN, "rb") as fp: + chk = zipfile.is_zipfile(fp) + self.assert_(chk) + fp.seek(0,0) + zip_contents = fp.read() + # - passing a file-like object + fp = io.BytesIO() + fp.write(zip_contents) + chk = zipfile.is_zipfile(fp) + self.assert_(chk) + fp.seek(0,0) + chk = zipfile.is_zipfile(fp) + self.assert_(chk) def testNonExistentFileRaisesIOError(self): # make sure we don't raise an AttributeError when a partially-constructed diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 46ec6ef065e566..5054097528876c 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -130,18 +130,30 @@ class LargeZipFile(Exception): _CD64_DIRECTORY_SIZE = 8 _CD64_OFFSET_START_CENTDIR = 9 -def is_zipfile(filename): - """Quickly see if file is a ZIP file by checking the magic number.""" +def _check_zipfile(fp): try: - fpin = io.open(filename, "rb") - endrec = _EndRecData(fpin) - fpin.close() - if endrec: - return True # file has correct magic number + if _EndRecData(fp): + return True # file has correct magic number except IOError: pass return False +def is_zipfile(filename): + """Quickly see if a file is a ZIP file by checking the magic number. + + The filename argument may be a file or file-like object too. + """ + result = False + try: + if hasattr(filename, "read"): + result = _check_zipfile(fp=filename) + else: + with open(filename, "rb") as fp: + result = _check_zipfile(fp) + except IOError: + pass + return result + def _EndRecData64(fpin, offset, endrec): """ Read the ZIP64 end-of-archive records and use that to update endrec diff --git a/Misc/NEWS b/Misc/NEWS index e247506af5728e..ed62a01c655201 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -59,6 +59,9 @@ Core and Builtins Library ------- +- Issue #4756: zipfile.is_zipfile() now supports file-like objects. Patch by + Gabriel Genellina. + - Issue #4574: reading an UTF16-encoded text file crashes if \r on 64-char boundary.