PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/local/nagios/venv/lib/python3.13/site-packages/setuptools/tests/config
Viewing File: /usr/local/nagios/venv/lib/python3.13/site-packages/setuptools/tests/config/test_setupcfg.py
import configparser import contextlib import inspect import re from pathlib import Path from unittest.mock import Mock, patch import pytest from packaging.requirements import InvalidRequirement from setuptools.config.setupcfg import ConfigHandler, Target, read_configuration from setuptools.dist import Distribution, _Distribution from setuptools.warnings import SetuptoolsDeprecationWarning from ..textwrap import DALS from distutils.errors import DistutilsFileError, DistutilsOptionError class ErrConfigHandler(ConfigHandler[Target]): """Erroneous handler. Fails to implement required methods.""" section_prefix = "**err**" def make_package_dir(name, base_dir, ns=False): dir_package = base_dir for dir_name in name.split('/'): dir_package = dir_package.mkdir(dir_name) init_file = None if not ns: init_file = dir_package.join('__init__.py') init_file.write('') return dir_package, init_file def fake_env( tmpdir, setup_cfg, setup_py=None, encoding='ascii', package_path='fake_package' ): if setup_py is None: setup_py = 'from setuptools import setup\nsetup()\n' tmpdir.join('setup.py').write(setup_py) config = tmpdir.join('setup.cfg') config.write(setup_cfg.encode(encoding), mode='wb') package_dir, init_file = make_package_dir(package_path, tmpdir) init_file.write( 'VERSION = (1, 2, 3)\n' '\n' 'VERSION_MAJOR = 1' '\n' 'def get_version():\n' ' return [3, 4, 5, "dev"]\n' '\n' ) return package_dir, config @contextlib.contextmanager def get_dist(tmpdir, kwargs_initial=None, parse=True): kwargs_initial = kwargs_initial or {} with tmpdir.as_cwd(): dist = Distribution(kwargs_initial) dist.script_name = 'setup.py' parse and dist.parse_config_files() yield dist def test_parsers_implemented(): with pytest.raises(NotImplementedError): handler = ErrConfigHandler(None, {}, False, Mock()) handler.parsers class TestConfigurationReader: def test_basic(self, tmpdir): _, config = fake_env( tmpdir, '[metadata]\n' 'version = 10.1.1\n' 'keywords = one, two\n' '\n' '[options]\n' 'scripts = bin/a.py, bin/b.py\n', ) config_dict = read_configuration(str(config)) assert config_dict['metadata']['version'] == '10.1.1' assert config_dict['metadata']['keywords'] == ['one', 'two'] assert config_dict['options']['scripts'] == ['bin/a.py', 'bin/b.py'] def test_no_config(self, tmpdir): with pytest.raises(DistutilsFileError): read_configuration(str(tmpdir.join('setup.cfg'))) def test_ignore_errors(self, tmpdir): _, config = fake_env( tmpdir, '[metadata]\nversion = attr: none.VERSION\nkeywords = one, two\n', ) with pytest.raises(ImportError): read_configuration(str(config)) config_dict = read_configuration(str(config), ignore_option_errors=True) assert config_dict['metadata']['keywords'] == ['one', 'two'] assert 'version' not in config_dict['metadata'] config.remove() class TestMetadata: def test_basic(self, tmpdir): fake_env( tmpdir, '[metadata]\n' 'version = 10.1.1\n' 'description = Some description\n' 'long_description_content_type = text/something\n' 'long_description = file: README\n' 'name = fake_name\n' 'keywords = one, two\n' 'provides = package, package.sub\n' 'license = otherlic\n' 'download_url = http://test.test.com/test/\n' 'maintainer_email = test@test.com\n', ) tmpdir.join('README').write('readme contents\nline2') meta_initial = { # This will be used so `otherlic` won't replace it. 'license': 'BSD 3-Clause License', } with get_dist(tmpdir, meta_initial) as dist: metadata = dist.metadata assert metadata.version == '10.1.1' assert metadata.description == 'Some description' assert metadata.long_description_content_type == 'text/something' assert metadata.long_description == 'readme contents\nline2' assert metadata.provides == ['package', 'package.sub'] assert metadata.license == 'BSD 3-Clause License' assert metadata.name == 'fake_name' assert metadata.keywords == ['one', 'two'] assert metadata.download_url == 'http://test.test.com/test/' assert metadata.maintainer_email == 'test@test.com' def test_license_cfg(self, tmpdir): fake_env( tmpdir, DALS( """ [metadata] name=foo version=0.0.1 license=Apache 2.0 """ ), ) with get_dist(tmpdir) as dist: metadata = dist.metadata assert metadata.name == "foo" assert metadata.version == "0.0.1" assert metadata.license == "Apache 2.0" def test_file_mixed(self, tmpdir): fake_env( tmpdir, '[metadata]\nlong_description = file: README.rst, CHANGES.rst\n\n', ) tmpdir.join('README.rst').write('readme contents\nline2') tmpdir.join('CHANGES.rst').write('changelog contents\nand stuff') with get_dist(tmpdir) as dist: assert dist.metadata.long_description == ( 'readme contents\nline2\nchangelog contents\nand stuff' ) def test_file_sandboxed(self, tmpdir): tmpdir.ensure("README") project = tmpdir.join('depth1', 'depth2') project.ensure(dir=True) fake_env(project, '[metadata]\nlong_description = file: ../../README\n') with get_dist(project, parse=False) as dist: with pytest.raises(DistutilsOptionError): dist.parse_config_files() # file: out of sandbox def test_aliases(self, tmpdir): fake_env( tmpdir, '[metadata]\n' 'author_email = test@test.com\n' 'home_page = http://test.test.com/test/\n' 'summary = Short summary\n' 'platform = a, b\n' 'classifier =\n' ' Framework :: Django\n' ' Programming Language :: Python :: 3.5\n', ) with get_dist(tmpdir) as dist: metadata = dist.metadata assert metadata.author_email == 'test@test.com' assert metadata.url == 'http://test.test.com/test/' assert metadata.description == 'Short summary' assert metadata.platforms == ['a', 'b'] assert metadata.classifiers == [ 'Framework :: Django', 'Programming Language :: Python :: 3.5', ] def test_multiline(self, tmpdir): fake_env( tmpdir, '[metadata]\n' 'name = fake_name\n' 'keywords =\n' ' one\n' ' two\n' 'classifiers =\n' ' Framework :: Django\n' ' Programming Language :: Python :: 3.5\n', ) with get_dist(tmpdir) as dist: metadata = dist.metadata assert metadata.keywords == ['one', 'two'] assert metadata.classifiers == [ 'Framework :: Django', 'Programming Language :: Python :: 3.5', ] def test_dict(self, tmpdir): fake_env( tmpdir, '[metadata]\n' 'project_urls =\n' ' Link One = https://example.com/one/\n' ' Link Two = https://example.com/two/\n', ) with get_dist(tmpdir) as dist: metadata = dist.metadata assert metadata.project_urls == { 'Link One': 'https://example.com/one/', 'Link Two': 'https://example.com/two/', } def test_version(self, tmpdir): package_dir, config = fake_env( tmpdir, '[metadata]\nversion = attr: fake_package.VERSION\n' ) sub_a = package_dir.mkdir('subpkg_a') sub_a.join('__init__.py').write('') sub_a.join('mod.py').write('VERSION = (2016, 11, 26)') sub_b = package_dir.mkdir('subpkg_b') sub_b.join('__init__.py').write('') sub_b.join('mod.py').write( 'import third_party_module\nVERSION = (2016, 11, 26)' ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' config.write('[metadata]\nversion = attr: fake_package.get_version\n') with get_dist(tmpdir) as dist: assert dist.metadata.version == '3.4.5.dev' config.write('[metadata]\nversion = attr: fake_package.VERSION_MAJOR\n') with get_dist(tmpdir) as dist: assert dist.metadata.version == '1' config.write('[metadata]\nversion = attr: fake_package.subpkg_a.mod.VERSION\n') with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' config.write('[metadata]\nversion = attr: fake_package.subpkg_b.mod.VERSION\n') with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' def test_version_file(self, tmpdir): fake_env(tmpdir, '[metadata]\nversion = file: fake_package/version.txt\n') tmpdir.join('fake_package', 'version.txt').write('1.2.3\n') with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' tmpdir.join('fake_package', 'version.txt').write('1.2.3\n4.5.6\n') with pytest.raises(DistutilsOptionError): with get_dist(tmpdir) as dist: dist.metadata.version def test_version_with_package_dir_simple(self, tmpdir): fake_env( tmpdir, '[metadata]\n' 'version = attr: fake_package_simple.VERSION\n' '[options]\n' 'package_dir =\n' ' = src\n', package_path='src/fake_package_simple', ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' def test_version_with_package_dir_rename(self, tmpdir): fake_env( tmpdir, '[metadata]\n' 'version = attr: fake_package_rename.VERSION\n' '[options]\n' 'package_dir =\n' ' fake_package_rename = fake_dir\n', package_path='fake_dir', ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' def test_version_with_package_dir_complex(self, tmpdir): fake_env( tmpdir, '[metadata]\n' 'version = attr: fake_package_complex.VERSION\n' '[options]\n' 'package_dir =\n' ' fake_package_complex = src/fake_dir\n', package_path='src/fake_dir', ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' def test_unknown_meta_item(self, tmpdir): fake_env(tmpdir, '[metadata]\nname = fake_name\nunknown = some\n') with get_dist(tmpdir, parse=False) as dist: dist.parse_config_files() # Skip unknown. def test_usupported_section(self, tmpdir): fake_env(tmpdir, '[metadata.some]\nkey = val\n') with get_dist(tmpdir, parse=False) as dist: with pytest.raises(DistutilsOptionError): dist.parse_config_files() def test_classifiers(self, tmpdir): expected = set([ 'Framework :: Django', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', ]) # From file. _, config = fake_env(tmpdir, '[metadata]\nclassifiers = file: classifiers\n') tmpdir.join('classifiers').write( 'Framework :: Django\n' 'Programming Language :: Python :: 3\n' 'Programming Language :: Python :: 3.5\n' ) with get_dist(tmpdir) as dist: assert set(dist.metadata.classifiers) == expected # From list notation config.write( '[metadata]\n' 'classifiers =\n' ' Framework :: Django\n' ' Programming Language :: Python :: 3\n' ' Programming Language :: Python :: 3.5\n' ) with get_dist(tmpdir) as dist: assert set(dist.metadata.classifiers) == expected def test_interpolation(self, tmpdir): fake_env(tmpdir, '[metadata]\ndescription = %(message)s\n') with pytest.raises(configparser.InterpolationMissingOptionError): with get_dist(tmpdir): pass def test_non_ascii_1(self, tmpdir): fake_env(tmpdir, '[metadata]\ndescription = éàïôñ\n', encoding='utf-8') with get_dist(tmpdir): pass def test_non_ascii_3(self, tmpdir): fake_env(tmpdir, '\n# -*- coding: invalid\n') with get_dist(tmpdir): pass def test_non_ascii_4(self, tmpdir): fake_env( tmpdir, '# -*- coding: utf-8\n[metadata]\ndescription = éàïôñ\n', encoding='utf-8', ) with get_dist(tmpdir) as dist: assert dist.metadata.description == 'éàïôñ' def test_not_utf8(self, tmpdir): """ Config files encoded not in UTF-8 will fail """ fake_env( tmpdir, '# vim: set fileencoding=iso-8859-15 :\n[metadata]\ndescription = éàïôñ\n', encoding='iso-8859-15', ) with pytest.raises(UnicodeDecodeError): with get_dist(tmpdir): pass @pytest.mark.parametrize( ("error_msg", "config", "invalid"), [ ( "Invalid dash-separated key 'author-email' in 'metadata' (setup.cfg)", DALS( """ [metadata] author-email = test@test.com maintainer_email = foo@foo.com """ ), {"author-email": "test@test.com"}, ), ( "Invalid uppercase key 'Name' in 'metadata' (setup.cfg)", DALS( """ [metadata] Name = foo description = Some description """ ), {"Name": "foo"}, ), ], ) def test_invalid_options_previously_deprecated( self, tmpdir, error_msg, config, invalid ): # This test and related methods can be removed when no longer needed. # Deprecation postponed due to push-back from the community in # https://github.com/pypa/setuptools/issues/4910 fake_env(tmpdir, config) with pytest.warns(SetuptoolsDeprecationWarning, match=re.escape(error_msg)): dist = get_dist(tmpdir).__enter__() tmpdir.join('setup.cfg').remove() for field, value in invalid.items(): attr = field.replace("-", "_").lower() assert getattr(dist.metadata, attr) == value class TestOptions: def test_basic(self, tmpdir): fake_env( tmpdir, '[options]\n' 'zip_safe = True\n' 'include_package_data = yes\n' 'package_dir = b=c, =src\n' 'packages = pack_a, pack_b.subpack\n' 'namespace_packages = pack1, pack2\n' 'scripts = bin/one.py, bin/two.py\n' 'eager_resources = bin/one.py, bin/two.py\n' 'install_requires = docutils>=0.3; pack ==1.1, ==1.3; hey\n' 'setup_requires = docutils>=0.3; spack ==1.1, ==1.3; there\n' 'dependency_links = http://some.com/here/1, ' 'http://some.com/there/2\n' 'python_requires = >=1.0, !=2.8\n' 'py_modules = module1, module2\n', ) deprec = pytest.warns(SetuptoolsDeprecationWarning, match="namespace_packages") with deprec, get_dist(tmpdir) as dist: assert dist.zip_safe assert dist.include_package_data assert dist.package_dir == {'': 'src', 'b': 'c'} assert dist.packages == ['pack_a', 'pack_b.subpack'] assert dist.namespace_packages == ['pack1', 'pack2'] assert dist.scripts == ['bin/one.py', 'bin/two.py'] assert dist.dependency_links == ([ 'http://some.com/here/1', 'http://some.com/there/2', ]) assert dist.install_requires == ([ 'docutils>=0.3', 'pack==1.1,==1.3', 'hey', ]) assert dist.setup_requires == ([ 'docutils>=0.3', 'spack ==1.1, ==1.3', 'there', ]) assert dist.python_requires == '>=1.0, !=2.8' assert dist.py_modules == ['module1', 'module2'] def test_multiline(self, tmpdir): fake_env( tmpdir, '[options]\n' 'package_dir = \n' ' b=c\n' ' =src\n' 'packages = \n' ' pack_a\n' ' pack_b.subpack\n' 'namespace_packages = \n' ' pack1\n' ' pack2\n' 'scripts = \n' ' bin/one.py\n' ' bin/two.py\n' 'eager_resources = \n' ' bin/one.py\n' ' bin/two.py\n' 'install_requires = \n' ' docutils>=0.3\n' ' pack ==1.1, ==1.3\n' ' hey\n' 'setup_requires = \n' ' docutils>=0.3\n' ' spack ==1.1, ==1.3\n' ' there\n' 'dependency_links = \n' ' http://some.com/here/1\n' ' http://some.com/there/2\n', ) deprec = pytest.warns(SetuptoolsDeprecationWarning, match="namespace_packages") with deprec, get_dist(tmpdir) as dist: assert dist.package_dir == {'': 'src', 'b': 'c'} assert dist.packages == ['pack_a', 'pack_b.subpack'] assert dist.namespace_packages == ['pack1', 'pack2'] assert dist.scripts == ['bin/one.py', 'bin/two.py'] assert dist.dependency_links == ([ 'http://some.com/here/1', 'http://some.com/there/2', ]) assert dist.install_requires == ([ 'docutils>=0.3', 'pack==1.1,==1.3', 'hey', ]) assert dist.setup_requires == ([ 'docutils>=0.3', 'spack ==1.1, ==1.3', 'there', ]) def test_package_dir_fail(self, tmpdir): fake_env(tmpdir, '[options]\npackage_dir = a b\n') with get_dist(tmpdir, parse=False) as dist: with pytest.raises(DistutilsOptionError): dist.parse_config_files() def test_package_data(self, tmpdir): fake_env( tmpdir, '[options.package_data]\n' '* = *.txt, *.rst\n' 'hello = *.msg\n' '\n' '[options.exclude_package_data]\n' '* = fake1.txt, fake2.txt\n' 'hello = *.dat\n', ) with get_dist(tmpdir) as dist: assert dist.package_data == { '': ['*.txt', '*.rst'], 'hello': ['*.msg'], } assert dist.exclude_package_data == { '': ['fake1.txt', 'fake2.txt'], 'hello': ['*.dat'], } def test_packages(self, tmpdir): fake_env(tmpdir, '[options]\npackages = find:\n') with get_dist(tmpdir) as dist: assert dist.packages == ['fake_package'] def test_find_directive(self, tmpdir): dir_package, config = fake_env(tmpdir, '[options]\npackages = find:\n') make_package_dir('sub_one', dir_package) make_package_dir('sub_two', dir_package) with get_dist(tmpdir) as dist: assert set(dist.packages) == set([ 'fake_package', 'fake_package.sub_two', 'fake_package.sub_one', ]) config.write( '[options]\n' 'packages = find:\n' '\n' '[options.packages.find]\n' 'where = .\n' 'include =\n' ' fake_package.sub_one\n' ' two\n' ) with get_dist(tmpdir) as dist: assert dist.packages == ['fake_package.sub_one'] config.write( '[options]\n' 'packages = find:\n' '\n' '[options.packages.find]\n' 'exclude =\n' ' fake_package.sub_one\n' ) with get_dist(tmpdir) as dist: assert set(dist.packages) == set(['fake_package', 'fake_package.sub_two']) def test_find_namespace_directive(self, tmpdir): dir_package, config = fake_env( tmpdir, '[options]\npackages = find_namespace:\n' ) make_package_dir('sub_one', dir_package) make_package_dir('sub_two', dir_package, ns=True) with get_dist(tmpdir) as dist: assert set(dist.packages) == { 'fake_package', 'fake_package.sub_two', 'fake_package.sub_one', } config.write( '[options]\n' 'packages = find_namespace:\n' '\n' '[options.packages.find]\n' 'where = .\n' 'include =\n' ' fake_package.sub_one\n' ' two\n' ) with get_dist(tmpdir) as dist: assert dist.packages == ['fake_package.sub_one'] config.write( '[options]\n' 'packages = find_namespace:\n' '\n' '[options.packages.find]\n' 'exclude =\n' ' fake_package.sub_one\n' ) with get_dist(tmpdir) as dist: assert set(dist.packages) == {'fake_package', 'fake_package.sub_two'} def test_extras_require(self, tmpdir): fake_env( tmpdir, '[options.extras_require]\n' 'pdf = ReportLab>=1.2; RXP\n' 'rest = \n' ' docutils>=0.3\n' ' pack ==1.1, ==1.3\n', ) with get_dist(tmpdir) as dist: assert dist.extras_require == { 'pdf': ['ReportLab>=1.2', 'RXP'], 'rest': ['docutils>=0.3', 'pack==1.1,==1.3'], } assert set(dist.metadata.provides_extras) == {'pdf', 'rest'} @pytest.mark.parametrize( "config", [ "[options.extras_require]\nfoo = bar;python_version<'3'", "[options.extras_require]\nfoo = bar;os_name=='linux'", "[options.extras_require]\nfoo = bar;python_version<'3'\n", "[options.extras_require]\nfoo = bar;os_name=='linux'\n", "[options]\ninstall_requires = bar;python_version<'3'", "[options]\ninstall_requires = bar;os_name=='linux'", "[options]\ninstall_requires = bar;python_version<'3'\n", "[options]\ninstall_requires = bar;os_name=='linux'\n", ], ) def test_raises_accidental_env_marker_misconfig(self, config, tmpdir): fake_env(tmpdir, config) match = ( r"One of the parsed requirements in `(install_requires|extras_require.+)` " "looks like a valid environment marker.*" ) with pytest.raises(InvalidRequirement, match=match): with get_dist(tmpdir) as _: pass @pytest.mark.parametrize( "config", [ "[options.extras_require]\nfoo = bar;python_version<3", "[options.extras_require]\nfoo = bar;python_version<3\n", "[options]\ninstall_requires = bar;python_version<3", "[options]\ninstall_requires = bar;python_version<3\n", ], ) def test_warn_accidental_env_marker_misconfig(self, config, tmpdir): fake_env(tmpdir, config) match = ( r"One of the parsed requirements in `(install_requires|extras_require.+)` " "looks like a valid environment marker.*" ) with pytest.warns(SetuptoolsDeprecationWarning, match=match): with get_dist(tmpdir) as _: pass @pytest.mark.parametrize( "config", [ "[options.extras_require]\nfoo =\n bar;python_version<'3'", "[options.extras_require]\nfoo = bar;baz\nboo = xxx;yyy", "[options.extras_require]\nfoo =\n bar;python_version<'3'\n", "[options.extras_require]\nfoo = bar;baz\nboo = xxx;yyy\n", "[options.extras_require]\nfoo =\n bar\n python_version<3\n", "[options]\ninstall_requires =\n bar;python_version<'3'", "[options]\ninstall_requires = bar;baz\nboo = xxx;yyy", "[options]\ninstall_requires =\n bar;python_version<'3'\n", "[options]\ninstall_requires = bar;baz\nboo = xxx;yyy\n", "[options]\ninstall_requires =\n bar\n python_version<3\n", ], ) @pytest.mark.filterwarnings("error::setuptools.SetuptoolsDeprecationWarning") def test_nowarn_accidental_env_marker_misconfig(self, config, tmpdir, recwarn): fake_env(tmpdir, config) num_warnings = len(recwarn) with get_dist(tmpdir) as _: pass # The examples are valid, no warnings shown assert len(recwarn) == num_warnings def test_dash_preserved_extras_require(self, tmpdir): fake_env(tmpdir, '[options.extras_require]\nfoo-a = foo\nfoo_b = test\n') with get_dist(tmpdir) as dist: assert dist.extras_require == {'foo-a': ['foo'], 'foo_b': ['test']} def test_entry_points(self, tmpdir): _, config = fake_env( tmpdir, '[options.entry_points]\n' 'group1 = point1 = pack.module:func, ' '.point2 = pack.module2:func_rest [rest]\n' 'group2 = point3 = pack.module:func2\n', ) with get_dist(tmpdir) as dist: assert dist.entry_points == { 'group1': [ 'point1 = pack.module:func', '.point2 = pack.module2:func_rest [rest]', ], 'group2': ['point3 = pack.module:func2'], } expected = ( '[blogtool.parsers]\n' '.rst = some.nested.module:SomeClass.some_classmethod[reST]\n' ) tmpdir.join('entry_points').write(expected) # From file. config.write('[options]\nentry_points = file: entry_points\n') with get_dist(tmpdir) as dist: assert dist.entry_points == expected def test_case_sensitive_entry_points(self, tmpdir): fake_env( tmpdir, '[options.entry_points]\n' 'GROUP1 = point1 = pack.module:func, ' '.point2 = pack.module2:func_rest [rest]\n' 'group2 = point3 = pack.module:func2\n', ) with get_dist(tmpdir) as dist: assert dist.entry_points == { 'GROUP1': [ 'point1 = pack.module:func', '.point2 = pack.module2:func_rest [rest]', ], 'group2': ['point3 = pack.module:func2'], } def test_data_files(self, tmpdir): fake_env( tmpdir, '[options.data_files]\n' 'cfg =\n' ' a/b.conf\n' ' c/d.conf\n' 'data = e/f.dat, g/h.dat\n', ) with get_dist(tmpdir) as dist: expected = [ ('cfg', ['a/b.conf', 'c/d.conf']), ('data', ['e/f.dat', 'g/h.dat']), ] assert sorted(dist.data_files) == sorted(expected) def test_data_files_globby(self, tmpdir): fake_env( tmpdir, '[options.data_files]\n' 'cfg =\n' ' a/b.conf\n' ' c/d.conf\n' 'data = *.dat\n' 'icons = \n' ' *.ico\n' 'audio = \n' ' *.wav\n' ' sounds.db\n', ) # Create dummy files for glob()'s sake: tmpdir.join('a.dat').write('') tmpdir.join('b.dat').write('') tmpdir.join('c.dat').write('') tmpdir.join('a.ico').write('') tmpdir.join('b.ico').write('') tmpdir.join('c.ico').write('') tmpdir.join('beep.wav').write('') tmpdir.join('boop.wav').write('') tmpdir.join('sounds.db').write('') with get_dist(tmpdir) as dist: expected = [ ('cfg', ['a/b.conf', 'c/d.conf']), ('data', ['a.dat', 'b.dat', 'c.dat']), ('icons', ['a.ico', 'b.ico', 'c.ico']), ('audio', ['beep.wav', 'boop.wav', 'sounds.db']), ] assert sorted(dist.data_files) == sorted(expected) def test_python_requires_simple(self, tmpdir): fake_env( tmpdir, DALS( """ [options] python_requires=>=2.7 """ ), ) with get_dist(tmpdir) as dist: dist.parse_config_files() def test_python_requires_compound(self, tmpdir): fake_env( tmpdir, DALS( """ [options] python_requires=>=2.7,!=3.0.* """ ), ) with get_dist(tmpdir) as dist: dist.parse_config_files() def test_python_requires_invalid(self, tmpdir): fake_env( tmpdir, DALS( """ [options] python_requires=invalid """ ), ) with pytest.raises(Exception): with get_dist(tmpdir) as dist: dist.parse_config_files() def test_cmdclass(self, tmpdir): module_path = Path(tmpdir, "src/custom_build.py") # auto discovery for src module_path.parent.mkdir(parents=True, exist_ok=True) module_path.write_text( "from distutils.core import Command\nclass CustomCmd(Command): pass\n", encoding="utf-8", ) setup_cfg = """ [options] cmdclass = customcmd = custom_build.CustomCmd """ fake_env(tmpdir, inspect.cleandoc(setup_cfg)) with get_dist(tmpdir) as dist: cmdclass = dist.cmdclass['customcmd'] assert cmdclass.__name__ == "CustomCmd" assert cmdclass.__module__ == "custom_build" assert module_path.samefile(inspect.getfile(cmdclass)) def test_requirements_file(self, tmpdir): fake_env( tmpdir, DALS( """ [options] install_requires = file:requirements.txt [options.extras_require] colors = file:requirements-extra.txt """ ), ) tmpdir.join('requirements.txt').write('\ndocutils>=0.3\n\n') tmpdir.join('requirements-extra.txt').write('colorama') with get_dist(tmpdir) as dist: assert dist.install_requires == ['docutils>=0.3'] assert dist.extras_require == {'colors': ['colorama']} saved_dist_init = _Distribution.__init__ class TestExternalSetters: # During creation of the setuptools Distribution() object, we call # the init of the parent distutils Distribution object via # _Distribution.__init__ (). # # It's possible distutils calls out to various keyword # implementations (i.e. distutils.setup_keywords entry points) # that may set a range of variables. # # This wraps distutil's Distribution.__init__ and simulates # pbr or something else setting these values. def _fake_distribution_init(self, dist, attrs): saved_dist_init(dist, attrs) # see self._DISTUTILS_UNSUPPORTED_METADATA dist.metadata.long_description_content_type = 'text/something' # Test overwrite setup() args dist.metadata.project_urls = { 'Link One': 'https://example.com/one/', 'Link Two': 'https://example.com/two/', } @patch.object(_Distribution, '__init__', autospec=True) def test_external_setters(self, mock_parent_init, tmpdir): mock_parent_init.side_effect = self._fake_distribution_init dist = Distribution(attrs={'project_urls': {'will_be': 'ignored'}}) assert dist.metadata.long_description_content_type == 'text/something' assert dist.metadata.project_urls == { 'Link One': 'https://example.com/one/', 'Link Two': 'https://example.com/two/', }