|
10 | 10 | from email.mime.nonmultipart import MIMENonMultipart
|
11 | 11 |
|
12 | 12 |
|
| 13 | +class MIMEImage(MIMENonMultipart): |
| 14 | + """Class for generating image/* type MIME documents.""" |
| 15 | + |
| 16 | + def __init__(self, _imagedata, _subtype=None, |
| 17 | + _encoder=encoders.encode_base64, *, policy=None, **_params): |
| 18 | + """Create an image/* type MIME document. |
| 19 | +
|
| 20 | + _imagedata is a string containing the raw image data. If the data |
| 21 | + type can be detected (jpeg, png, gif, tiff, rgb, pbm, pgm, ppm, |
| 22 | + rast, xbm, bmp, webp, and exr attempted), then the subtype will be |
| 23 | + automatically included in the Content-Type header. Otherwise, you can |
| 24 | + specify the specific image subtype via the _subtype parameter. |
| 25 | +
|
| 26 | + _encoder is a function which will perform the actual encoding for |
| 27 | + transport of the image data. It takes one argument, which is this |
| 28 | + Image instance. It should use get_payload() and set_payload() to |
| 29 | + change the payload to the encoded form. It should also add any |
| 30 | + Content-Transfer-Encoding or other headers to the message as |
| 31 | + necessary. The default encoding is Base64. |
| 32 | +
|
| 33 | + Any additional keyword arguments are passed to the base class |
| 34 | + constructor, which turns them into parameters on the Content-Type |
| 35 | + header. |
| 36 | + """ |
| 37 | + _subtype = _what(_imagedata) if _subtype is None else _subtype |
| 38 | + if _subtype is None: |
| 39 | + raise TypeError('Could not guess image MIME subtype') |
| 40 | + MIMENonMultipart.__init__(self, 'image', _subtype, policy=policy, |
| 41 | + **_params) |
| 42 | + self.set_payload(_imagedata) |
| 43 | + _encoder(self) |
| 44 | + |
| 45 | + |
| 46 | +_rules = [] |
| 47 | + |
| 48 | + |
13 | 49 | # Originally from the imghdr module.
|
14 |
| -def _what(h): |
15 |
| - for tf in tests: |
16 |
| - if res := tf(h): |
| 50 | +def _what(data): |
| 51 | + for rule in _rules: |
| 52 | + if res := rule(data): |
17 | 53 | return res
|
18 | 54 | else:
|
19 | 55 | return None
|
20 | 56 |
|
21 |
| -tests = [] |
22 | 57 |
|
23 |
| -def _test_jpeg(h): |
| 58 | +def rule(rulefunc): |
| 59 | + _rules.append(rulefunc) |
| 60 | + return rulefunc |
| 61 | + |
| 62 | + |
| 63 | +@rule |
| 64 | +def _jpeg(h): |
24 | 65 | """JPEG data with JFIF or Exif markers; and raw JPEG"""
|
25 | 66 | if h[6:10] in (b'JFIF', b'Exif'):
|
26 | 67 | return 'jpeg'
|
27 | 68 | elif h[:4] == b'\xff\xd8\xff\xdb':
|
28 | 69 | return 'jpeg'
|
29 | 70 |
|
30 |
| -tests.append(_test_jpeg) |
31 | 71 |
|
32 |
| -def _test_png(h): |
| 72 | +@rule |
| 73 | +def _png(h): |
33 | 74 | if h.startswith(b'\211PNG\r\n\032\n'):
|
34 | 75 | return 'png'
|
35 | 76 |
|
36 |
| -tests.append(_test_png) |
37 | 77 |
|
38 |
| -def _test_gif(h): |
| 78 | +@rule |
| 79 | +def _gif(h): |
39 | 80 | """GIF ('87 and '89 variants)"""
|
40 | 81 | if h[:6] in (b'GIF87a', b'GIF89a'):
|
41 | 82 | return 'gif'
|
42 | 83 |
|
43 |
| -tests.append(_test_gif) |
44 | 84 |
|
45 |
| -def _test_tiff(h): |
| 85 | +@rule |
| 86 | +def _tiff(h): |
46 | 87 | """TIFF (can be in Motorola or Intel byte order)"""
|
47 | 88 | if h[:2] in (b'MM', b'II'):
|
48 | 89 | return 'tiff'
|
49 | 90 |
|
50 |
| -tests.append(_test_tiff) |
51 | 91 |
|
52 |
| -def _test_rgb(h): |
| 92 | +@rule |
| 93 | +def _rgb(h): |
53 | 94 | """SGI image library"""
|
54 | 95 | if h.startswith(b'\001\332'):
|
55 | 96 | return 'rgb'
|
56 | 97 |
|
57 |
| -tests.append(_test_rgb) |
58 | 98 |
|
59 |
| -def _test_pbm(h): |
| 99 | +@rule |
| 100 | +def _pbm(h): |
60 | 101 | """PBM (portable bitmap)"""
|
61 | 102 | 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': |
| 103 | + h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r': |
63 | 104 | return 'pbm'
|
64 | 105 |
|
65 |
| -tests.append(_test_pbm) |
66 | 106 |
|
67 |
| -def _test_pgm(h): |
| 107 | +@rule |
| 108 | +def _pgm(h): |
68 | 109 | """PGM (portable graymap)"""
|
69 | 110 | 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': |
| 111 | + h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r': |
71 | 112 | return 'pgm'
|
72 | 113 |
|
73 |
| -tests.append(_test_pgm) |
74 | 114 |
|
75 |
| -def _test_ppm(h): |
| 115 | +@rule |
| 116 | +def _ppm(h): |
76 | 117 | """PPM (portable pixmap)"""
|
77 | 118 | 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': |
| 119 | + h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r': |
79 | 120 | return 'ppm'
|
80 | 121 |
|
81 |
| -tests.append(_test_ppm) |
82 | 122 |
|
83 |
| -def _test_rast(h): |
| 123 | +@rule |
| 124 | +def _rast(h): |
84 | 125 | """Sun raster file"""
|
85 | 126 | if h.startswith(b'\x59\xA6\x6A\x95'):
|
86 | 127 | return 'rast'
|
87 | 128 |
|
88 |
| -tests.append(_test_rast) |
89 | 129 |
|
90 |
| -def _test_xbm(h): |
| 130 | +@rule |
| 131 | +def _xbm(h): |
91 | 132 | """X bitmap (X10 or X11)"""
|
92 | 133 | if h.startswith(b'#define '):
|
93 | 134 | return 'xbm'
|
94 | 135 |
|
95 |
| -tests.append(_test_xbm) |
96 | 136 |
|
97 |
| -def _test_bmp(h): |
| 137 | +@rule |
| 138 | +def _bmp(h): |
98 | 139 | if h.startswith(b'BM'):
|
99 | 140 | return 'bmp'
|
100 | 141 |
|
101 |
| -tests.append(_test_bmp) |
102 | 142 |
|
103 |
| -def _test_webp(h): |
| 143 | +@rule |
| 144 | +def _webp(h): |
104 | 145 | if h.startswith(b'RIFF') and h[8:12] == b'WEBP':
|
105 | 146 | return 'webp'
|
106 | 147 |
|
107 |
| -tests.append(_test_webp) |
108 | 148 |
|
109 |
| -def _test_exr(h): |
| 149 | +@rule |
| 150 | +def _exr(h): |
110 | 151 | if h.startswith(b'\x76\x2f\x31\x01'):
|
111 | 152 | return 'exr'
|
112 |
| - |
113 |
| -tests.append(_test_exr) |
114 |
| - |
115 |
| - |
116 |
| -class MIMEImage(MIMENonMultipart): |
117 |
| - """Class for generating image/* type MIME documents.""" |
118 |
| - |
119 |
| - def __init__(self, _imagedata, _subtype=None, |
120 |
| - _encoder=encoders.encode_base64, *, policy=None, **_params): |
121 |
| - """Create an image/* type MIME document. |
122 |
| -
|
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. |
128 |
| -
|
129 |
| - _encoder is a function which will perform the actual encoding for |
130 |
| - transport of the image data. It takes one argument, which is this |
131 |
| - Image instance. It should use get_payload() and set_payload() to |
132 |
| - change the payload to the encoded form. It should also add any |
133 |
| - Content-Transfer-Encoding or other headers to the message as |
134 |
| - necessary. The default encoding is Base64. |
135 |
| -
|
136 |
| - Any additional keyword arguments are passed to the base class |
137 |
| - constructor, which turns them into parameters on the Content-Type |
138 |
| - header. |
139 |
| - """ |
140 |
| - if _subtype is None: |
141 |
| - if (_subtype := _what(_imagedata)) is None: |
142 |
| - raise TypeError('Could not guess image MIME subtype') |
143 |
| - MIMENonMultipart.__init__(self, 'image', _subtype, policy=policy, |
144 |
| - **_params) |
145 |
| - self.set_payload(_imagedata) |
146 |
| - _encoder(self) |
|
0 commit comments