Skip to content

Commit 3fc57e8

Browse files
brettcannonhugovkwarsaw
authored
gh-91217: deprecate imghdr (#91461)
* Deprecate imghdr Co-authored-by: Hugo van Kemenade <[email protected]> * Update Doc/whatsnew/3.11.rst Co-authored-by: Hugo van Kemenade <[email protected]> * Inline `imghdr` into `email.mime.image` Co-authored-by: Hugo van Kemenade <[email protected]> Co-authored-by: Barry Warsaw <[email protected]>
1 parent dfbc792 commit 3fc57e8

File tree

8 files changed

+128
-23
lines changed

8 files changed

+128
-23
lines changed

Doc/includes/email-mime.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# Import smtplib for the actual sending function
22
import smtplib
33

4-
# And imghdr to find the types of our images
5-
import imghdr
6-
74
# Here are the email package modules we'll need
85
from email.message import EmailMessage
96

@@ -22,7 +19,7 @@
2219
with open(file, 'rb') as fp:
2320
img_data = fp.read()
2421
msg.add_attachment(img_data, maintype='image',
25-
subtype=imghdr.what(None, img_data))
22+
subtype='jpeg')
2623

2724
# Send the email via our own SMTP server.
2825
with smtplib.SMTP('localhost') as s:

Doc/library/email.mime.rst

+6-5
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,12 @@ Here are the classes:
180180
A subclass of :class:`~email.mime.nonmultipart.MIMENonMultipart`, the
181181
:class:`MIMEImage` class is used to create MIME message objects of major type
182182
:mimetype:`image`. *_imagedata* is a string containing the raw image data. If
183-
this data can be decoded by the standard Python module :mod:`imghdr`, then the
184-
subtype will be automatically included in the :mailheader:`Content-Type` header.
185-
Otherwise you can explicitly specify the image subtype via the *_subtype*
186-
argument. If the minor type could not be guessed and *_subtype* was not given,
187-
then :exc:`TypeError` is raised.
183+
this data type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm,
184+
rast, xbm, bmp, webp, and exr attempted), then the subtype will be
185+
automatically included in the :mailheader:`Content-Type` header. Otherwise
186+
you can explicitly specify the image subtype via the *_subtype* argument.
187+
If the minor type could not be guessed and *_subtype* was not given, then
188+
:exc:`TypeError` is raised.
188189

189190
Optional *_encoder* is a callable (i.e. function) which will perform the actual
190191
encoding of the image data for transport. This callable takes one argument,

Doc/whatsnew/3.11.rst

+1
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,7 @@ Deprecated
856856
* :mod:`cgitb`
857857
* :mod:`chunk`
858858
* :mod:`crypt`
859+
* :mod:`imghdr`
859860

860861
(Contributed by Brett Cannon in :issue:`47061`.)
861862

Lib/email/mime/image.py

+110-11
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,125 @@
66

77
__all__ = ['MIMEImage']
88

9-
import imghdr
10-
119
from email import encoders
1210
from email.mime.nonmultipart import MIMENonMultipart
1311

1412

15-
13+
# Originally from the imghdr module.
14+
def _what(h):
15+
for tf in tests:
16+
if res := tf(h):
17+
return res
18+
else:
19+
return None
20+
21+
tests = []
22+
23+
def _test_jpeg(h):
24+
"""JPEG data with JFIF or Exif markers; and raw JPEG"""
25+
if h[6:10] in (b'JFIF', b'Exif'):
26+
return 'jpeg'
27+
elif h[:4] == b'\xff\xd8\xff\xdb':
28+
return 'jpeg'
29+
30+
tests.append(_test_jpeg)
31+
32+
def _test_png(h):
33+
if h.startswith(b'\211PNG\r\n\032\n'):
34+
return 'png'
35+
36+
tests.append(_test_png)
37+
38+
def _test_gif(h):
39+
"""GIF ('87 and '89 variants)"""
40+
if h[:6] in (b'GIF87a', b'GIF89a'):
41+
return 'gif'
42+
43+
tests.append(_test_gif)
44+
45+
def _test_tiff(h):
46+
"""TIFF (can be in Motorola or Intel byte order)"""
47+
if h[:2] in (b'MM', b'II'):
48+
return 'tiff'
49+
50+
tests.append(_test_tiff)
51+
52+
def _test_rgb(h):
53+
"""SGI image library"""
54+
if h.startswith(b'\001\332'):
55+
return 'rgb'
56+
57+
tests.append(_test_rgb)
58+
59+
def _test_pbm(h):
60+
"""PBM (portable bitmap)"""
61+
if len(h) >= 3 and \
62+
h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r':
63+
return 'pbm'
64+
65+
tests.append(_test_pbm)
66+
67+
def _test_pgm(h):
68+
"""PGM (portable graymap)"""
69+
if len(h) >= 3 and \
70+
h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r':
71+
return 'pgm'
72+
73+
tests.append(_test_pgm)
74+
75+
def _test_ppm(h):
76+
"""PPM (portable pixmap)"""
77+
if len(h) >= 3 and \
78+
h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r':
79+
return 'ppm'
80+
81+
tests.append(_test_ppm)
82+
83+
def _test_rast(h):
84+
"""Sun raster file"""
85+
if h.startswith(b'\x59\xA6\x6A\x95'):
86+
return 'rast'
87+
88+
tests.append(_test_rast)
89+
90+
def _test_xbm(h):
91+
"""X bitmap (X10 or X11)"""
92+
if h.startswith(b'#define '):
93+
return 'xbm'
94+
95+
tests.append(_test_xbm)
96+
97+
def _test_bmp(h):
98+
if h.startswith(b'BM'):
99+
return 'bmp'
100+
101+
tests.append(_test_bmp)
102+
103+
def _test_webp(h):
104+
if h.startswith(b'RIFF') and h[8:12] == b'WEBP':
105+
return 'webp'
106+
107+
tests.append(_test_webp)
108+
109+
def _test_exr(h):
110+
if h.startswith(b'\x76\x2f\x31\x01'):
111+
return 'exr'
112+
113+
tests.append(_test_exr)
114+
115+
16116
class MIMEImage(MIMENonMultipart):
17117
"""Class for generating image/* type MIME documents."""
18118

19119
def __init__(self, _imagedata, _subtype=None,
20120
_encoder=encoders.encode_base64, *, policy=None, **_params):
21121
"""Create an image/* type MIME document.
22122
23-
_imagedata is a string containing the raw image data. If this data
24-
can be decoded by the standard Python `imghdr' module, then the
25-
subtype will be automatically included in the Content-Type header.
26-
Otherwise, you can specify the specific image subtype via the _subtype
27-
parameter.
123+
_imagedata is a string containing the raw image data. If the data
124+
type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm,
125+
rast, xbm, bmp, webp, and exr attempted), then the subtype will be
126+
automatically included in the Content-Type header. Otherwise, you can
127+
specify the specific image subtype via the _subtype parameter.
28128
29129
_encoder is a function which will perform the actual encoding for
30130
transport of the image data. It takes one argument, which is this
@@ -38,9 +138,8 @@ def __init__(self, _imagedata, _subtype=None,
38138
header.
39139
"""
40140
if _subtype is None:
41-
_subtype = imghdr.what(None, _imagedata)
42-
if _subtype is None:
43-
raise TypeError('Could not guess image MIME subtype')
141+
if (_subtype := _what(_imagedata)) is None:
142+
raise TypeError('Could not guess image MIME subtype')
44143
MIMENonMultipart.__init__(self, 'image', _subtype, policy=policy,
45144
**_params)
46145
self.set_payload(_imagedata)

Lib/imghdr.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
"""Recognize image file formats based on their first few bytes."""
22

33
from os import PathLike
4+
import warnings
45

56
__all__ = ["what"]
67

8+
9+
warnings._deprecated(__name__, remove=(3, 13))
10+
11+
712
#-------------------------#
813
# Recognize image headers #
914
#-------------------------#

Lib/test/test_email/test_email.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import base64
88
import unittest
99
import textwrap
10+
import warnings
1011

1112
from io import StringIO, BytesIO
1213
from itertools import chain
@@ -1591,7 +1592,6 @@ def test_add_header(self):
15911592
header='foobar'), missing)
15921593

15931594

1594-
15951595
# Test the basic MIMEApplication class
15961596
class TestMIMEApplication(unittest.TestCase):
15971597
def test_headers(self):

Lib/test/test_imghdr.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import imghdr
21
import io
32
import os
43
import pathlib
54
import unittest
65
import warnings
7-
from test.support import findfile
6+
from test.support import findfile, warnings_helper
87
from test.support.os_helper import TESTFN, unlink
98

9+
imghdr = warnings_helper.import_deprecated("imghdr")
10+
1011

1112
TEST_FILES = (
1213
('python.png', 'png'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Deprecate the imghdr module.

0 commit comments

Comments
 (0)