diff --git a/Doc/library/imghdr.rst b/Doc/library/imghdr.rst deleted file mode 100644 index 630fd7019f94de..00000000000000 --- a/Doc/library/imghdr.rst +++ /dev/null @@ -1,86 +0,0 @@ -:mod:`imghdr` --- Determine the type of an image -================================================ - -.. module:: imghdr - :synopsis: Determine the type of image contained in a file or byte stream. - :deprecated: - -**Source code:** :source:`Lib/imghdr.py` - -.. deprecated-removed:: 3.11 3.13 - The :mod:`imghdr` module is deprecated - (see :pep:`PEP 594 <594#imghdr>` for details and alternatives). - --------------- - -The :mod:`imghdr` module determines the type of image contained in a file or -byte stream. - -The :mod:`imghdr` module defines the following function: - - -.. function:: what(file, h=None) - - Test the image data contained in the file named *file* and return a - string describing the image type. If *h* is provided, the *file* - argument is ignored and *h* is assumed to contain the byte stream to test. - - .. versionchanged:: 3.6 - Accepts a :term:`path-like object`. - -The following image types are recognized, as listed below with the return value -from :func:`what`: - -+------------+-----------------------------------+ -| Value | Image format | -+============+===================================+ -| ``'rgb'`` | SGI ImgLib Files | -+------------+-----------------------------------+ -| ``'gif'`` | GIF 87a and 89a Files | -+------------+-----------------------------------+ -| ``'pbm'`` | Portable Bitmap Files | -+------------+-----------------------------------+ -| ``'pgm'`` | Portable Graymap Files | -+------------+-----------------------------------+ -| ``'ppm'`` | Portable Pixmap Files | -+------------+-----------------------------------+ -| ``'tiff'`` | TIFF Files | -+------------+-----------------------------------+ -| ``'rast'`` | Sun Raster Files | -+------------+-----------------------------------+ -| ``'xbm'`` | X Bitmap Files | -+------------+-----------------------------------+ -| ``'jpeg'`` | JPEG data in JFIF or Exif formats | -+------------+-----------------------------------+ -| ``'bmp'`` | BMP files | -+------------+-----------------------------------+ -| ``'png'`` | Portable Network Graphics | -+------------+-----------------------------------+ -| ``'webp'`` | WebP files | -+------------+-----------------------------------+ -| ``'exr'`` | OpenEXR Files | -+------------+-----------------------------------+ - -.. versionadded:: 3.5 - The *exr* and *webp* formats were added. - - -You can extend the list of file types :mod:`imghdr` can recognize by appending -to this variable: - - -.. data:: tests - - A list of functions performing the individual tests. Each function takes two - arguments: the byte-stream and an open file-like object. When :func:`what` is - called with a byte-stream, the file-like object will be ``None``. - - The test function should return a string describing the image type if the test - succeeded, or ``None`` if it failed. - -Example:: - - >>> import imghdr - >>> imghdr.what('bass.gif') - 'gif' - diff --git a/Doc/library/superseded.rst b/Doc/library/superseded.rst index 24e74110a319f9..bd17039bc6f6b6 100644 --- a/Doc/library/superseded.rst +++ b/Doc/library/superseded.rst @@ -10,5 +10,4 @@ backwards compatibility. They have been superseded by other modules. .. toctree:: - imghdr.rst optparse.rst diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 9cbd15c2209dc6..d3fa6ce6dabc60 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -6,7 +6,8 @@ # Sphinx version is pinned so that new versions that introduce new warnings # won't suddenly cause build failures. Updating the version is fine as long # as no warnings are raised by doing so. -sphinx==4.5.0 +# PR #104777: Sphinx 6.2 no longer uses imghdr, removed in Python 3.13. +sphinx==6.2.0 blurb diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 7186ba5521f957..066070eda7dba3 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1735,7 +1735,7 @@ Modules +---------------------+---------------------+---------------------+---------------------+---------------------+ | :mod:`!audioop` | :mod:`!crypt` | :mod:`!nis` | :mod:`!sndhdr` | :mod:`!uu` | +---------------------+---------------------+---------------------+---------------------+---------------------+ - | :mod:`!cgi` | :mod:`imghdr` | :mod:`!nntplib` | :mod:`!spwd` | :mod:`!xdrlib` | + | :mod:`!cgi` | :mod:`!imghdr` | :mod:`!nntplib` | :mod:`!spwd` | :mod:`!xdrlib` | +---------------------+---------------------+---------------------+---------------------+---------------------+ | :mod:`!cgitb` | :mod:`!mailcap` | :mod:`!ossaudiodev` | :mod:`!sunau` | | +---------------------+---------------------+---------------------+---------------------+---------------------+ diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 5266f5ffb93737..c8fd77f0fdb4a9 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -924,7 +924,7 @@ Modules (see :pep:`594`): * :mod:`!cgitb` * :mod:`!chunk` * :mod:`!crypt` -* :mod:`imghdr` +* :mod:`!imghdr` * :mod:`!mailcap` * :mod:`!msilib` * :mod:`!nis` diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 0e78a080c4304b..28c98554fdbfdb 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -244,6 +244,14 @@ Removed :class:`typing.TypedDict` types, deprecated in Python 3.11. (Contributed by Tomas Roun in :gh:`104786`.) +* :pep:`594`: Remove the :mod:`!imghdr` module, deprecated in Python 3.11: + use the projects + `filetype `_, + `puremagic `_, + or `python-magic `_ instead. + (Contributed by Victor Stinner in :gh:`104773`.) + + Porting to Python 3.13 ====================== diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index cd8f6e2cd9b595..ccf71bf08e8608 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -1252,7 +1252,7 @@ Oberkirch in :issue:`21800`.) imghdr ------ -The :func:`~imghdr.what` function now recognizes the +The :func:`~!imghdr.what` function now recognizes the `OpenEXR `_ format (contributed by Martin Vignali and Claudiu Popa in :issue:`20295`), and the `WebP `_ format diff --git a/Lib/imghdr.py b/Lib/imghdr.py deleted file mode 100644 index 33868883470764..00000000000000 --- a/Lib/imghdr.py +++ /dev/null @@ -1,180 +0,0 @@ -"""Recognize image file formats based on their first few bytes.""" - -from os import PathLike -import warnings - -__all__ = ["what"] - - -warnings._deprecated(__name__, remove=(3, 13)) - - -#-------------------------# -# Recognize image headers # -#-------------------------# - -def what(file, h=None): - """Return the type of image contained in a file or byte stream.""" - f = None - try: - if h is None: - if isinstance(file, (str, PathLike)): - f = open(file, 'rb') - h = f.read(32) - else: - location = file.tell() - h = file.read(32) - file.seek(location) - for tf in tests: - res = tf(h, f) - if res: - return res - finally: - if f: f.close() - return None - - -#---------------------------------# -# Subroutines per image file type # -#---------------------------------# - -tests = [] - -def test_jpeg(h, f): - """Test for JPEG data with JFIF or Exif markers; and raw JPEG.""" - if h[6:10] in (b'JFIF', b'Exif'): - return 'jpeg' - elif h[:4] == b'\xff\xd8\xff\xdb': - return 'jpeg' - -tests.append(test_jpeg) - -def test_png(h, f): - """Verify if the image is a PNG.""" - if h.startswith(b'\211PNG\r\n\032\n'): - return 'png' - -tests.append(test_png) - -def test_gif(h, f): - """Verify if the image is a GIF ('87 or '89 variants).""" - if h[:6] in (b'GIF87a', b'GIF89a'): - return 'gif' - -tests.append(test_gif) - -def test_tiff(h, f): - """Verify if the image is a TIFF (can be in Motorola or Intel byte order).""" - if h[:2] in (b'MM', b'II'): - return 'tiff' - -tests.append(test_tiff) - -def test_rgb(h, f): - """test for the SGI image library.""" - if h.startswith(b'\001\332'): - return 'rgb' - -tests.append(test_rgb) - -def test_pbm(h, f): - """Verify if the image is a PBM (portable bitmap).""" - if len(h) >= 3 and \ - h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r': - return 'pbm' - -tests.append(test_pbm) - -def test_pgm(h, f): - """Verify if the image is a PGM (portable graymap).""" - if len(h) >= 3 and \ - h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r': - return 'pgm' - -tests.append(test_pgm) - -def test_ppm(h, f): - """Verify if the image is a PPM (portable pixmap).""" - if len(h) >= 3 and \ - h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r': - return 'ppm' - -tests.append(test_ppm) - -def test_rast(h, f): - """test for the Sun raster file.""" - if h.startswith(b'\x59\xA6\x6A\x95'): - return 'rast' - -tests.append(test_rast) - -def test_xbm(h, f): - """Verify if the image is a X bitmap (X10 or X11).""" - if h.startswith(b'#define '): - return 'xbm' - -tests.append(test_xbm) - -def test_bmp(h, f): - """Verify if the image is a BMP file.""" - if h.startswith(b'BM'): - return 'bmp' - -tests.append(test_bmp) - -def test_webp(h, f): - """Verify if the image is a WebP.""" - if h.startswith(b'RIFF') and h[8:12] == b'WEBP': - return 'webp' - -tests.append(test_webp) - -def test_exr(h, f): - """verify is the image ia a OpenEXR fileOpenEXR.""" - if h.startswith(b'\x76\x2f\x31\x01'): - return 'exr' - -tests.append(test_exr) - -#--------------------# -# Small test program # -#--------------------# - -def test(): - import sys - recursive = 0 - if sys.argv[1:] and sys.argv[1] == '-r': - del sys.argv[1:2] - recursive = 1 - try: - if sys.argv[1:]: - testall(sys.argv[1:], recursive, 1) - else: - testall(['.'], recursive, 1) - except KeyboardInterrupt: - sys.stderr.write('\n[Interrupted]\n') - sys.exit(1) - -def testall(list, recursive, toplevel): - import sys - import os - for filename in list: - if os.path.isdir(filename): - print(filename + '/:', end=' ') - if recursive or toplevel: - print('recursing down:') - import glob - names = glob.glob(os.path.join(glob.escape(filename), '*')) - testall(names, recursive, 0) - else: - print('*** directory (use -r) ***') - else: - print(filename + ':', end=' ') - sys.stdout.flush() - try: - print(what(filename)) - except OSError: - print('*** not found ***') - -if __name__ == '__main__': - test() diff --git a/Lib/test/imghdrdata/python-raw.jpg b/Lib/test/imghdrdata/python-raw.jpg deleted file mode 100644 index 11940b3410ddf0..00000000000000 Binary files a/Lib/test/imghdrdata/python-raw.jpg and /dev/null differ diff --git a/Lib/test/imghdrdata/python.bmp b/Lib/test/imghdrdata/python.bmp deleted file mode 100644 index 675f95191a45fd..00000000000000 Binary files a/Lib/test/imghdrdata/python.bmp and /dev/null differ diff --git a/Lib/test/imghdrdata/python.exr b/Lib/test/imghdrdata/python.exr deleted file mode 100644 index 773c81ee1fb850..00000000000000 Binary files a/Lib/test/imghdrdata/python.exr and /dev/null differ diff --git a/Lib/test/imghdrdata/python.jpg b/Lib/test/imghdrdata/python.jpg deleted file mode 100644 index 21222c09f5a71d..00000000000000 Binary files a/Lib/test/imghdrdata/python.jpg and /dev/null differ diff --git a/Lib/test/imghdrdata/python.pbm b/Lib/test/imghdrdata/python.pbm deleted file mode 100644 index 1848ba7ff064e7..00000000000000 --- a/Lib/test/imghdrdata/python.pbm +++ /dev/null @@ -1,3 +0,0 @@ -P4 -16 16 -ûñ¿úßÕ­±[ñ¥a_ÁX°°ðððð?ÿÿ \ No newline at end of file diff --git a/Lib/test/imghdrdata/python.ras b/Lib/test/imghdrdata/python.ras deleted file mode 100644 index 130e96f817ed9d..00000000000000 Binary files a/Lib/test/imghdrdata/python.ras and /dev/null differ diff --git a/Lib/test/imghdrdata/python.sgi b/Lib/test/imghdrdata/python.sgi deleted file mode 100644 index ffe9081c7a5b67..00000000000000 Binary files a/Lib/test/imghdrdata/python.sgi and /dev/null differ diff --git a/Lib/test/imghdrdata/python.tiff b/Lib/test/imghdrdata/python.tiff deleted file mode 100644 index 39d0bfcec02533..00000000000000 Binary files a/Lib/test/imghdrdata/python.tiff and /dev/null differ diff --git a/Lib/test/imghdrdata/python.webp b/Lib/test/imghdrdata/python.webp deleted file mode 100644 index e824ec7fb1c7fa..00000000000000 Binary files a/Lib/test/imghdrdata/python.webp and /dev/null differ diff --git a/Lib/test/test_imghdr.py b/Lib/test/test_imghdr.py deleted file mode 100644 index 208c8eee455e7b..00000000000000 --- a/Lib/test/test_imghdr.py +++ /dev/null @@ -1,144 +0,0 @@ -import io -import os -import pathlib -import unittest -import warnings -from test.support import findfile, warnings_helper -from test.support.os_helper import TESTFN, unlink - -imghdr = warnings_helper.import_deprecated("imghdr") - - -TEST_FILES = ( - ('python.png', 'png'), - ('python.gif', 'gif'), - ('python.bmp', 'bmp'), - ('python.ppm', 'ppm'), - ('python.pgm', 'pgm'), - ('python.pbm', 'pbm'), - ('python.jpg', 'jpeg'), - ('python-raw.jpg', 'jpeg'), # raw JPEG without JFIF/EXIF markers - ('python.ras', 'rast'), - ('python.sgi', 'rgb'), - ('python.tiff', 'tiff'), - ('python.xbm', 'xbm'), - ('python.webp', 'webp'), - ('python.exr', 'exr'), -) - -class UnseekableIO(io.FileIO): - def tell(self): - raise io.UnsupportedOperation - - def seek(self, *args, **kwargs): - raise io.UnsupportedOperation - -class TestImghdr(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.testfile = findfile('python.png', subdir='imghdrdata') - with open(cls.testfile, 'rb') as stream: - cls.testdata = stream.read() - - def tearDown(self): - unlink(TESTFN) - - def test_data(self): - for filename, expected in TEST_FILES: - filename = findfile(filename, subdir='imghdrdata') - self.assertEqual(imghdr.what(filename), expected) - with open(filename, 'rb') as stream: - self.assertEqual(imghdr.what(stream), expected) - with open(filename, 'rb') as stream: - data = stream.read() - self.assertEqual(imghdr.what(None, data), expected) - self.assertEqual(imghdr.what(None, bytearray(data)), expected) - - def test_pathlike_filename(self): - for filename, expected in TEST_FILES: - with self.subTest(filename=filename): - filename = findfile(filename, subdir='imghdrdata') - self.assertEqual(imghdr.what(pathlib.Path(filename)), expected) - - def test_register_test(self): - def test_jumbo(h, file): - if h.startswith(b'eggs'): - return 'ham' - imghdr.tests.append(test_jumbo) - self.addCleanup(imghdr.tests.pop) - self.assertEqual(imghdr.what(None, b'eggs'), 'ham') - - def test_file_pos(self): - with open(TESTFN, 'wb') as stream: - stream.write(b'ababagalamaga') - pos = stream.tell() - stream.write(self.testdata) - with open(TESTFN, 'rb') as stream: - stream.seek(pos) - self.assertEqual(imghdr.what(stream), 'png') - self.assertEqual(stream.tell(), pos) - - def test_bad_args(self): - with self.assertRaises(TypeError): - imghdr.what() - with self.assertRaises(AttributeError): - imghdr.what(None) - with self.assertRaises(TypeError): - imghdr.what(self.testfile, 1) - with self.assertRaises(AttributeError): - imghdr.what(os.fsencode(self.testfile)) - with open(self.testfile, 'rb') as f: - with self.assertRaises(AttributeError): - imghdr.what(f.fileno()) - - def test_invalid_headers(self): - for header in (b'\211PN\r\n', - b'\001\331', - b'\x59\xA6', - b'cutecat', - b'000000JFI', - b'GIF80'): - self.assertIsNone(imghdr.what(None, header)) - - def test_string_data(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", BytesWarning) - for filename, _ in TEST_FILES: - filename = findfile(filename, subdir='imghdrdata') - with open(filename, 'rb') as stream: - data = stream.read().decode('latin1') - with self.assertRaises(TypeError): - imghdr.what(io.StringIO(data)) - with self.assertRaises(TypeError): - imghdr.what(None, data) - - def test_missing_file(self): - with self.assertRaises(FileNotFoundError): - imghdr.what('missing') - - def test_closed_file(self): - stream = open(self.testfile, 'rb') - stream.close() - with self.assertRaises(ValueError) as cm: - imghdr.what(stream) - stream = io.BytesIO(self.testdata) - stream.close() - with self.assertRaises(ValueError) as cm: - imghdr.what(stream) - - def test_unseekable(self): - with open(TESTFN, 'wb') as stream: - stream.write(self.testdata) - with UnseekableIO(TESTFN, 'rb') as stream: - with self.assertRaises(io.UnsupportedOperation): - imghdr.what(stream) - - def test_output_stream(self): - with open(TESTFN, 'wb') as stream: - stream.write(self.testdata) - stream.seek(0) - with self.assertRaises(OSError) as cm: - imghdr.what(stream) - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/test/test_tkinter/test_images.py b/Lib/test/test_tkinter/test_images.py index b6f8b79ae689fa..c07de867ce04b7 100644 --- a/Lib/test/test_tkinter/test_images.py +++ b/Lib/test/test_tkinter/test_images.py @@ -66,7 +66,7 @@ class BitmapImageTest(AbstractTkTest, unittest.TestCase): @classmethod def setUpClass(cls): AbstractTkTest.setUpClass.__func__(cls) - cls.testfile = support.findfile('python.xbm', subdir='imghdrdata') + cls.testfile = support.findfile('python.xbm', subdir='tkinterdata') def test_create_from_file(self): image = tkinter.BitmapImage('::img::test', master=self.root, @@ -150,7 +150,7 @@ class PhotoImageTest(AbstractTkTest, unittest.TestCase): @classmethod def setUpClass(cls): AbstractTkTest.setUpClass.__func__(cls) - cls.testfile = support.findfile('python.gif', subdir='imghdrdata') + cls.testfile = support.findfile('python.gif', subdir='tkinterdata') def create(self): return tkinter.PhotoImage('::img::test', master=self.root, @@ -163,7 +163,7 @@ def colorlist(self, *args): return tkinter._join(args) def check_create_from_file(self, ext): - testfile = support.findfile('python.' + ext, subdir='imghdrdata') + testfile = support.findfile('python.' + ext, subdir='tkinterdata') image = tkinter.PhotoImage('::img::test', master=self.root, file=testfile) self.assertEqual(str(image), '::img::test') @@ -178,7 +178,7 @@ def check_create_from_file(self, ext): self.assertNotIn('::img::test', self.root.image_names()) def check_create_from_data(self, ext): - testfile = support.findfile('python.' + ext, subdir='imghdrdata') + testfile = support.findfile('python.' + ext, subdir='tkinterdata') with open(testfile, 'rb') as f: data = f.read() image = tkinter.PhotoImage('::img::test', master=self.root, diff --git a/Lib/test/test_tkinter/widget_tests.py b/Lib/test/test_tkinter/widget_tests.py index 85b0511aba3c7a..f60087a6e9f385 100644 --- a/Lib/test/test_tkinter/widget_tests.py +++ b/Lib/test/test_tkinter/widget_tests.py @@ -250,7 +250,7 @@ def test_configure_bitmap(self): widget = self.create() self.checkParam(widget, 'bitmap', 'questhead') self.checkParam(widget, 'bitmap', 'gray50') - filename = test.support.findfile('python.xbm', subdir='imghdrdata') + filename = test.support.findfile('python.xbm', subdir='tkinterdata') self.checkParam(widget, 'bitmap', '@' + filename) # Cocoa Tk widgets don't detect invalid -bitmap values # See https://core.tcl.tk/tk/info/31cd33dbf0 diff --git a/Lib/test/imghdrdata/python.gif b/Lib/test/tkinterdata/python.gif similarity index 100% rename from Lib/test/imghdrdata/python.gif rename to Lib/test/tkinterdata/python.gif diff --git a/Lib/test/imghdrdata/python.pgm b/Lib/test/tkinterdata/python.pgm similarity index 100% rename from Lib/test/imghdrdata/python.pgm rename to Lib/test/tkinterdata/python.pgm diff --git a/Lib/test/imghdrdata/python.png b/Lib/test/tkinterdata/python.png similarity index 100% rename from Lib/test/imghdrdata/python.png rename to Lib/test/tkinterdata/python.png diff --git a/Lib/test/imghdrdata/python.ppm b/Lib/test/tkinterdata/python.ppm similarity index 100% rename from Lib/test/imghdrdata/python.ppm rename to Lib/test/tkinterdata/python.ppm diff --git a/Lib/test/imghdrdata/python.xbm b/Lib/test/tkinterdata/python.xbm similarity index 100% rename from Lib/test/imghdrdata/python.xbm rename to Lib/test/tkinterdata/python.xbm diff --git a/Makefile.pre.in b/Makefile.pre.in index b277092f20079d..7ae94ff02cd285 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2116,7 +2116,6 @@ TESTSUBDIRS= idlelib/idle_test \ test/decimaltestdata \ test/dtracedata \ test/encoded_modules \ - test/imghdrdata \ test/leakers \ test/libregrtest \ test/subprocessdata \ @@ -2210,6 +2209,7 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_zipfile \ test/test_zoneinfo \ test/test_zoneinfo/data \ + test/tkinterdata \ test/tracedmodules \ test/typinganndata \ test/xmltestdata \ diff --git a/Misc/NEWS.d/next/Library/2023-05-23-02-13-11.gh-issue-104773.JNiEjv.rst b/Misc/NEWS.d/next/Library/2023-05-23-02-13-11.gh-issue-104773.JNiEjv.rst new file mode 100644 index 00000000000000..213b6f5b764258 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-23-02-13-11.gh-issue-104773.JNiEjv.rst @@ -0,0 +1,2 @@ +:pep:`594`: Remove the :mod:`!imghdr` module, deprecated in Python 3.11. +Patch by Victor Stinner. diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index 5af120d2542822..925b8b3230fce3 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -156,7 +156,6 @@ static const char* _Py_stdlib_module_names[] = { "http", "idlelib", "imaplib", -"imghdr", "importlib", "inspect", "io",