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 | 140 ++++++++++++----------- 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, 114 insertions(+), 79 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 c87db6a064b0..34c6bdb60fff 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 fac238c7289f..e19dea91c0c9 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 7e308f542b864cdbfe8e872d37574bfb9b0eced4..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 896 zc-jGg1AqKSNk%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 zc-noEYe72(!tz6fyd=ClJ?HhT-a+nRzYk~6Iln#UzoRr-RtmVX zAce>}ikrY+AS4aicP?;zxP?-alv83`2u^wE=jC5!caw*Od6t;XL2|QsbyI!4dyL1F zFfh3)lyCU2*16Uo8<7Y^iD(ct5{Yc0{beg|{D(n0u{av?ie-Yv0enXR){iVsi|Pgn zupNzvT%9+~uuS}WfT(GJov#6igOtw;mch0h@kQK`Ra{&G!3w(eB<}rl0H%)soMQ?d z698N29N1UTdu>DfkhSza0Z0t#9sB~|aL7i!TEm~)JGR>F9RdCJ4)5M2L=D6F)aBor zC(mlhkuiRbjeG?(=gL4I>U$t- zZs9I*q@rUVI*dHW4xVGf;4d4Qp}RDJ=`4zewi9xwJ|wo12#^xzFBN0Nn?ciJkm?8rL4=%{{9!6+g8YntIB#&7DQ6R{Z_;+DxhSqFHRM z%Zzk7k9fO!za)!0yezKrvY>Cq%hmZli-CLZjx1VoWB+D>?O^(5m#u+HQ`h5jx_K^( W;&^DsEUq1kVQSMPHT0~IzyA-2^0_7e literal 0 Hc-jL100001 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 zc-p;|OK2NM7{|ws9&uwQJ-GIvd6eQfHgzm%ceFb*t0bhgEea`tRK}M!MYSYHph(72 z+!T@$D6)J>DHO-P^Z~_|Pi>*d$t8)QC^D2jE^Z7Z=}ESswDrYtq0si5*^yQ&sqL*Z zEZ=$_-~9GJ{~3AO<7;U}2#sF~CzH`c8eS6dbOyZ)ZD&ynGE0kmUTkOyHnd!5XhGwt z%ac>lbUL0&+TKNHB%BG4#gmcLTjG7#(G-;-PDVof{v}q-V_SY;?J7g># z$y};SfxedC{c5Wjf0S{2pBaCaF>TI3f5`Z*(~JjPX8b_LXWeG}RK^!1&6hG7ZD#zc z&4Rp91ALvP3>4sX)tDnQ{jTSct+?0^bctT3r|2#z5Y$i-1e3#LTc6dt^_K#^&$CHU;5r?q&(KF?u?a_rpuq3?f*#cOwD~3+MT`O= z+= zF2asrysVx8CEW1TayTT%uhgzYg@gN~lQ{b|c91<5&hS$w{YFr5j#2+2d>ukJNrDW4 zGd92(y8cj5aE@-{H}*M0=(v9u7o;;x63G$RF}^nta#r+cLa{kg8UT(0>^0AKjhrJr#Bv6&F5uW>4+V$Q&(+}0SQgIUbwn}O-2o2QKX9xXD|8nR z5%aHQ^$ER4XM{7#!WnadA_RNAr7ZandX3nfkrNctKyjOW3>@p&?u@*kn2=`^A$J|e z3Vk5RGeCY@Tb1J?_*;65zD`fl4@n4D&jyTft4qF!7p9YTvZhby^IC6p*N?LA*nOaQ z2RIfXBgWfYiK^qejh!mTe#v33I7-XdPb;fi+GOx8;f$N0!IqZSy0k0)b@)2B`oNd< z^33hR8PlM_A>+KE8^?^l*iVw;B5*{6Gx!cJSgXs*_oYp8+-E`!|3^*y+QmBY72%8m z-h-21Qeb>SP`Eug;Mfq(7*UQHZy0B66ftl(LRdJ%?fJrJ%>M~+JS92y#2yrIGrunD zIpql4TxC$Br1feu{<5Ih6clbxb^xI1Bw#M0|g3PY{F6NQSQ%5IatS0#4%>Fl-M18Lba<7 zx4+7LQ3NeU2J8EH$$=8{LW|SvZj;}i_l*sCUN#IDv;Xf?%i-t?dh$vSoc0i$$~BLp I5B)FYZ}0KvRR910 literal 0 Hc-jL100001 diff --git a/Lib/test/test_email/data/python.gif b/Lib/test/test_email/data/python.gif new file mode 100644 index 0000000000000000000000000000000000000000..efa0be3861d79f44b006b876a6408e42a09dc8fd GIT binary patch literal 405 zc-jG#0c!q7Nk%w1VGsZi0QElr>$@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<(GK3MNlbMABs8N8MlUsmf@c#gVAP0j0 zg8(z55(ASUBeNjm|04`yV5cGjb{1BkxQYM+BMT!7(8VxuMkb)kS=l*+gawrtL^utN zfT~2%H8U`<0fm(XfvQ=VSrMX)OoGfTtZa%5LWZJZiH#5c-(uilW&}EeS&+e=L1fvb zEa~Ed&xD1pOqKa8nEXUFb>FV2H<_*u8=op{3RxC}#2pA=S4vz?wZ+4R}$^6SrX zmy0(p4VS5xc~$tx+w%HjrQlCenfI;~dRc5~dg0xvRhTJvR#(ybaiv@y0yEpwOUbgM*7O^DLXw={db;d)fDt@)cR0$^OH?OUFbgEZ%GRs&b)Xw%Qj}a z^XBVSsSz7*1}b*!PuX$#x(y?Y3chU=zZ=W$(@EkZ0<@^X^Yc5NS#h?%feI jL&Kc4v#2uRiBV_%;ikUjK|QY&diPaG%CoPn|9=wzUksC{ literal 0 Hc-jL100001 diff --git a/Lib/test/test_email/data/python.pbm b/Lib/test/test_email/data/python.pbm new file mode 100644 index 000000000000..1848ba7ff064 --- /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 0000000000000000000000000000000000000000..8349f2a53a9be1d895a561cc0fd31f91d7d42b33 GIT binary patch literal 269 zc-jjv m_Uzm_3*scO?735L_ujg3_2RkzC*l0F7tWtMb#xj7HUIz!yJVLD literal 0 Hc-jL100001 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 zc-rd>@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=IjczVPIf98sHP+3RK|Nd%&x0mrdb3^NeW>sKBrPh)>sk zkJfF@wQFrl7Fy;^GfSHQkqVl4?C)K#pI7aGY@>vBAo=&I8IUy1m;@x>oCBF`mN{A4 z&=sQo(P7uWHywctx5iCo=@WsBPY?6{zRdc2TH3jGos5z9*T?yv4@x*^Q>tWIn(`n z4|;d(cBuvGT4r0kz%qB{|3{$#{fB)z_jt5y1qRgrd*1(VIr?_*b8FaOT`=eWEvNt2 z%>Q53^=jYg-nhxRX0?6U(*M^j{-0O=|19zUZfZCrY{eK+s|GwY<%liLsyZpay_5ZTL{|l=BZ&>}m0%WL7@?>BF`XsC* z$S;^-{oC)8xS2H^Me7zUT)ceMDi4Xh-tZD36Dc6W(l z-n|qQLti~z977~7Cnqp4xh>o9fg?>U%#2N=qo=E{v$s1vy_Hi%MNM`2l4HdMO_ecs zZ1&XrVaojYvB^ncUqO{yL|RyxpG|;5xJyWnqW*pbt7Qg!C&Z^~Xib}vq~WRI>!s)2 zJ6&x0MBVi(rX;UewQl9q@BopJM*(3gSFZ_A4LE%!==y~#Ss6K5>#{Sih9zC-)Vy*v zJ3Bw;)r*|Wx0jwL7#O@U%XYtR4x|6S|X zJ65i+D_LZfKie#G3Lf>It=ruiHabVqa8^Y1{_6+m)RCCu*{taQ4CT4|551QyIw!9+5y=C z{fB)z_jt5yg*Y2%KE&A&WR%bjQh(3;|1HPASIvNI-|l^2XK#Qxdk)0erWunUir<`b z{eR2p|26afmvw<$ulAkpjhmcnR@;{?HOrhVZRm>Z+DC_7|K4=`f87G8`2Trjpom-J zCWy1m(kBABpC0D@eVO(DS>pf4;s5XZ{=e-6R;>5`yvpCx(m(~ywd)|xmND}F`Z)jp z>zx13;fkIAU$=xP{(na1^Fb+~dWX`*w#AFA3g&?P1yY>!|8d0s`+hLR`VhrHbZC|k zTG+xBzc_CD`l!k4Lt3x*DZbt<0Yt}U3t>ha%w(7U*RB3vHu!%*6=*D;^zio{Sn&<3 n|5t#D)qrUU;zwNI;|2R)mo5KZ(g!I%BlGLHPO+6c7+e=g5+p5Hy^f9`!YJJQc{ z95+kUB95C%4{i!KPBd6bT8EQPK7{f3T_oh!BmO`gAG_~zd1Puuz(k-JFc&w2h5m2@ zG`ZKH&c1?ZYZVk}=ax}E;D7wug8MKWxdm;W3+fy@VlpowGQEQF8p_WZgUPp)KB+Mm zKdvCEO+UE_#ik@AHU1b*9bNu+YmlwXK1E017 zLmPn)$-q0U$gUzbgcqI#v3K|BY&Rs&TjXJ;JHLjkfF}d^?9b|mRZ1-2mbd?Go5%XH z#^>01RTljtnbjume7x)qVmA?M@nb_Jmg!*oMOiqVpH2!U#gZbt{9o4frAy`XJPmhx z+Lt>$Ex}GtQy^1rXO&FjyA0f9(hSmUlFZAG)=i+g(NuRe(7lA!)QB}!kQR*dFZ05H z$o3Qaim^-Cbzt*~r@UOOH;puD4Bq^`JI+1QqvVB`p+43v?;q)!o7Y+?ixGRADA+va Si+yUv@2@9Ckrs-1|M&xssHXk^ literal 0 Hc-jL100001 diff --git a/Lib/test/test_email/data/python.sgi b/Lib/test/test_email/data/python.sgi new file mode 100644 index 0000000000000000000000000000000000000000..ffe9081c7a5b67ed14285041fd0650f4010bc36d GIT binary patch literal 1967 zc-rlhZ%kWN7{=ejZ7D4pYMBwG{#oqiV%+9%6v`;{QVJEi&KMVG+=sajlTH*2V8F?= z?S&R_{~J)528J@MB07<&%sDpQKCE#lDjSO<8;P5l``{MSxTHBg_sC_N2$!A@|7$Z!XM zHngBt0E@s=pc700oFTmnNFagC0}C%_wE7MUfW z9()dx$gKby!5-8bK`Cee=&w7E%mbhXd0Qw%Vdbj-zo`x#0=9q7a2F@L%hWCDZs z`xfmx3)}`QV1Y)KHg*GHo;LrZRwjF%{qqp8n4J1yGI63u6{U{li>-HB6w%>JBso95WtBxq1ygzs zl(KVYPyaZ{PpZOSUi9!{o~wvpQqQHN%W7nL{QKz0z}1?$*|XDAlL=L?O0~Bvb*55O>_SAr$+g% zB1J07U3R*fGH{lX>w~S$&(itdepQ>Wld^YUcyJfJuo+Ft3YXbfF1!dfI_@7#B9>&8 z)LvETF16?CD0CquaE!M{g0H+3SfL~7*qP}mc1pIsIPk%Hdpxi_V&PJDKa=0h=-7In zw^DpAiBtu>zWULMvXWH=_WV5hUFoyEtL?QdP4$}tH5=%=nV;E|>>WP%aeQEJcWifr zJY@xJDZC6k#IZF|WSkUv%;)up&LY7s2&jf6bbo9^~X{(XQ+{yrj==im==m! zp^h{cW?31fnUB&xvXaa+BSVEAETIQ+Nf^I==j?7>vOMFv`#b0SKIiv1cZ-a4V{EOM zc_XH*r!DHtgIUF?I}n!N3c<&kA@Il*);FoD61NwlESUv{s7#l@QAdL; zuT?9_Y-kpzR_T1y=XHLm)s*FU1;ZTm z4e%D@Qq~<=7ce|^X35#Iyc=Vtnnj+J2TPj9GLjC@wm_TwK}_J6CAUsw!`Y;5cD!|afd#V!pBu3*2Z0q` zBcX&mA4kq6=dR%F0@O;*pKOSD{#((upm_=yslb}rLOeeRZ5yujZ?3;!-+ywvVIx31aG>o}HPbNpsIkR;!uc*;LyH literal 0 Hc-jL100001 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 zc-jH50Z;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 Hc-jL100001 diff --git a/Lib/test/test_email/data/python.xbm b/Lib/test/test_email/data/python.xbm new file mode 100644 index 000000000000..cfbee2e98062 --- /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 b87dae22de1d..6ead5947acb6 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, ('',)), -- 2.47.3