summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/includes/email-mime.py10
-rw-r--r--Lib/email/mime/image.py140
-rw-r--r--Lib/test/test_email/data/PyBanner048.gifbin896 -> 0 bytes
-rw-r--r--Lib/test/test_email/data/python.bmpbin0 -> 1162 bytes
-rw-r--r--Lib/test/test_email/data/python.exrbin0 -> 2635 bytes
-rw-r--r--Lib/test/test_email/data/python.gifbin0 -> 405 bytes
-rw-r--r--Lib/test/test_email/data/python.jpgbin0 -> 543 bytes
-rw-r--r--Lib/test/test_email/data/python.pbm3
-rw-r--r--Lib/test/test_email/data/python.pgmbin0 -> 269 bytes
-rw-r--r--Lib/test/test_email/data/python.pngbin0 -> 1020 bytes
-rw-r--r--Lib/test/test_email/data/python.ppmbin0 -> 781 bytes
-rw-r--r--Lib/test/test_email/data/python.rasbin0 -> 1056 bytes
-rw-r--r--Lib/test/test_email/data/python.sgibin0 -> 1967 bytes
-rw-r--r--Lib/test/test_email/data/python.tiffbin0 -> 1326 bytes
-rw-r--r--Lib/test/test_email/data/python.webpbin0 -> 432 bytes
-rw-r--r--Lib/test/test_email/data/python.xbm6
-rw-r--r--Lib/test/test_email/test_email.py34
17 files changed, 114 insertions, 79 deletions
diff --git a/Doc/includes/email-mime.py b/Doc/includes/email-mime.py
index c87db6a..34c6bdb 100644
--- a/Doc/includes/email-mime.py
+++ b/Doc/includes/email-mime.py
@@ -1,7 +1,7 @@
-# Import smtplib for the actual sending function
+# Import smtplib for the actual sending function.
import smtplib
-# Here are the email package modules we'll need
+# Here are the email package modules we'll need.
from email.message import EmailMessage
# Create the container email message.
@@ -13,13 +13,13 @@ msg['From'] = me
msg['To'] = ', '.join(family)
msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
-# Open the files in binary mode. Use imghdr to figure out the
-# MIME subtype for each specific image.
+# Open the files in binary mode. You can also omit the subtype
+# if you want MIMEImage to guess it.
for file in pngfiles:
with open(file, 'rb') as fp:
img_data = fp.read()
msg.add_attachment(img_data, maintype='image',
- subtype='jpeg')
+ subtype='png')
# Send the email via our own SMTP server.
with smtplib.SMTP('localhost') as s:
diff --git a/Lib/email/mime/image.py b/Lib/email/mime/image.py
index fac238c..e19dea9 100644
--- a/Lib/email/mime/image.py
+++ b/Lib/email/mime/image.py
@@ -10,137 +10,143 @@ from email import encoders
from email.mime.nonmultipart import MIMENonMultipart
+class MIMEImage(MIMENonMultipart):
+ """Class for generating image/* type MIME documents."""
+
+ 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 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
+ Image instance. It should use get_payload() and set_payload() to
+ change the payload to the encoded form. It should also add any
+ Content-Transfer-Encoding or other headers to the message as
+ necessary. The default encoding is Base64.
+
+ Any additional keyword arguments are passed to the base class
+ constructor, which turns them into parameters on the Content-Type
+ header.
+ """
+ _subtype = _what(_imagedata) if _subtype is None else _subtype
+ if _subtype is None:
+ raise TypeError('Could not guess image MIME subtype')
+ MIMENonMultipart.__init__(self, 'image', _subtype, policy=policy,
+ **_params)
+ self.set_payload(_imagedata)
+ _encoder(self)
+
+
+_rules = []
+
+
# Originally from the imghdr module.
-def _what(h):
- for tf in tests:
- if res := tf(h):
+def _what(data):
+ for rule in _rules:
+ if res := rule(data):
return res
else:
return None
-tests = []
-def _test_jpeg(h):
+def rule(rulefunc):
+ _rules.append(rulefunc)
+ return rulefunc
+
+
+@rule
+def _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):
+@rule
+def _png(h):
if h.startswith(b'\211PNG\r\n\032\n'):
return 'png'
-tests.append(_test_png)
-def _test_gif(h):
+@rule
+def _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):
+@rule
+def _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):
+@rule
+def _rgb(h):
"""SGI image library"""
if h.startswith(b'\001\332'):
return 'rgb'
-tests.append(_test_rgb)
-def _test_pbm(h):
+@rule
+def _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':
+ 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):
+@rule
+def _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':
+ 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):
+@rule
+def _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':
+ 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):
+@rule
+def _rast(h):
"""Sun raster file"""
if h.startswith(b'\x59\xA6\x6A\x95'):
return 'rast'
-tests.append(_test_rast)
-def _test_xbm(h):
+@rule
+def _xbm(h):
"""X bitmap (X10 or X11)"""
if h.startswith(b'#define '):
return 'xbm'
-tests.append(_test_xbm)
-def _test_bmp(h):
+@rule
+def _bmp(h):
if h.startswith(b'BM'):
return 'bmp'
-tests.append(_test_bmp)
-def _test_webp(h):
+@rule
+def _webp(h):
if h.startswith(b'RIFF') and h[8:12] == b'WEBP':
return 'webp'
-tests.append(_test_webp)
-def _test_exr(h):
+@rule
+def _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."""
-
- 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 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
- Image instance. It should use get_payload() and set_payload() to
- change the payload to the encoded form. It should also add any
- Content-Transfer-Encoding or other headers to the message as
- necessary. The default encoding is Base64.
-
- Any additional keyword arguments are passed to the base class
- constructor, which turns them into parameters on the Content-Type
- header.
- """
- if _subtype is None:
- 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)
- _encoder(self)
diff --git a/Lib/test/test_email/data/PyBanner048.gif b/Lib/test/test_email/data/PyBanner048.gif
deleted file mode 100644
index 7e308f5..0000000
--- a/Lib/test/test_email/data/PyBanner048.gif
+++ /dev/null
Binary files differ
diff --git a/Lib/test/test_email/data/python.bmp b/Lib/test/test_email/data/python.bmp
new file mode 100644
index 0000000..675f951
--- /dev/null
+++ b/Lib/test/test_email/data/python.bmp
Binary files differ
diff --git a/Lib/test/test_email/data/python.exr b/Lib/test/test_email/data/python.exr
new file mode 100644
index 0000000..773c81e
--- /dev/null
+++ b/Lib/test/test_email/data/python.exr
Binary files differ
diff --git a/Lib/test/test_email/data/python.gif b/Lib/test/test_email/data/python.gif
new file mode 100644
index 0000000..efa0be3
--- /dev/null
+++ b/Lib/test/test_email/data/python.gif
Binary files differ
diff --git a/Lib/test/test_email/data/python.jpg b/Lib/test/test_email/data/python.jpg
new file mode 100644
index 0000000..21222c0
--- /dev/null
+++ b/Lib/test/test_email/data/python.jpg
Binary files differ
diff --git a/Lib/test/test_email/data/python.pbm b/Lib/test/test_email/data/python.pbm
new file mode 100644
index 0000000..1848ba7
--- /dev/null
+++ b/Lib/test/test_email/data/python.pbm
@@ -0,0 +1,3 @@
+P4
+16 16
+ûñ¿úßÕ­±[ñ¥a_ÁX°°ðððð?ÿÿ \ No newline at end of file
diff --git a/Lib/test/test_email/data/python.pgm b/Lib/test/test_email/data/python.pgm
new file mode 100644
index 0000000..8349f2a
--- /dev/null
+++ b/Lib/test/test_email/data/python.pgm
Binary files differ
diff --git a/Lib/test/test_email/data/python.png b/Lib/test/test_email/data/python.png
new file mode 100644
index 0000000..1a987f7
--- /dev/null
+++ b/Lib/test/test_email/data/python.png
Binary files differ
diff --git a/Lib/test/test_email/data/python.ppm b/Lib/test/test_email/data/python.ppm
new file mode 100644
index 0000000..7d9cdb3
--- /dev/null
+++ b/Lib/test/test_email/data/python.ppm
Binary files differ
diff --git a/Lib/test/test_email/data/python.ras b/Lib/test/test_email/data/python.ras
new file mode 100644
index 0000000..130e96f
--- /dev/null
+++ b/Lib/test/test_email/data/python.ras
Binary files differ
diff --git a/Lib/test/test_email/data/python.sgi b/Lib/test/test_email/data/python.sgi
new file mode 100644
index 0000000..ffe9081
--- /dev/null
+++ b/Lib/test/test_email/data/python.sgi
Binary files differ
diff --git a/Lib/test/test_email/data/python.tiff b/Lib/test/test_email/data/python.tiff
new file mode 100644
index 0000000..39d0bfc
--- /dev/null
+++ b/Lib/test/test_email/data/python.tiff
Binary files differ
diff --git a/Lib/test/test_email/data/python.webp b/Lib/test/test_email/data/python.webp
new file mode 100644
index 0000000..e824ec7
--- /dev/null
+++ b/Lib/test/test_email/data/python.webp
Binary files differ
diff --git a/Lib/test/test_email/data/python.xbm b/Lib/test/test_email/data/python.xbm
new file mode 100644
index 0000000..cfbee2e
--- /dev/null
+++ b/Lib/test/test_email/data/python.xbm
@@ -0,0 +1,6 @@
+#define python_width 16
+#define python_height 16
+static char python_bits[] = {
+ 0xDF, 0xFE, 0x8F, 0xFD, 0x5F, 0xFB, 0xAB, 0xFE, 0xB5, 0x8D, 0xDA, 0x8F,
+ 0xA5, 0x86, 0xFA, 0x83, 0x1A, 0x80, 0x0D, 0x80, 0x0D, 0x80, 0x0F, 0xE0,
+ 0x0F, 0xF8, 0x0F, 0xF8, 0x0F, 0xFC, 0xFF, 0xFF, };
diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
index b87dae2..6ead594 100644
--- a/Lib/test/test_email/test_email.py
+++ b/Lib/test/test_email/test_email.py
@@ -798,7 +798,7 @@ class TestMessageAPI(TestEmailBase):
class TestEncoders(unittest.TestCase):
def test_EncodersEncode_base64(self):
- with openfile('PyBanner048.gif', 'rb') as fp:
+ with openfile('python.gif', 'rb') as fp:
bindata = fp.read()
mimed = email.mime.image.MIMEImage(bindata)
base64ed = mimed.get_payload()
@@ -1555,24 +1555,44 @@ class TestMIMEAudio(unittest.TestCase):
# Test the basic MIMEImage class
class TestMIMEImage(unittest.TestCase):
- def setUp(self):
- with openfile('PyBanner048.gif', 'rb') as fp:
+ def _make_image(self, ext):
+ with openfile(f'python.{ext}', 'rb') as fp:
self._imgdata = fp.read()
self._im = MIMEImage(self._imgdata)
def test_guess_minor_type(self):
- self.assertEqual(self._im.get_content_type(), 'image/gif')
+ for ext, subtype in {
+ 'bmp': None,
+ 'exr': None,
+ 'gif': None,
+ 'jpg': 'jpeg',
+ 'pbm': None,
+ 'pgm': None,
+ 'png': None,
+ 'ppm': None,
+ 'ras': 'rast',
+ 'sgi': 'rgb',
+ 'tiff': None,
+ 'webp': None,
+ 'xbm': None,
+ }.items():
+ self._make_image(ext)
+ subtype = ext if subtype is None else subtype
+ self.assertEqual(self._im.get_content_type(), f'image/{subtype}')
def test_encoding(self):
+ self._make_image('gif')
payload = self._im.get_payload()
self.assertEqual(base64.decodebytes(bytes(payload, 'ascii')),
- self._imgdata)
+ self._imgdata)
def test_checkSetMinor(self):
+ self._make_image('gif')
im = MIMEImage(self._imgdata, 'fish')
self.assertEqual(im.get_content_type(), 'image/fish')
def test_add_header(self):
+ self._make_image('gif')
eq = self.assertEqual
self._im.add_header('Content-Disposition', 'attachment',
filename='dingusfish.gif')
@@ -1747,7 +1767,7 @@ class TestMIMEText(unittest.TestCase):
# Test complicated multipart/* messages
class TestMultipart(TestEmailBase):
def setUp(self):
- with openfile('PyBanner048.gif', 'rb') as fp:
+ with openfile('python.gif', 'rb') as fp:
data = fp.read()
container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY')
image = MIMEImage(data, name='dingusfish.gif')
@@ -3444,7 +3464,7 @@ multipart/report
def test_mime_classes_policy_argument(self):
with openfile('audiotest.au', 'rb') as fp:
audiodata = fp.read()
- with openfile('PyBanner048.gif', 'rb') as fp:
+ with openfile('python.gif', 'rb') as fp:
bindata = fp.read()
classes = [
(MIMEApplication, ('',)),