From 1fcb39ea64192fc83e7b52f067856bdf977ec2c1 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 14 Apr 2022 17:48:59 -0700 Subject: [PATCH] gh-91520: Rewrite imghdr inlining for clarity and completeness (#91521) * Rewrite imghdr inlining for clarity and completeness * Move MIMEImage class back closer to the top of the file since it's the important thing. * Use a decorate to mark a given rule function and simplify the rule function names for clarity. * Copy over all the imghdr test data files into the email package's test data directory. This way when imghdr is actually removed, it won't affect the MIMEImage guessing tests. * Rewrite and extend the MIMEImage tests to test for all supported auto-detected MIME image subtypes. * Remove the now redundant PyBanner048.gif data file. * See https://github.com/python/cpython/pull/91461#discussion_r850313336 Co-authored-by: Oleg Iarygin Co-authored-by: Oleg Iarygin --- Doc/includes/email-mime.py | 10 +- Lib/email/mime/image.py | 216 ++++++++++++----------- Lib/test/test_email/data/PyBanner048.gif | Bin 896 -> 0 bytes Lib/test/test_email/data/python.bmp | Bin 0 -> 1162 bytes Lib/test/test_email/data/python.exr | Bin 0 -> 2635 bytes Lib/test/test_email/data/python.gif | Bin 0 -> 405 bytes Lib/test/test_email/data/python.jpg | Bin 0 -> 543 bytes Lib/test/test_email/data/python.pbm | 3 + Lib/test/test_email/data/python.pgm | Bin 0 -> 269 bytes Lib/test/test_email/data/python.png | Bin 0 -> 1020 bytes Lib/test/test_email/data/python.ppm | Bin 0 -> 781 bytes Lib/test/test_email/data/python.ras | Bin 0 -> 1056 bytes Lib/test/test_email/data/python.sgi | Bin 0 -> 1967 bytes Lib/test/test_email/data/python.tiff | Bin 0 -> 1326 bytes Lib/test/test_email/data/python.webp | Bin 0 -> 432 bytes Lib/test/test_email/data/python.xbm | 6 + Lib/test/test_email/test_email.py | 34 +++- 17 files changed, 152 insertions(+), 117 deletions(-) delete mode 100644 Lib/test/test_email/data/PyBanner048.gif create mode 100644 Lib/test/test_email/data/python.bmp create mode 100644 Lib/test/test_email/data/python.exr create mode 100644 Lib/test/test_email/data/python.gif create mode 100644 Lib/test/test_email/data/python.jpg create mode 100644 Lib/test/test_email/data/python.pbm create mode 100644 Lib/test/test_email/data/python.pgm create mode 100644 Lib/test/test_email/data/python.png create mode 100644 Lib/test/test_email/data/python.ppm create mode 100644 Lib/test/test_email/data/python.ras create mode 100644 Lib/test/test_email/data/python.sgi create mode 100644 Lib/test/test_email/data/python.tiff create mode 100644 Lib/test/test_email/data/python.webp create mode 100644 Lib/test/test_email/data/python.xbm diff --git a/Doc/includes/email-mime.py b/Doc/includes/email-mime.py index c87db6a064b..34c6bdb60ff 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 fac238c7289..e19dea91c0c 100644 --- a/Lib/email/mime/image.py +++ b/Lib/email/mime/image.py @@ -10,109 +10,6 @@ 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.""" @@ -137,10 +34,119 @@ class MIMEImage(MIMENonMultipart): constructor, which turns them into parameters on the Content-Type header. """ + _subtype = _what(_imagedata) if _subtype is None else _subtype if _subtype is None: - if (_subtype := _what(_imagedata)) is None: - raise TypeError('Could not guess image MIME subtype') + 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(data): + for rule in _rules: + if res := rule(data): + return res + else: + return None + + +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' + + +@rule +def _png(h): + if h.startswith(b'\211PNG\r\n\032\n'): + return 'png' + + +@rule +def _gif(h): + """GIF ('87 and '89 variants)""" + if h[:6] in (b'GIF87a', b'GIF89a'): + return 'gif' + + +@rule +def _tiff(h): + """TIFF (can be in Motorola or Intel byte order)""" + if h[:2] in (b'MM', b'II'): + return 'tiff' + + +@rule +def _rgb(h): + """SGI image library""" + if h.startswith(b'\001\332'): + return 'rgb' + + +@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': + return 'pbm' + + +@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': + return 'pgm' + + +@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': + return 'ppm' + + +@rule +def _rast(h): + """Sun raster file""" + if h.startswith(b'\x59\xA6\x6A\x95'): + return 'rast' + + +@rule +def _xbm(h): + """X bitmap (X10 or X11)""" + if h.startswith(b'#define '): + return 'xbm' + + +@rule +def _bmp(h): + if h.startswith(b'BM'): + return 'bmp' + + +@rule +def _webp(h): + if h.startswith(b'RIFF') and h[8:12] == b'WEBP': + return 'webp' + + +@rule +def _exr(h): + if h.startswith(b'\x76\x2f\x31\x01'): + return 'exr' diff --git a/Lib/test/test_email/data/PyBanner048.gif b/Lib/test/test_email/data/PyBanner048.gif deleted file mode 100644 index 7e308f542b864cdbfe8e872d37574bfb9b0eced4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 896 zcmV-`1AqKSNk%v~VU_?R0P_F<|NsB>`26ef_~7gH*5vWb-R{EH>$uP7t;yu0!r+;^ z-H)`|g{{_lr_*qt&t{p-EC2ui0G0qF000F4@X1N5y*TU5yZ>M)j$~<`XsWJk>%MR- z&vb3yc&_h!e-QMtSs0~^g0|0>-3~Gpa0|^apKSCJ>00@z4loAVrmKaBC9fO5^iJFN8 zjgAsWWEcbh1Q>n*s}l_d0h1R6rJ)sSycL=Oat&9BkZ)d88l7?!d9N6GrxdNDzY@32 z8Mgr1P}OS3GY8%h3<(DY2u+=Q7Y=IWoe3JP1{T%auGebQ7z%0xU?8UF8V3eP0u*A< zfWZO;Y19BQV1PixBonInyKv+Z1x*J30S>^2gbtzszv69(buR;hTM8=>+h_p+qAUpt z5YU&XCn7A`8hF}pq+FT+90qx7bw>d=losFtplM{2hC@^W9TAwJlPm`jeTofG00Gr! zVq{s6$f1?E2a=#YXb_P=A$VLhNK(7<4~2QK0-ixwkpO{;1_}ftM>HVSkKHWXl3Qu1 zD+&yP`8(87LqvBO5KYSyvjRW>!a883TuF~bAP5pLV921_ED3Bkq$$*?f`e)Uj!i+j zf!poCd~vS{X>OvyXp$VlIN0YEy!Iw~U21J%rmmApBC0go@7 zMt^~nD+31*q_fBqeYFj(9Jd$$ba3fGi7wyGaZ?8g+(Hjz*V!b1K^DXkA2n+bD8X;n zQ4kA#63k~oZ^%)h-Ue6!0OAA-02h@e6Mh(iO;de>8ilbyCzLYp48q+617@H=SO7ej z!H&2XqKiZX*0x4LAtdBME9F^`M>!8HncxH*s(3+M8#u7n9U9~z9+wS`kq8zWEa_JW zk`Tv1oE+>p9gBz<;Ll!FK0p9e2VKY|B5Bx{2m!~v<5iy;=)!1K036g|1?nf>eo8cPh9lh`Yjx WAUKLH4cxMjYX}IH`|b%50029Mwr#`! diff --git a/Lib/test/test_email/data/python.bmp b/Lib/test/test_email/data/python.bmp new file mode 100644 index 0000000000000000000000000000000000000000..675f95191a45fdb3ae845996d6871b86286f848a GIT binary patch literal 1162 zcmZ{kYempK|q2sSaMQcOvV!f#cTTaT5pwGN1$s5CJv=mUkMll5Jgcy;ItSC?Tnd^`$!ZyO|7%HXiN}owe^9#6tKCr10S#!uzCDIGzdHNjmWMc9{p|zePQa{W;AvB4VXGT z`>Y5WM+&f)Y5BJ0OR`&Y#e7pvBu<7?XPJl`HRPB@gg%1MhyQDN3OWN`{bX)5G|R z)Zkw1O;Wu2St}P^;?17p-|QWcLE;K)~*-(-^GMc0W0vFmLgqB#t_8FRmYt=IheKYf;2%Ew2~5z(0uc Bxh4Pr literal 0 HcmV?d00001 diff --git a/Lib/test/test_email/data/python.exr b/Lib/test/test_email/data/python.exr new file mode 100644 index 0000000000000000000000000000000000000000..773c81ee1fb850cdbb6cccd3ae5edabb80146481 GIT binary patch literal 2635 zcmcJROK2NM7{@2=N@|-p=|Qy*&7%~@59(Nt9qrD{DhX+Ai$Y5vmGPwyR7-LMiexOs zO(7|QBFmSQLUHU%A5d)h)E0`IT#^`yB17rp;>J*toManH+guzM3T?lco%Qb8Dm`_U z-I?7P&G(!C_n$$NfzZCk5JIEp6S-U}TYyG3Qz)XB;oBLs51zd6n%^((`4Zdn<=mbx zXf%Ied?HmSWb!#zTTGu!6cZzvTr&R-dOLq9oIy|Xh_W1ln%~JxVLY37_dmyFGr82+ ziDYU5*)0)eJaZ|PJyRG@jTX-(ikUo0XY+|7Qbe56>B8tlDwQ**e<_tK!UU7yGT>-%92u@w`Iw28!})*_a_yz24^?ozV2MpXdU;Ku^*wQX}p@{+1<} z944FkwBDgtw7vaJJdewG04sP}-3Z(LT!xbUicALU=?P8sIy(_srzm=b4$^1nJu=^d zBTi8C4}DIL>D$_D3ywNQ(KzEC-w6uNF&vqP)gg41WXS+HV-1|4>vshO=jbqgXJ0aejz_j|O*+FQksQ&7#*YR< z-0ngTv3@tj62*{B6wMpu;8C_tzo6V1ZNeE`jVN%`AlH0dm&q%nli1GSYIuQTo81*0 zo(NZiJ7ZBegHsG~f57|?frD?eRvgR565YZB;5vRSs*mU`IwhRZ6wa6t6miJ)MtC8F z(Cd&Br!y*oViG8>vrj>fRqS>~RZxt{y@?Rt-cF7sdPk5Kf&99*EXO7B_w*WlgPx!t zlQ@Q-h{9LrQx`(XAS{a`E}J8F834afDjY<3RnWqx-U*e1eC7kf8IzESL&jM{ zH;x&9vj>vmP2fn1GlOSv&W>&}h`jLQjC0&(JnB;9lNNq$XMTK9IHQKQVJDar7&>V*RY0J)-yFx(5`FA~<58 znssSF0eOYH1h?{Bxu@O-bGmr_xEh`cO$Y4Loog|GW2!ei4_d$vcNN$@21z9I9$8v4f>{mUEw%pB{&D*w+T|IZ}<&?W!V zE$hWJ=*K<((=h+kF#Xjw`qMf8*E#vsKL6M{?9N61+Cl%@ME~AP|KCpk;#L3SSpVl@ z^5Jgv<8S@xYX9qOGGK=`W{@^#kv3_QH)xhOYL+={mO5{nI&hpjaGX4Go;`D-K6Ii# zcBMdgrb2tFLwu`4eXT`*uSS5eM}e|PgS1G6wn~P&Oo_Zqio8sUzfX<9P>;h>k;ec3 z{{R30A^!_WZDD6+O<`wgV`~RzVQp<;JumN z4?hnVHy<}AC$AtcAHRTrpa2(-kg$+|Fpw<(GK3MNlbMABs8N8MlN%^XBKUuRL6Cz% zfI)znQHg;`kdaxC@&6G9F|bn^kN`UiD^OfTfPs;Pkp<{tm=q%u(B-V`974i^N(>^L zhDJbDq9A{w0ydzqvLFK^6Du<-LWYq^keP**O_4#!P*g0j@!|hl3_Q$?KxZ%uGT1YS zEW4B?U3~DFu+WvMGM@#LpQxtp+ZFXD)74?)Q-w_-OXV+}+VSg?!s={V53|O-aJN mJA!6tn6q{kRVF+!>dZgf)VDmS=aoY5z6wcs_O=#+*1b_a3c>-Bw{Bd$c<%p6IREU0^XE<-od)M4GXMy?WS0N{ literal 0 HcmV?d00001 diff --git a/Lib/test/test_email/data/python.png b/Lib/test/test_email/data/python.png new file mode 100644 index 0000000000000000000000000000000000000000..1a987f79fcd248a94fcd1c45934136919cc0ffdd GIT binary patch literal 1020 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=IjczVPIf98sHP+3RK|Nd%&x0mrdb3^NeXgE;8`zKjPE1 z-=lS#bM0E&l7*Hz)6CK)KokT`Jofjl*UzhVK(0|jJCOW))eK0QW=sN-Z_a^CHp`qW zZ3toj!K1^je{VVh8E%c6%+ewSu^cS{_bjjjNq`0qVGWL+OG*#Ek0`S+6kuj7&jreo3K z(Y(c_ZmmPba-Xg}|NsAYtzYk0xx%hwkyZX|v&<=;t=ruiHabHjtJ|Ce>W+IPA)ZgQ?!ZC|$Z|8gnk%7jDF#&p$6AgEMe;uIFrk^Al-!^-G$=KGCmYgs2(3;7>L0);>u7?Zr+ zU80zGF9pTWS5Ftm5Q)pl2@Fhb%Qk%AND~V)W7Fv9>FVq3?M_c`nE~Gk@#z{`)21Y8cxw21 z>3R1~7n?p&cm0Yf$tzZ^TRAm6KqTZ*K-kLFYr<0lPM-<7e&I@1Mo!kc?98iSNf$ab zuUyT}&d+)EA}90hrRNC-25-!=UcY?%`o)|4x37%M5)Dj^w=;awUArbXC-<$z8%Y_N zxpS(2tNF|^+1Y*g&{5GxRogj(m-%&HD*ImWLj-nwqKP5A*5>tbL0Yt;W_if978W=oX{an^LB{Ts5zOa#I literal 0 HcmV?d00001 diff --git a/Lib/test/test_email/data/python.ppm b/Lib/test/test_email/data/python.ppm new file mode 100644 index 0000000000000000000000000000000000000000..7d9cdb3215877b4604de4c177fc81ebb645b8290 GIT binary patch literal 781 zcmWGA<1#c;Ff`*bGBxF5KmvZf2fW&Lc{Fcvsaxw%vD~I`o_WSJBzbhEPuHIR|Npzz zuXn6mVOO%qDu1?F<`i^ANPN%M?QRVlovK&al`gg}m}{Oj4Xb*;{v$qJ`#oB>IoGbW zEm>%pGtDe*fO9I=FWsDhN%DlDD>}Lub)@# zfNlxsKkU=F$D?H{#MwaeAHjtJ|Ce=v>b=@`x;Jieu32qgw$vkbAW8H;-vqNBmUp_ z`+pmvSRbMosQ%C_A)p#$@c%hf@r&cOuaBC%KBV<}pW^G?5H49eJC|cLygR|t7(RqR+G|1 z8);jZU5(OAXX%HeBr~nZNTCbM4;67pIQ)B_xycaof#29U=l#9^=Q)>cwJDb*63JXI zK@!Py`YV~nKND4hwYYUS{>)>TPThw-uO6|7>xAfoRb`4K+Z+=9D|$DBrKlNPhGPxT zWZ!@~^BN-4Dj-k3xPsR39+MR|?2o>g0l zp4OQ18|}K>`Z`r+jZ>LwbISLeM?`8lyNrq4fN}nlO{E=3EN+9juxWJWT%NfWs*GwV z_h0t8J|7s|EQlZDPyX#y&!)Ywv7{ZAv&}G_elRvO<@;v#`Hi7F3v|3517_g8W?1Z% zhwyR2xfbutxBX_W^vx`UCzY|ANNWp!yfl!j1>P!vp4BS$;|m@k?!;Z_j^0Gfq3ezE zsgceLMsoF|GdtybORaRS_SfeFg7`fJ z__7Na+5&t^06tKz$6Z5ih#~(ij?pDgRQm)Tk%em|_M_gB=VYGfG5lloA;RNN5 zxz2B$Du?@dn8WQ_>2S9MJKRkHOm;_wl(U0~sl+T|9w8~O{^ zVlgq%yDW$U+%viTn2Qm_{T)-x1rN^pMbxLy_G6F-f|PIYRc(6(ZU`96~>T0F5h z=V!OAHmNRuLeGKfxp4mM&*S{GCcMRYPb}e?s_@74T!}l(X-toQ9~~LEmNPSbesW?w zril&0vDgfu%u8EnGVq|B{WyD&rNba`|5g1p|4Cd+9kpJWnLhRX*s+n3E=_OV;I*%M zY*9vr30g^Al%I|KsQx2=1C!aqq&Vz(WaMm&5w+LVRMRySz`|!@G4`&A^&Z$yl_=u0 zt+1HQ%!HUy73byfyv|lplT)^v|4QTRrbupVsLh(0tH4G@?9=|9@~X-g)~|8emRrp1 ze#Dw4Ie-2zOk+%@P)V`FN;eV)&JrTu-&p@5UHtvBwheY##10Mg?WR|@qDWEhatq5u zEQEuOdi&zA#aSsemz6pTt(iIsTuMkB^UWdu8?X6R>PR_pZgPU15uvXRef;5G7eX#M zcNu#GAsr77Z}hlJ<(J}cmC)O3o+>FSSe;|d&Z0lmI_+&uZ*8lq-s-E^L_bXZ$|m4y zKX&A^Xz%`x$es|nigMU8#8UAPhu4PTu~X=2kJ}~N^CYX1MO^DEw6jHdv!S+1@+q6> z!W8=jp3sS-Ukvv5bV(5tC(V Y#PP3(1`fX8p^CuZbqNQ(CtZT;AGbYjjQ{`u literal 0 HcmV?d00001 diff --git a/Lib/test/test_email/data/python.tiff b/Lib/test/test_email/data/python.tiff new file mode 100644 index 0000000000000000000000000000000000000000..39d0bfcec02533ade8934a8796aebb8990c9d389 GIT binary patch literal 1326 zcmZ{kYe(N;1=o3>C6iK^NjBVVur+_Bv`_wuaafkG@48y9bf^^$>oj7DD%5;{sDlOE7sh%6W8^ zHdQL~>Z@h?%35i9sZpXmZwyK=V=V6HA{sU;lGCVYLVIt4=!0B-{U`qtBV%3@DGgv|wwpgepN!VX?X zuM16{8&xT|rbbb}n^B6)`JN@ai;Xy^`8kqNWB#&- zgHJmF#34StwlYyJCWfFy(}0;bbRJ7kRA&T=p1%qegj$B8b?b1$8thpa^KedR6Wd`- ze-K0XWzWx3xlVjc$L3-)?<%wt%)z;p*q58of$d3FBAXm{KBj#a@Ua2og?v64if!`O zVoW}CP;#Loi$%D97{*RGJBs;9e7+@*+OvEFE(6tc^oEg~Ep!#U=uxxlrSfvR8njyW fXuf-=%w_kUu8C|tS<3P=v-PRE3}1bkRx9`m*;LyH literal 0 HcmV?d00001 diff --git a/Lib/test/test_email/data/python.webp b/Lib/test/test_email/data/python.webp new file mode 100644 index 0000000000000000000000000000000000000000..e824ec7fb1c7fa85716d27454e9d1715a896d4b4 GIT binary patch literal 432 zcmV;h0Z;x?Nk&Gf0RRA3MM6+kP&il$0000G0000F000jF06|PpNW%aC00AeWsF5V) zdgmBl=ie%sh?xI$agE?6Fyi5hcQVMq5nO{SFzB#&6^?jOHNglB0#A~lYOqxq9M-mN zMlVjrc09K2cl)m?t|6lTNzwg}koUSoNhBLOC6P4q6m1U2)8!T*^a~Wekl};38Zw?X zf`1(;eol4>`-q;ik?)yvM(8qv+xW~#viC(WeIWfd7mOh?RxPF0Ya_f}@)trH( z*>09~iWo5uimEJ7H2N@t-@0p1QYp19&*@YY3IPBB09H^qAie+q0FVLzodGHk01yBJ z05m1A0(3A>2!#xM104t~{pbMx{dA!H0-GO-_d|S~H}=vDuhaiEXyWdty~%GI^s2>U zuDYL>gx^7;JK9_1@A%8{y?^1a`ak6f?DV^4U;nBp*?-Aw=Z5C2m$~G|^phiuU)_&6 zRUm(rPy9Lf|8LT31L&W;gP3M*vDFj*b{FQc{#}aXpGUv5`oI1i9rF^8{#?(&`uP~c az0c1dkvX6N{h9$wF)ZG^*?X$20000=+R1SM literal 0 HcmV?d00001 diff --git a/Lib/test/test_email/data/python.xbm b/Lib/test/test_email/data/python.xbm new file mode 100644 index 00000000000..cfbee2e9806 --- /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 b87dae22de1..6ead5947acb 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, ('',)),