diff -r a4405b799e1b Lib/imghdr.py --- a/Lib/imghdr.py Thu Jul 07 12:59:31 2011 +0100 +++ b/Lib/imghdr.py Thu Jul 07 15:51:19 2011 -0400 @@ -1,4 +1,5 @@ """Recognize image file formats based on their first few bytes.""" +import io __all__ = ["what"] @@ -6,17 +7,44 @@ # Recognize image headers # #-------------------------# +# The initial number of bytes to read from a file or stream. We assume this +# number of bytes is sufficient to be able to tell what type of image the file +# contains. +BLOCK_SIZE = 32 + + def what(file, h=None): + """Wrapper around the :func:`_what` function for backward compatibility.""" if h is None: if isinstance(file, str): - f = open(file, 'rb') - h = f.read(32) + with open(file, 'rb') as f: + return _what(f) else: - location = file.tell() - h = file.read(32) - file.seek(location) - f = None + return _what(file) else: + return _what(h) + + +def _what(data): + """Returns the type of image specified by `data`, which can be either an + open file-like object, or a sequence of bytes. + + If `data` is an open file, the file will remain open and its position as + returned by :func:`file.tell` will be reset to the value it had when this + function was entered (unless a custom test affects the file pointer). + + If `data` is a sequence of bytes, this function assumes that the characters + are the bytes read from the file whose type will be guessed. + + """ + if (isinstance(data, io.RawIOBase) or isinstance(data, io.BufferedIOBase) + or isinstance(data, io.TextIOBase)): + location = data.tell() + h = data.read(BLOCK_SIZE) + data.seek(location) + f = data + else: + h = data f = None try: for tf in tests: diff -r a4405b799e1b Lib/test/test_imghdr.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_imghdr.py Thu Jul 07 15:51:19 2011 -0400 @@ -0,0 +1,71 @@ +"""Provides unit tests for the imghdr module.""" +from imghdr import _what +from io import BytesIO +from io import StringIO +from os import unlink +from test.support import run_unittest +from unittest import TestCase + +# The initial 32 bytes of an example image of each type. +IMAGES = { + 'pbm': (b'P4\n# CREATOR: GIMP PNM Filter Ve'), + 'pgm': (b'P5\n# CREATOR: GIMP PNM Filter Ve'), + 'ppm': (b'P6\n# CREATOR: GIMP PNM Filter Ve'), + 'xbm': (b'#define image_width 1\n#define im'), + 'rast': (b'\x59\xA6\x6A\x95\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00'), + 'rgb': (b'\x01\xda\x00\x01\x00\x03\x00\x01' + b'\x00\x01\x00\x03\x00\x00\x00\x00' + b'\x00\x00\x00\xff\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00'), + 'gif': (b'GIF87a\x01\x00' + b'\x01\x00\x80\x00\x00\xff\xff\xff' + b'\xff\xff\xff,\x00\x00\x00\x00' + b'\x01\x00\x01\x00\x00\x02\x02D'), + 'tiff': (b'II*\x00\x0c\x00\x00\x00' + b'\xff\xff\xff\x00\x11\x00\xfe\x00' + b'\x04\x00\x01\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x01\x03\x00\x01\x00'), + 'jpeg': (b'\xff\xd8\xff\xe0\x00\x10JF' + b'IF\x00\x01\x01\x01\x00H' + b'\x00H\x00\x00\xff\xfe\x00\x13' + b'Created '), + 'bmp': (b'BM:\x00\x00\x00\x00\x00' + b'\x00\x006\x00\x00\x00(\x00' + b'\x00\x00\x01\x00\x00\x00\x01\x00' + b'\x00\x00\x01\x00\x18\x00\x00\x00'), + 'png': (b'\x89PNG\r\n\x1a\n' + b'\x00\x00\x00\rIHDR' + b'\x00\x00\x00\x01\x00\x00\x00\x01' + b'\x08\x02\x00\x00\x00\x90wS') + } + + +class ImghdrTest(TestCase): + """Test case for the imghdr module.""" + + def test__what_bytes(self): + """Test case for determining image type from a sequence of bytes.""" + for imagetype, image in IMAGES.items(): + self.assertEqual(imagetype, _what(image)) + + def test__what_file(self): + """Test case for determining image type from an open file.""" + for imagetype, image in IMAGES.items(): + with BytesIO(image) as f: + self.assertEqual(imagetype, _what(f)) + + def test__what_unknown(self): + """Test case for an unknown type of image.""" + with BytesIO(b'garbage') as f: + self.assertIsNone(_what(f)) + + +def test_main(): + """Runs all the tests in this module.""" + run_unittest(ImghdrTest) + +if __name__ == "__main__": + test_main()