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: /opt/imh-python/lib/python3.9/site-packages/twisted/scripts
Viewing File: /opt/imh-python/lib/python3.9/site-packages/twisted/scripts/trial.py
# -*- test-case-name: twisted.trial.test.test_script -*- # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import gc import inspect import os import pdb import random import sys import time import warnings from twisted import plugin from twisted.application import app from twisted.internet import defer from twisted.python import failure, reflect, usage from twisted.python.filepath import FilePath from twisted.python.reflect import namedModule from twisted.trial import itrial, reporter, runner # Yea, this is stupid. Leave it for command-line compatibility for a # while, though. TBFORMAT_MAP = { "plain": "default", "default": "default", "emacs": "brief", "brief": "brief", "cgitb": "verbose", "verbose": "verbose", } def _parseLocalVariables(line): """ Accepts a single line in Emacs local variable declaration format and returns a dict of all the variables {name: value}. Raises ValueError if 'line' is in the wrong format. See http://www.gnu.org/software/emacs/manual/html_node/File-Variables.html """ paren = "-*-" start = line.find(paren) + len(paren) end = line.rfind(paren) if start == -1 or end == -1: raise ValueError(f"{line!r} not a valid local variable declaration") items = line[start:end].split(";") localVars = {} for item in items: if len(item.strip()) == 0: continue split = item.split(":") if len(split) != 2: raise ValueError(f"{line!r} contains invalid declaration {item!r}") localVars[split[0].strip()] = split[1].strip() return localVars def loadLocalVariables(filename): """ Accepts a filename and attempts to load the Emacs variable declarations from that file, simulating what Emacs does. See http://www.gnu.org/software/emacs/manual/html_node/File-Variables.html """ with open(filename) as f: lines = [f.readline(), f.readline()] for line in lines: try: return _parseLocalVariables(line) except ValueError: pass return {} def getTestModules(filename): testCaseVar = loadLocalVariables(filename).get("test-case-name", None) if testCaseVar is None: return [] return testCaseVar.split(",") def isTestFile(filename): """ Returns true if 'filename' looks like a file containing unit tests. False otherwise. Doesn't care whether filename exists. """ basename = os.path.basename(filename) return basename.startswith("test_") and os.path.splitext(basename)[1] == (".py") def _reporterAction(): return usage.CompleteList([p.longOpt for p in plugin.getPlugins(itrial.IReporter)]) def _maybeFindSourceLine(testThing): """ Try to find the source line of the given test thing. @param testThing: the test item to attempt to inspect @type testThing: an L{TestCase}, test method, or module, though only the former two have a chance to succeed @rtype: int @return: the starting source line, or -1 if one couldn't be found """ # an instance of L{TestCase} -- locate the test it will run method = getattr(testThing, "_testMethodName", None) if method is not None: testThing = getattr(testThing, method) # If it's a function, we can get the line number even if the source file no # longer exists code = getattr(testThing, "__code__", None) if code is not None: return code.co_firstlineno try: return inspect.getsourcelines(testThing)[1] except (OSError, TypeError): # either testThing is a module, which raised a TypeError, or the file # couldn't be read return -1 # orders which can be passed to trial --order _runOrders = { "alphabetical": ( "alphabetical order for test methods, arbitrary order for test cases", runner.name, ), "toptobottom": ( "attempt to run test cases and methods in the order they were defined", _maybeFindSourceLine, ), } def _checkKnownRunOrder(order): """ Check that the given order is a known test running order. Does nothing else, since looking up the appropriate callable to sort the tests should be done when it actually will be used, as the default argument will not be coerced by this function. @param order: one of the known orders in C{_runOrders} @return: the order unmodified """ if order not in _runOrders: raise usage.UsageError( "--order must be one of: %s. See --help-orders for details" % (", ".join(repr(order) for order in _runOrders),) ) return order class _BasicOptions: """ Basic options shared between trial and its local workers. """ longdesc = ( "trial loads and executes a suite of unit tests, obtained " "from modules, packages and files listed on the command line." ) optFlags = [ ["help", "h"], ["no-recurse", "N", "Don't recurse into packages"], ["help-orders", None, "Help on available test running orders"], ["help-reporters", None, "Help on available output plugins (reporters)"], [ "rterrors", "e", "realtime errors, print out tracebacks as " "soon as they occur", ], ["unclean-warnings", None, "Turn dirty reactor errors into warnings"], [ "force-gc", None, "Have Trial run gc.collect() before and " "after each test case.", ], [ "exitfirst", "x", "Exit after the first non-successful result (cannot be " "specified along with --jobs).", ], ] optParameters = [ [ "order", "o", None, "Specify what order to run test cases and methods. " "See --help-orders for more info.", _checkKnownRunOrder, ], ["random", "z", None, "Run tests in random order using the specified seed"], [ "temp-directory", None, "_trial_temp", "Path to use as working directory for tests.", ], [ "reporter", None, "verbose", "The reporter to use for this test run. See --help-reporters for " "more info.", ], ] compData = usage.Completions( optActions={ "order": usage.CompleteList(_runOrders), "reporter": _reporterAction, "logfile": usage.CompleteFiles(descr="log file name"), "random": usage.Completer(descr="random seed"), }, extraActions=[ usage.CompleteFiles( "*.py", descr="file | module | package | TestCase | testMethod", repeat=True, ) ], ) fallbackReporter = reporter.TreeReporter tracer = None def __init__(self): self["tests"] = [] usage.Options.__init__(self) def getSynopsis(self): executableName = reflect.filenameToModuleName(sys.argv[0]) if executableName.endswith(".__main__"): executableName = "{} -m {}".format( os.path.basename(sys.executable), executableName.replace(".__main__", ""), ) return """{} [options] [[file|package|module|TestCase|testmethod]...] """.format( executableName, ) def coverdir(self): """ Return a L{FilePath} representing the directory into which coverage results should be written. """ coverdir = "coverage" result = FilePath(self["temp-directory"]).child(coverdir) print(f"Setting coverage directory to {result.path}.") return result # TODO: Some of the opt_* methods on this class have docstrings and some do # not. This is mostly because usage.Options's currently will replace # any intended output in optFlags and optParameters with the # docstring. See #6427. When that is fixed, all methods should be # given docstrings (and it should be verified that those with # docstrings already have content suitable for printing as usage # information). def opt_coverage(self): """ Generate coverage information in the coverage file in the directory specified by the temp-directory option. """ import trace self.tracer = trace.Trace(count=1, trace=0) sys.settrace(self.tracer.globaltrace) self["coverage"] = True def opt_testmodule(self, filename): """ Filename to grep for test cases (-*- test-case-name). """ # If the filename passed to this parameter looks like a test module # we just add that to the test suite. # # If not, we inspect it for an Emacs buffer local variable called # 'test-case-name'. If that variable is declared, we try to add its # value to the test suite as a module. # # This parameter allows automated processes (like Buildbot) to pass # a list of files to Trial with the general expectation of "these files, # whatever they are, will get tested" if not os.path.isfile(filename): sys.stderr.write(f"File {filename!r} doesn't exist\n") return filename = os.path.abspath(filename) if isTestFile(filename): self["tests"].append(filename) else: self["tests"].extend(getTestModules(filename)) def opt_spew(self): """ Print an insanely verbose log of everything that happens. Useful when debugging freezes or locks in complex code. """ from twisted.python.util import spewer sys.settrace(spewer) def opt_help_orders(self): synopsis = ( "Trial can attempt to run test cases and their methods in " "a few different orders. You can select any of the " "following options using --order=<foo>.\n" ) print(synopsis) for name, (description, _) in sorted(_runOrders.items()): print(" ", name, "\t", description) sys.exit(0) def opt_help_reporters(self): synopsis = ( "Trial's output can be customized using plugins called " "Reporters. You can\nselect any of the following " "reporters using --reporter=<foo>\n" ) print(synopsis) for p in plugin.getPlugins(itrial.IReporter): print(" ", p.longOpt, "\t", p.description) sys.exit(0) def opt_disablegc(self): """ Disable the garbage collector """ self["disablegc"] = True gc.disable() def opt_tbformat(self, opt): """ Specify the format to display tracebacks with. Valid formats are 'plain', 'emacs', and 'cgitb' which uses the nicely verbose stdlib cgitb.text function """ try: self["tbformat"] = TBFORMAT_MAP[opt] except KeyError: raise usage.UsageError("tbformat must be 'plain', 'emacs', or 'cgitb'.") def opt_recursionlimit(self, arg): """ see sys.setrecursionlimit() """ try: sys.setrecursionlimit(int(arg)) except (TypeError, ValueError): raise usage.UsageError("argument to recursionlimit must be an integer") else: self["recursionlimit"] = int(arg) def opt_random(self, option): try: self["random"] = int(option) except ValueError: raise usage.UsageError("Argument to --random must be a positive integer") else: if self["random"] < 0: raise usage.UsageError( "Argument to --random must be a positive integer" ) elif self["random"] == 0: self["random"] = int(time.time() * 100) def opt_without_module(self, option): """ Fake the lack of the specified modules, separated with commas. """ self["without-module"] = option for module in option.split(","): if module in sys.modules: warnings.warn( "Module '%s' already imported, " "disabling anyway." % (module,), category=RuntimeWarning, ) sys.modules[module] = None def parseArgs(self, *args): self["tests"].extend(args) def _loadReporterByName(self, name): for p in plugin.getPlugins(itrial.IReporter): qual = f"{p.module}.{p.klass}" if p.longOpt == name: return reflect.namedAny(qual) raise usage.UsageError( "Only pass names of Reporter plugins to " "--reporter. See --help-reporters for " "more info." ) def postOptions(self): # Only load reporters now, as opposed to any earlier, to avoid letting # application-defined plugins muck up reactor selecting by importing # t.i.reactor and causing the default to be installed. self["reporter"] = self._loadReporterByName(self["reporter"]) if "tbformat" not in self: self["tbformat"] = "default" if self["order"] is not None and self["random"] is not None: raise usage.UsageError("You can't specify --random when using --order") class Options(_BasicOptions, usage.Options, app.ReactorSelectionMixin): """ Options to the trial command line tool. @ivar _workerFlags: List of flags which are accepted by trial distributed workers. This is used by C{_getWorkerArguments} to build the command line arguments. @type _workerFlags: C{list} @ivar _workerParameters: List of parameter which are accepted by trial distributed workers. This is used by C{_getWorkerArguments} to build the command line arguments. @type _workerParameters: C{list} """ optFlags = [ [ "debug", "b", "Run tests in a debugger. If that debugger is " "pdb, will load '.pdbrc' from current directory if it exists.", ], [ "debug-stacktraces", "B", "Report Deferred creation and " "callback stack traces", ], [ "nopm", None, "don't automatically jump into debugger for " "postmorteming of exceptions", ], ["dry-run", "n", "do everything but run the tests"], ["profile", None, "Run tests under the Python profiler"], ["until-failure", "u", "Repeat test until it fails"], ] optParameters = [ [ "debugger", None, "pdb", "the fully qualified name of a debugger to " "use if --debug is passed", ], ["logfile", "l", "test.log", "log file name"], ["jobs", "j", None, "Number of local workers to run"], ] compData = usage.Completions( optActions={ "tbformat": usage.CompleteList(["plain", "emacs", "cgitb"]), "reporter": _reporterAction, }, ) _workerFlags = ["disablegc", "force-gc", "coverage"] _workerParameters = ["recursionlimit", "reactor", "without-module"] fallbackReporter = reporter.TreeReporter extra = None tracer = None def opt_jobs(self, number): """ Number of local workers to run, a strictly positive integer. """ try: number = int(number) except ValueError: raise usage.UsageError( "Expecting integer argument to jobs, got '%s'" % number ) if number <= 0: raise usage.UsageError( "Argument to jobs must be a strictly positive integer" ) self["jobs"] = number def _getWorkerArguments(self): """ Return a list of options to pass to distributed workers. """ args = [] for option in self._workerFlags: if self.get(option) is not None: if self[option]: args.append(f"--{option}") for option in self._workerParameters: if self.get(option) is not None: args.extend([f"--{option}", str(self[option])]) return args def postOptions(self): _BasicOptions.postOptions(self) if self["jobs"]: conflicts = ["debug", "profile", "debug-stacktraces", "exitfirst"] for option in conflicts: if self[option]: raise usage.UsageError( "You can't specify --%s when using --jobs" % option ) if self["nopm"]: if not self["debug"]: raise usage.UsageError("You must specify --debug when using " "--nopm ") failure.DO_POST_MORTEM = False def _initialDebugSetup(config): # do this part of debug setup first for easy debugging of import failures if config["debug"]: failure.startDebugMode() if config["debug"] or config["debug-stacktraces"]: defer.setDebugging(True) def _getSuite(config): loader = _getLoader(config) recurse = not config["no-recurse"] return loader.loadByNames(config["tests"], recurse=recurse) def _getLoader(config): loader = runner.TestLoader() if config["random"]: randomer = random.Random() randomer.seed(config["random"]) loader.sorter = lambda x: randomer.random() print("Running tests shuffled with seed %d\n" % config["random"]) elif config["order"]: _, sorter = _runOrders[config["order"]] loader.sorter = sorter if not config["until-failure"]: loader.suiteFactory = runner.DestructiveTestSuite return loader def _wrappedPdb(): """ Wrap an instance of C{pdb.Pdb} with readline support and load any .rcs. """ dbg = pdb.Pdb() try: namedModule("readline") except ImportError: print("readline module not available") for path in (".pdbrc", "pdbrc"): if os.path.exists(path): try: rcFile = open(path) except OSError: pass else: with rcFile: dbg.rcLines.extend(rcFile.readlines()) return dbg class _DebuggerNotFound(Exception): """ A debugger import failed. Used to allow translating these errors into usage error messages. """ def _makeRunner(config): """ Return a trial runner class set up with the parameters extracted from C{config}. @return: A trial runner instance. @rtype: L{runner.TrialRunner} or C{DistTrialRunner} depending on the configuration. """ cls = runner.TrialRunner args = { "reporterFactory": config["reporter"], "tracebackFormat": config["tbformat"], "realTimeErrors": config["rterrors"], "uncleanWarnings": config["unclean-warnings"], "logfile": config["logfile"], "workingDirectory": config["temp-directory"], } if config["dry-run"]: args["mode"] = runner.TrialRunner.DRY_RUN elif config["jobs"]: from twisted.trial._dist.disttrial import DistTrialRunner cls = DistTrialRunner args["workerNumber"] = config["jobs"] args["workerArguments"] = config._getWorkerArguments() else: if config["debug"]: args["mode"] = runner.TrialRunner.DEBUG debugger = config["debugger"] if debugger != "pdb": try: args["debugger"] = reflect.namedAny(debugger) except reflect.ModuleNotFound: raise _DebuggerNotFound( f"{debugger!r} debugger could not be found." ) else: args["debugger"] = _wrappedPdb() args["exitFirst"] = config["exitfirst"] args["profile"] = config["profile"] args["forceGarbageCollection"] = config["force-gc"] return cls(**args) def run(): if len(sys.argv) == 1: sys.argv.append("--help") config = Options() try: config.parseOptions() except usage.error as ue: raise SystemExit(f"{sys.argv[0]}: {ue}") _initialDebugSetup(config) try: trialRunner = _makeRunner(config) except _DebuggerNotFound as e: raise SystemExit(f"{sys.argv[0]}: {str(e)}") suite = _getSuite(config) if config["until-failure"]: test_result = trialRunner.runUntilFailure(suite) else: test_result = trialRunner.run(suite) if config.tracer: sys.settrace(None) results = config.tracer.results() results.write_results( show_missing=1, summary=False, coverdir=config.coverdir().path ) sys.exit(not test_result.wasSuccessful())