[Python-checkins] gh-91217: deprecate imghdr (#91461)

warsaw webhook-mailer at python.org
Wed Apr 13 13:47:55 EDT 2022


https://github.com/python/cpython/commit/3fc57e8f6ff925b561b03c46bcf5bd323782c19c
commit: 3fc57e8f6ff925b561b03c46bcf5bd323782c19c
branch: main
author: Brett Cannon <brett at python.org>
committer: warsaw <barry at python.org>
date: 2022-04-13T10:47:41-07:00
summary:

gh-91217: deprecate imghdr (#91461)

* Deprecate imghdr

Co-authored-by: Hugo van Kemenade <hugovk at users.noreply.github.com>

* Update Doc/whatsnew/3.11.rst

Co-authored-by: Hugo van Kemenade <hugovk at users.noreply.github.com>

* Inline `imghdr` into `email.mime.image`

Co-authored-by: Hugo van Kemenade <hugovk at users.noreply.github.com>
Co-authored-by: Barry Warsaw <barry at python.org>

files:
A Misc/NEWS.d/next/Library/2022-04-11-17-04-38.gh-issue-91217.QVDLOq.rst
M Doc/includes/email-mime.py
M Doc/library/email.mime.rst
M Doc/whatsnew/3.11.rst
M Lib/email/mime/image.py
M Lib/imghdr.py
M Lib/test/test_email/test_email.py
M Lib/test/test_imghdr.py

diff --git a/Doc/includes/email-mime.py b/Doc/includes/email-mime.py
index 6af2be0b08a48..c87db6a064b00 100644
--- a/Doc/includes/email-mime.py
+++ b/Doc/includes/email-mime.py
@@ -1,9 +1,6 @@
 # Import smtplib for the actual sending function
 import smtplib
 
-# And imghdr to find the types of our images
-import imghdr
-
 # Here are the email package modules we'll need
 from email.message import EmailMessage
 
@@ -22,7 +19,7 @@
     with open(file, 'rb') as fp:
         img_data = fp.read()
     msg.add_attachment(img_data, maintype='image',
-                                 subtype=imghdr.what(None, img_data))
+                                 subtype='jpeg')
 
 # Send the email via our own SMTP server.
 with smtplib.SMTP('localhost') as s:
diff --git a/Doc/library/email.mime.rst b/Doc/library/email.mime.rst
index f37f6aa28dec7..ab4f7bc54e025 100644
--- a/Doc/library/email.mime.rst
+++ b/Doc/library/email.mime.rst
@@ -180,11 +180,12 @@ Here are the classes:
    A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the
    :class:`MIMEImage` class is used to create MIME message objects of major type
    :mimetype:`image`. *_imagedata* is a string containing the raw image data.  If
-   this data can be decoded by the standard Python module :mod:`imghdr`, then the
-   subtype will be automatically included in the :mailheader:`Content-Type` header.
-   Otherwise you can explicitly specify the image subtype via the *_subtype*
-   argument.  If the minor type could not be guessed and *_subtype* was not given,
-   then :exc:`TypeError` is raised.
+   this data type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm,
+   rast, xbm, bmp, webp, and exr attempted), then the subtype will be
+   automatically included in the :mailheader:`Content-Type` header. Otherwise
+   you can explicitly specify the image subtype via the *_subtype* argument.
+   If the minor type could not be guessed and *_subtype* was not given, then
+   :exc:`TypeError` is raised.
 
    Optional *_encoder* is a callable (i.e. function) which will perform the actual
    encoding of the image data for transport.  This callable takes one argument,
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index d803801f273c5..894ec8a9d0d92 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -856,6 +856,7 @@ Deprecated
   * :mod:`cgitb`
   * :mod:`chunk`
   * :mod:`crypt`
+  * :mod:`imghdr`
 
   (Contributed by Brett Cannon in :issue:`47061`.)
 
diff --git a/Lib/email/mime/image.py b/Lib/email/mime/image.py
index 92724643cdeea..fac238c7289fa 100644
--- a/Lib/email/mime/image.py
+++ b/Lib/email/mime/image.py
@@ -6,13 +6,113 @@
 
 __all__ = ['MIMEImage']
 
-import imghdr
-
 from email import encoders
 from email.mime.nonmultipart import MIMENonMultipart
 
 
-

+# Originally from the imghdr module.
+def _what(h):
+    for tf in tests:
+        if res := tf(h):
+            return res
+    else:
+        return None
+
+tests = []
+
+def _test_jpeg(h):
+    """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):
+    if h.startswith(b'\211PNG\r\n\032\n'):
+        return 'png'
+
+tests.append(_test_png)
+
+def _test_gif(h):
+    """GIF ('87 and '89 variants)"""
+    if h[:6] in (b'GIF87a', b'GIF89a'):
+        return 'gif'
+
+tests.append(_test_gif)
+
+def _test_tiff(h):
+    """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):
+    """SGI image library"""
+    if h.startswith(b'\001\332'):
+        return 'rgb'
+
+tests.append(_test_rgb)
+
+def _test_pbm(h):
+    """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):
+    """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):
+    """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):
+    """Sun raster file"""
+    if h.startswith(b'\x59\xA6\x6A\x95'):
+        return 'rast'
+
+tests.append(_test_rast)
+
+def _test_xbm(h):
+    """X bitmap (X10 or X11)"""
+    if h.startswith(b'#define '):
+        return 'xbm'
+
+tests.append(_test_xbm)
+
+def _test_bmp(h):
+    if h.startswith(b'BM'):
+        return 'bmp'
+
+tests.append(_test_bmp)
+
+def _test_webp(h):
+    if h.startswith(b'RIFF') and h[8:12] == b'WEBP':
+        return 'webp'
+
+tests.append(_test_webp)
+
+def _test_exr(h):
+    if h.startswith(b'\x76\x2f\x31\x01'):
+        return 'exr'
+
+tests.append(_test_exr)
+
+
 class MIMEImage(MIMENonMultipart):
     """Class for generating image/* type MIME documents."""
 
@@ -20,11 +120,11 @@ def __init__(self, _imagedata, _subtype=None,
                  _encoder=encoders.encode_base64, *, policy=None, **_params):
         """Create an image/* type MIME document.
 
-        _imagedata is a string containing the raw image data.  If this data
-        can be decoded by the standard Python `imghdr' module, then the
-        subtype will be automatically included in the Content-Type header.
-        Otherwise, you can specify the specific image subtype via the _subtype
-        parameter.
+        _imagedata is a string containing the raw image data.  If the data
+        type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm,
+        rast, xbm, bmp, webp, and exr attempted), then the subtype will be
+        automatically included in the Content-Type header. Otherwise, you can
+        specify the specific image subtype via the _subtype parameter.
 
         _encoder is a function which will perform the actual encoding for
         transport of the image data.  It takes one argument, which is this
@@ -38,9 +138,8 @@ def __init__(self, _imagedata, _subtype=None,
         header.
         """
         if _subtype is None:
-            _subtype = imghdr.what(None, _imagedata)
-        if _subtype is None:
-            raise TypeError('Could not guess image MIME subtype')
+            if (_subtype := _what(_imagedata)) is None:
+                raise TypeError('Could not guess image MIME subtype')
         MIMENonMultipart.__init__(self, 'image', _subtype, policy=policy,
                                   **_params)
         self.set_payload(_imagedata)
diff --git a/Lib/imghdr.py b/Lib/imghdr.py
index afcb67772ee9a..6a372e66c7f26 100644
--- a/Lib/imghdr.py
+++ b/Lib/imghdr.py
@@ -1,9 +1,14 @@
 """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 #
 #-------------------------#
diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
index ca9c773bbc559..b87dae22de1d2 100644
--- a/Lib/test/test_email/test_email.py
+++ b/Lib/test/test_email/test_email.py
@@ -7,6 +7,7 @@
 import base64
 import unittest
 import textwrap
+import warnings
 
 from io import StringIO, BytesIO
 from itertools import chain
@@ -1591,7 +1592,6 @@ def test_add_header(self):
                                          header='foobar'), missing)
 
 
-
 # Test the basic MIMEApplication class
 class TestMIMEApplication(unittest.TestCase):
     def test_headers(self):
diff --git a/Lib/test/test_imghdr.py b/Lib/test/test_imghdr.py
index ca0a0b23c3cf1..208c8eee455e7 100644
--- a/Lib/test/test_imghdr.py
+++ b/Lib/test/test_imghdr.py
@@ -1,12 +1,13 @@
-import imghdr
 import io
 import os
 import pathlib
 import unittest
 import warnings
-from test.support import findfile
+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'),
diff --git a/Misc/NEWS.d/next/Library/2022-04-11-17-04-38.gh-issue-91217.QVDLOq.rst b/Misc/NEWS.d/next/Library/2022-04-11-17-04-38.gh-issue-91217.QVDLOq.rst
new file mode 100644
index 0000000000000..3e59c205aae29
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-04-11-17-04-38.gh-issue-91217.QVDLOq.rst
@@ -0,0 +1 @@
+Deprecate the imghdr module.



More information about the Python-checkins mailing list