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/gsutil/third_party/argcomplete/test
Viewing File: /opt/gsutil/third_party/argcomplete/test/test.py
#!/usr/bin/env python import argparse import contextlib import os import os.path import re import shutil import subprocess import sys import unittest import unittest.util from io import StringIO from tempfile import NamedTemporaryFile, TemporaryFile, mkdtemp import pexpect from pexpect.replwrap import PEXPECT_CONTINUATION_PROMPT, PEXPECT_PROMPT, REPLWrapper TEST_DIR = os.path.abspath(os.path.dirname(__file__)) BASE_DIR = os.path.dirname(TEST_DIR) sys.path.insert(0, BASE_DIR) from argparse import SUPPRESS, ArgumentParser # noqa: E402 import argcomplete # noqa: E402 import argcomplete.io # noqa: E402 from argcomplete import ( # noqa: E402 CompletionFinder, ExclusiveCompletionFinder, _check_module, autocomplete, shellcode, warn, ) from argcomplete.completers import DirectoriesCompleter, FilesCompleter, SuppressCompleter # noqa: E402 from argcomplete.lexers import split_line # noqa: E402 # Default max length is insufficient for troubleshooting. unittest.util._MAX_LENGTH = 1000 IFS = "\013" COMP_WORDBREAKS = " \t\n\"'><=;|&(:" BASH_VERSION = subprocess.check_output(["bash", "-c", "echo $BASH_VERSION"]).decode() BASH_MAJOR_VERSION = int(BASH_VERSION.split(".")[0]) class ArgcompleteREPLWrapper(REPLWrapper): def run_command(self, command, **kwargs): if "\n" in command: raise Exception("newlines not supported in REPL input") res = super().run_command(command, **kwargs) if self.child.command.split("/")[-1] == "zsh": if "\n" not in res: raise Exception("Expected to see a newline in command response") echo_cmd, actual_res = res.split("\n", 1) res_without_ansi_seqs = re.sub(r"\x1b\[0m.+\x1b\[J", "", actual_res) # Unsure why some environments produce trailing null characters, # but they break tests and trimming them seems to be harmless. # https://github.com/kislyuk/argcomplete/issues/447 res_without_null_chars = res_without_ansi_seqs.rstrip("\x00") return res_without_null_chars else: return res def _repl_sh(command, args, non_printable_insert): os.environ["PS1"] = "$" os.environ["TERM"] = "" child = pexpect.spawn(command, args, echo=False, encoding="utf-8") ps1 = PEXPECT_PROMPT[:5] + non_printable_insert + PEXPECT_PROMPT[5:] ps2 = PEXPECT_CONTINUATION_PROMPT[:5] + non_printable_insert + PEXPECT_CONTINUATION_PROMPT[5:] prompt_change = f"PS1='{ps1}' PS2='{ps2}' PROMPT_COMMAND=''" return ArgcompleteREPLWrapper(child, "\\$", prompt_change, extra_init_cmd="export PAGER=cat") def bash_repl(command="bash"): bashrc = os.path.join(os.path.dirname(pexpect.__file__), "bashrc.sh") sh = _repl_sh(command, ["--rcfile", bashrc], non_printable_insert="\\[\\]") return sh def zsh_repl(command="zsh"): sh = _repl_sh(command, ["--no-rcs", "--no-globalrcs", "-V"], non_printable_insert="%(!..)") # Require two tabs to print all options (some tests rely on this). sh.run_command("setopt BASH_AUTO_LIST") return sh def setUpModule(): os.environ["INPUTRC"] = os.path.join(os.path.dirname(__file__), "inputrc") class TempDir: """ Temporary directory for testing FilesCompletion Usage: with TempDir(prefix="temp_fc") as t: print("tempdir", t) # you are not chdir-ed to the temporary directory # everything created here will be deleted """ def __init__(self, suffix="", prefix="tmpdir", dir=None): self.tmp_dir = mkdtemp(suffix=suffix, prefix=prefix, dir=dir) self.old_dir = os.getcwd() def __enter__(self): os.chdir(self.tmp_dir) return self.tmp_dir def __exit__(self, *err): os.chdir(self.old_dir) shutil.rmtree(self.tmp_dir) class TestArgcomplete(unittest.TestCase): def setUp(self): self._os_environ = os.environ os.environ = os.environ.copy() os.environ["_ARGCOMPLETE"] = "1" os.environ["_ARC_DEBUG"] = "yes" os.environ["IFS"] = IFS os.environ["_ARGCOMPLETE_COMP_WORDBREAKS"] = COMP_WORDBREAKS os.environ["_ARGCOMPLETE_SHELL"] = "bash" def tearDown(self): os.environ = self._os_environ def run_completer(self, parser, command, point=None, completer=autocomplete, shell="bash", **kwargs): if point is None: point = str(len(command)) with TemporaryFile(mode="w+") as t: os.environ["COMP_LINE"] = command os.environ["COMP_POINT"] = point os.environ["_ARGCOMPLETE_SHELL"] = shell with self.assertRaises(SystemExit) as cm: completer(parser, output_stream=t, exit_method=sys.exit, **kwargs) if cm.exception.code != 0: raise Exception("Unexpected exit code %d" % cm.exception.code) t.seek(0) return t.read().split(IFS) def test_basic_completion(self): p = ArgumentParser() p.add_argument("--foo") p.add_argument("--bar") completions = self.run_completer(p, "prog ") self.assertEqual(set(completions), set(["-h", "--help", "--foo", "--bar"])) completions = self.run_completer(p, "prog -") self.assertEqual(set(completions), set(["-h", "--help", "--foo", "--bar"])) completions = self.run_completer(p, "prog ", always_complete_options=False) self.assertEqual(set(completions), set([""])) completions = self.run_completer(p, "prog -", always_complete_options=False) self.assertEqual(set(completions), set(["-h", "--help", "--foo", "--bar"])) def test_choices(self): def make_parser(): parser = ArgumentParser() parser.add_argument("--ship", choices=["submarine", "speedboat"]) return parser expected_outputs = ( ("prog ", ["--ship", "-h", "--help"]), ("prog --shi", ["--ship "]), ("prog --ship ", ["submarine", "speedboat"]), ("prog --ship s", ["submarine", "speedboat"]), ("prog --ship su", ["submarine "]), ("prog --ship=", ["submarine", "speedboat"]), ("prog --ship=s", ["submarine", "speedboat"]), ("prog --ship=su", ["submarine "]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_non_str_choices(self): def make_parser(): parser = ArgumentParser() parser.add_argument("x", type=int, choices=[4, 8, 15, 16, 23, 42]) return parser expected_outputs = ( ("prog ", ["4", "8", "15", "16", "23", "42", "-h", "--help"]), ("prog 1", ["15", "16"]), ("prog 2", ["23 "]), ("prog 4", ["4", "42"]), ("prog 4 ", ["-h", "--help"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_suppress_args(self): def make_parser(): parser = ArgumentParser() parser.add_argument("--foo") parser.add_argument("--bar", help=SUPPRESS) return parser expected_outputs = (("prog ", ["--foo", "-h", "--help"]), ("prog --b", [""])) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) expected_outputs = (("prog ", ["--foo", "--bar", "-h", "--help"]), ("prog --b", ["--bar "])) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd, print_suppressed=True)), set(output)) def test_suppress_completer(self): def make_parser(): parser = ArgumentParser() parser.add_argument("--foo") arg = parser.add_argument("--bar") arg.completer = SuppressCompleter() return parser expected_outputs = (("prog ", ["--foo", "-h", "--help"]), ("prog --b", [""])) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) expected_outputs = (("prog ", ["--foo", "--bar", "-h", "--help"]), ("prog --b", ["--bar "])) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd, print_suppressed=True)), set(output)) def test_action_activation(self): def make_parser(): parser = ArgumentParser() parser.add_argument("var", choices=["bus", "car"]) parser.add_argument("value", choices=["orange", "apple"]) return parser expected_outputs = ( ("prog ", ["bus", "car", "-h", "--help"]), ("prog bu", ["bus "]), ("prog bus ", ["apple", "orange", "-h", "--help"]), ("prog bus appl", ["apple "]), ("prog bus apple ", ["-h", "--help"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_action_activation_with_subparser(self): def make_parser(): parser = ArgumentParser() parser.add_argument("name", nargs=2, choices=["a", "b", "c"]) subparsers = parser.add_subparsers(title="subcommands", metavar="subcommand") subparser_build = subparsers.add_parser("build") subparser_build.add_argument("var", choices=["bus", "car"]) subparser_build.add_argument("--profile", nargs=1) return parser expected_outputs = ( ("prog ", ["a", "b", "c", "-h", "--help"]), ("prog b", ["b "]), ("prog b ", ["a", "b", "c", "-h", "--help"]), ("prog c b ", ["build", "-h", "--help"]), ("prog c b bu", ["build "]), ("prog c b build ", ["bus", "car", "--profile", "-h", "--help"]), ("prog c b build ca", ["car "]), ("prog c b build car ", ["--profile", "-h", "--help"]), ("prog build car ", ["-h", "--help"]), ("prog a build car ", ["-h", "--help"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_completers(self): self.completions = ["http://url1", "http://url2"] def c_url(prefix, parsed_args, **kwargs): return self.completions def make_parser(): parser = ArgumentParser() parser.add_argument("--url").completer = c_url parser.add_argument("--email", nargs=3, choices=["a@b.c", "a@b.d", "ab@c.d", "bcd@e.f", "bce@f.g"]) return parser expected_outputs = ( ("prog --url ", ["http://url1", "http://url2"]), ('prog --url "', ["http://url1", "http://url2"]), ('prog --url "http://url1" --email ', ["a@b.c", "a@b.d", "ab@c.d", "bcd@e.f", "bce@f.g"]), ('prog --url "http://url1" --email a', ["a@b.c", "a@b.d", "ab@c.d"]), ('prog --url "http://url1" --email "a@', ["a@b.c", "a@b.d"]), ('prog --url "http://url1" --email "a@b.c" "a@b.d" "a@', ["a@b.c", "a@b.d"]), ('prog --url "http://url1" --email "a@b.c" "a@b.d" "ab@c.d" ', ["--url", "--email", "-h", "--help"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) self.completions = {"http://url1": "foo", "http://url2": "bar"} for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) zsh_expected_outputs = ( ("prog --url ", ["http\\://url1:foo", "http\\://url2:bar"]), ('prog --url "', ["http\\://url1:foo", "http\\://url2:bar"]), ('prog --url "http://url1" --email ', ["a@b.c:", "a@b.d:", "ab@c.d:", "bcd@e.f:", "bce@f.g:"]), ('prog --url "http://url1" --email a', ["a@b.c:", "a@b.d:", "ab@c.d:"]), ('prog --url "http://url1" --email "a@', ["a@b.c:", "a@b.d:"]), ('prog --url "http://url1" --email "a@b.c" "a@b.d" "a@', ["a@b.c:", "a@b.d:"]), ( 'prog --url "http://url1" --email "a@b.c" "a@b.d" "ab@c.d" ', ["--url:", "--email:", "-h:show this help message and exit", "--help:show this help message and exit"], ), ) for cmd, output in zsh_expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd, shell="zsh")), set(output)) def test_subparser_completers(self): def c_depends_on_positional_arg1(prefix, parsed_args, **kwargs): return [parsed_args.arg1] def c_depends_on_optional_arg5(prefix, parsed_args, **kwargs): return [parsed_args.arg5] def make_parser(): parser = ArgumentParser() subparsers = parser.add_subparsers() subparser = subparsers.add_parser("subcommand") subparser.add_argument("arg1") subparser.add_argument("arg2").completer = c_depends_on_positional_arg1 subparser.add_argument("arg3").completer = c_depends_on_optional_arg5 subparser.add_argument("--arg4").completer = c_depends_on_optional_arg5 subparser.add_argument("--arg5") return parser expected_outputs = ( ("prog subcommand val1 ", ["val1", "--arg4", "--arg5", "-h", "--help"]), ("prog subcommand val1 val2 --arg5 val5 ", ["val5", "--arg4", "--arg5", "-h", "--help"]), ("prog subcommand val1 val2 --arg5 val6 --arg4 v", ["val6 "]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_file_completion(self): # setup and teardown should probably be in class with TempDir(prefix="test_dir_fc", dir="."): fc = FilesCompleter() os.makedirs(os.path.join("abcdefж", "klm")) self.assertEqual(fc("a"), ["abcdefж/"]) os.makedirs(os.path.join("abcaha", "klm")) with open("abcxyz", "w") as fp: fp.write("test") self.assertEqual(set(fc("a")), set(["abcdefж/", "abcaha/", "abcxyz"])) def test_filescompleter_filetype_integration(self): def make_parser(): parser = ArgumentParser() parser.add_argument("--r", type=argparse.FileType("r")) parser.add_argument("--w", type=argparse.FileType("w")) return parser with TempDir(prefix="test_dir_fc2", dir="."): os.makedirs(os.path.join("abcdefж", "klm")) os.makedirs(os.path.join("abcaha", "klm")) with open("abcxyz", "w") as fh, open("abcdefж/klm/test", "w") as fh2: fh.write("test") fh2.write("test") expected_outputs = ( ("prog subcommand --r ", ["abcxyz", "abcdefж/", "abcaha/"]), ("prog subcommand --w abcdefж/klm/t", ["abcdefж/klm/test "]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_directory_completion(self): completer = DirectoriesCompleter() def c(prefix): return set(completer(prefix)) with TempDir(prefix="test_dir", dir="."): # Create some temporary dirs and files (files must be ignored) os.makedirs(os.path.join("abc", "baz")) os.makedirs(os.path.join("abb", "baz")) os.makedirs(os.path.join("abc", "faz")) os.makedirs(os.path.join("def", "baz")) with open("abc1", "w") as fp1: with open("def1", "w") as fp2: fp1.write("A test") fp2.write("Another test") # Test completions self.assertEqual(c("a"), set(["abb/", "abc/"])) self.assertEqual(c("ab"), set(["abc/", "abb/"])) self.assertEqual(c("abc"), set(["abc/"])) self.assertEqual(c("abc/"), set(["abc/baz/", "abc/faz/"])) self.assertEqual(c("d"), set(["def/"])) self.assertEqual(c("def/"), set(["def/baz/"])) self.assertEqual(c("e"), set([])) self.assertEqual(c("def/k"), set([])) return def test_default_completer(self): def make_parser(): parser = ArgumentParser(add_help=False) parser.add_argument("--one") parser.add_argument("--many", nargs="+") return parser with TempDir(prefix="test_dir_dc", dir="."): os.mkdir("test") expected_outputs = ( ("prog --one ", ["test/"]), ("prog --many ", ["test/"]), ("prog --many test/ ", ["test/", "--one", "--many"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_subparsers(self): def make_parser(): parser = ArgumentParser() parser.add_argument("--age", type=int) sub = parser.add_subparsers() eggs = sub.add_parser("eggs") eggs.add_argument("type", choices=["on a boat", "with a goat", "in the rain", "on a train"]) spam = sub.add_parser("spam") spam.add_argument("type", choices=["ham", "iberico"]) return parser expected_outputs = ( ("prog ", ["--help", "eggs", "-h", "spam", "--age"]), ("prog --age 1 eggs", ["eggs "]), ( "prog --age 2 eggs ", [r"on\ a\ train", r"with\ a\ goat", r"on\ a\ boat", r"in\ the\ rain", "--help", "-h"], ), ("prog eggs ", [r"on\ a\ train", r"with\ a\ goat", r"on\ a\ boat", r"in\ the\ rain", "--help", "-h"]), ('prog eggs "on a', ["on a train", "on a boat"]), ("prog eggs on\\ a", [r"on\ a\ train", r"on\ a\ boat"]), ("prog spam ", ["iberico", "ham", "--help", "-h"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) self.assertEqual(set(self.run_completer(make_parser(), cmd, exclude=["-h"])), set(output) - set(["-h"])) self.assertEqual( set(self.run_completer(make_parser(), cmd, exclude=["-h", "--help"])), set(output) - set(["-h", "--help"]), ) def test_non_ascii(self): def make_parser(): parser = ArgumentParser() parser.add_argument( "--книга", choices=[ "Трудно быть богом", "Парень из преисподней", "Понедельник начинается в субботу", ], ) return parser expected_outputs = ( ("prog ", ["--книга", "-h", "--help"]), ( "prog --книга ", [r"Трудно\ быть\ богом", r"Парень\ из\ преисподней", r"Понедельник\ начинается\ в\ субботу"], ), ("prog --книга П", [r"Парень\ из\ преисподней", r"Понедельник\ начинается\ в\ субботу"]), ("prog --книга Пу", [""]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_custom_validator(self): def make_parser(): parser = ArgumentParser() parser.add_argument("var", choices=["bus", "car"]) parser.add_argument("value", choices=["orange", "apple"]) return parser expected_outputs = ( ("prog ", ["-h", "--help"]), ("prog bu", [""]), ("prog bus ", ["-h", "--help"]), ("prog bus appl", [""]), ("prog bus apple ", ["-h", "--help"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd, validator=lambda x, y: False)), set(output)) def test_different_validators(self): def make_parser(): parser = ArgumentParser() parser.add_argument("var", choices=["bus", "car"]) parser.add_argument("value", choices=["orange", "apple"]) return parser validators = ( lambda x, y: False, lambda x, y: True, lambda x, y: x.startswith(y), ) expected_outputs = ( ("prog ", ["-h", "--help"], validators[0]), ("prog ", ["bus", "car", "-h", "--help"], validators[1]), ("prog bu", ["bus", "car"], validators[1]), ("prog bus ", ["apple", "orange", "-h", "--help"], validators[1]), ("prog bus appl", ["apple "], validators[2]), ("prog bus cappl", [""], validators[2]), ("prog bus pple ", ["-h", "--help"], validators[2]), ) for cmd, output, validator in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd, validator=validator)), set(output)) def test_readline_entry_point(self): def get_readline_completions(completer, text): completions = [] for i in range(9999): completion = completer.rl_complete(text, i) if completion is None: break completions.append(completion) return completions parser = ArgumentParser() parser.add_argument("rover", choices=["sojourner", "spirit", "opportunity", "curiosity"]) parser.add_argument("antenna", choices=["low gain", "high gain"]) completer = CompletionFinder(parser) self.assertEqual( get_readline_completions(completer, ""), ["-h", "--help", "sojourner", "spirit", "opportunity", "curiosity"] ) self.assertEqual(get_readline_completions(completer, "s"), ["sojourner", "spirit"]) self.assertEqual(get_readline_completions(completer, "x"), []) def test_display_completions(self): parser = ArgumentParser() parser.add_argument( "rover", choices=["sojourner", "spirit", "opportunity", "curiosity"], help="help for rover " ) parser.add_argument("antenna", choices=["low gain", "high gain"], help="help for antenna") sub = parser.add_subparsers() p = sub.add_parser("list") p.add_argument("-o", "--oh", help="ttt") p.add_argument("-c", "--ch", help="ccc") sub2 = p.add_subparsers() sub2.add_parser("cat", help="list cat") sub2.add_parser("dog", help="list dog") completer = CompletionFinder(parser) completer.rl_complete("", 0) disp = completer.get_display_completions() self.assertEqual("help for rover ", disp.get("spirit", "")) self.assertEqual("help for rover ", disp.get("sojourner", "")) self.assertEqual("", disp.get("low gain", "")) def test_display_completions_with_aliases(self): parser = ArgumentParser() parser.add_subparsers().add_parser("a", aliases=["b", "c"], help="abc help") # empty completer = CompletionFinder(parser) completer.rl_complete("", 0) disp = completer.get_display_completions() self.assertEqual( { "a": "abc help", "b": "abc help", "c": "abc help", "-h": "show this help message and exit", "--help": "show this help message and exit", }, disp, ) # a completer = CompletionFinder(parser) completer.rl_complete("a", 0) disp = completer.get_display_completions() self.assertEqual({"a": "abc help"}, disp) # b completer = CompletionFinder(parser) completer.rl_complete("b", 0) disp = completer.get_display_completions() self.assertEqual({"b": "abc help"}, disp) # c completer = CompletionFinder(parser) completer.rl_complete("c", 0) disp = completer.get_display_completions() self.assertEqual({"c": "abc help"}, disp) def test_nargs_one_or_more(self): def make_parser(): parser = ArgumentParser() parser.add_argument("h1", choices=["c", "d"]) parser.add_argument("var", choices=["bus", "car"], nargs="+") parser.add_argument("value", choices=["orange", "apple"]) parser.add_argument("end", choices=["end"]) return parser expected_outputs = ( ("prog ", ["c", "d", "-h", "--help"]), ("prog c ", ["bus", "car", "-h", "--help"]), ("prog c bu", ["bus "]), ("prog c bus ", ["bus", "car", "apple", "orange", "-h", "--help"]), ("prog c bus car ", ["bus", "car", "apple", "orange", "-h", "--help"]), ("prog c bus appl", ["apple "]), # No way to know which completers to run past this point. ("prog c bus apple ", ["bus", "car", "apple", "orange", "end", "-h", "--help"]), ("prog c bus car apple ", ["bus", "car", "apple", "orange", "end", "-h", "--help"]), ("prog c bus car apple end ", ["bus", "car", "apple", "orange", "end", "-h", "--help"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_nargs_zero_or_more(self): def make_parser(): parser = ArgumentParser() # default="foo" is necessary to stop argparse trying to validate [] parser.add_argument("foo", choices=["foo"], nargs="*", default="foo") parser.add_argument("bar", choices=["bar"]) return parser expected_outputs = ( ("prog ", ["foo", "bar", "-h", "--help"]), ("prog foo ", ["foo", "bar", "-h", "--help"]), ("prog foo bar ", ["foo", "bar", "-h", "--help"]), ("prog foo foo bar ", ["foo", "bar", "-h", "--help"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_nargs_optional(self): def make_parser(): parser = ArgumentParser() parser.add_argument("foo", choices=["foo"], nargs="?") parser.add_argument("bar", choices=["bar"]) return parser expected_outputs = ( ("prog ", ["foo", "bar", "-h", "--help"]), ("prog foo ", ["foo", "bar", "-h", "--help"]), ("prog foo bar ", ["-h", "--help"]), ("prog bar ", ["foo", "bar", "-h", "--help"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_optional_nargs(self): def make_parser(): parser = ArgumentParser() parser.add_argument("--foo", choices=["foo1", "foo2"], nargs=2) parser.add_argument("--bar", choices=["bar1", "bar2"], nargs="?") parser.add_argument("--baz", choices=["baz1", "baz2"], nargs="*") parser.add_argument("--qux", choices=["qux1", "qux2"], nargs="+") parser.add_argument("--foobar", choices=["pos", "--opt"], nargs=argparse.REMAINDER) return parser options = ["--foo", "--bar", "--baz", "--qux", "--foobar", "-h", "--help"] expected_outputs = ( ("prog ", options), ("prog --foo ", ["foo1", "foo2"]), ("prog --foo foo1 ", ["foo1", "foo2"]), ("prog --foo foo1 foo2 ", options), ("prog --bar ", ["bar1", "bar2"] + options), ("prog --bar bar1 ", options), ("prog --baz ", ["baz1", "baz2"] + options), ("prog --baz baz1 ", ["baz1", "baz2"] + options), ("prog --qux ", ["qux1", "qux2"]), ("prog --qux qux1 ", ["qux1", "qux2"] + options), ("prog --foobar ", ["pos", "--opt"]), ("prog --foobar pos ", ["pos", "--opt"]), ("prog --foobar --", ["--opt "]), ("prog --foobar --opt ", ["pos", "--opt"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_positional_remainder(self): def make_parser(): parser = ArgumentParser() parser.add_argument("--foo", choices=["foo1", "foo2"]) parser.add_argument("remainder", choices=["pos", "--opt"], nargs=argparse.REMAINDER) return parser options = ["--foo", "-h", "--help"] expected_outputs = ( ("prog ", ["pos", "--opt"] + options), ("prog --foo foo1 ", ["pos", "--opt"] + options), ("prog pos ", ["pos", "--opt"]), ("prog -- ", ["pos", "--opt"]), ("prog -- --opt ", ["pos", "--opt"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_skipped_completer(self): parser = ArgumentParser(add_help=False) parser.add_argument("--foo", choices=["--bar"]) self.assertEqual(self.run_completer(parser, "prog --foo --"), ["--foo "]) def test_optional_long_short_filtering(self): def make_parser(): parser = ArgumentParser() parser.add_argument("--foo") parser.add_argument("-b", "--bar") parser.add_argument("--baz", "--xyz") parser.add_argument("-t") parser.add_argument("-z", "--zzz") parser.add_argument("-x") return parser long_opts = "--foo --bar --baz --xyz --zzz --help -x -t".split() short_opts = "-b -t -x -z -h --foo --baz --xyz".split() expected_outputs = ( ("prog ", {"long": long_opts, "short": short_opts, True: long_opts + short_opts, False: [""]}), ("prog --foo", {"long": ["--foo "], "short": ["--foo "], True: ["--foo "], False: ["--foo "]}), ( "prog --b", { "long": ["--bar", "--baz"], "short": ["--bar", "--baz"], True: ["--bar", "--baz"], False: ["--bar", "--baz"], }, ), ("prog -z -x", {"long": ["-x "], "short": ["-x "], True: ["-x "], False: ["-x "]}), ) for cmd, outputs in expected_outputs: for always_complete_options, output in outputs.items(): result = self.run_completer(make_parser(), cmd, always_complete_options=always_complete_options) self.assertEqual(set(result), set(output)) def test_exclusive(self): def make_parser(): parser = ArgumentParser(add_help=False) parser.add_argument("--foo", action="store_true") group = parser.add_mutually_exclusive_group() group.add_argument("--bar", action="store_true") group.add_argument("--no-bar", action="store_true") return parser expected_outputs = ( ("prog ", ["--foo", "--bar", "--no-bar"]), ("prog --foo ", ["--foo", "--bar", "--no-bar"]), ("prog --bar ", ["--foo", "--bar"]), ("prog --foo --no-bar ", ["--foo", "--no-bar"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_mixed_optional_positional(self): def make_parser(): parser = ArgumentParser(add_help=False) parser.add_argument("name", choices=["name1", "name2"]) group = parser.add_mutually_exclusive_group() group.add_argument("--get", action="store_true") group.add_argument("--set", action="store_true") return parser expected_outputs = ( ("prog ", ["--get", "--set", "name1", "name2"]), ("prog --", ["--get", "--set"]), ("prog -- ", ["name1", "name2"]), ("prog --get ", ["--get", "name1", "name2"]), ("prog --get name1 ", ["--get "]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(make_parser(), cmd)), set(output)) def test_append_space(self): def make_parser(): parser = ArgumentParser(add_help=False) parser.add_argument("foo", choices=["bar"]) return parser self.assertEqual(self.run_completer(make_parser(), "prog "), ["bar "]) self.assertEqual(self.run_completer(make_parser(), "prog ", append_space=False), ["bar"]) def test_exclusive_class(self): parser = ArgumentParser(add_help=False) parser.add_argument("--foo", dest="types", action="append_const", const=str) parser.add_argument("--bar", dest="types", action="append", choices=["bar1", "bar2"]) parser.add_argument("--baz", choices=["baz1", "baz2"]) parser.add_argument("--no-bar", action="store_true") completer = ExclusiveCompletionFinder(parser, always_complete_options=True) expected_outputs = ( ("prog ", ["--foo", "--bar", "--baz", "--no-bar"]), ("prog --baz ", ["baz1", "baz2"]), ("prog --baz baz1 ", ["--foo", "--bar", "--no-bar"]), ("prog --foo --no-bar ", ["--foo", "--bar", "--baz"]), ("prog --foo --bar bar1 ", ["--foo", "--bar", "--baz", "--no-bar"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(parser, cmd, completer=completer)), set(output)) def test_escape_special_chars(self): def make_parser(): parser = ArgumentParser(add_help=False) parser.add_argument("-1", choices=["bar<$>baz"]) parser.add_argument("-2", choices=[r"\* "]) parser.add_argument("-3", choices=["\"'"]) return parser self.assertEqual(set(self.run_completer(make_parser(), "prog -1 ")), {r"bar\<\$\>baz "}) self.assertEqual(set(self.run_completer(make_parser(), "prog -2 ")), {r"\\\*\ "}) self.assertEqual(set(self.run_completer(make_parser(), "prog -3 ")), {r"\"\' "}) self.assertEqual(set(self.run_completer(make_parser(), 'prog -3 "')), {r"\"'"}) self.assertEqual(set(self.run_completer(make_parser(), "prog -3 '")), {"\"'\\''"}) self.assertEqual(set(self.run_completer(make_parser(), "prog -1 ", shell="tcsh")), {"bar<$>baz "}) # The trailing space won't actually work correctly in tcsh. self.assertEqual(set(self.run_completer(make_parser(), "prog -2 ", shell="tcsh")), {r"\* "}) self.assertEqual(set(self.run_completer(make_parser(), "prog -3 ", shell="tcsh")), {"\"' "}) self.assertEqual(set(self.run_completer(make_parser(), 'prog -3 "', shell="tcsh")), {"\"'"}) self.assertEqual(set(self.run_completer(make_parser(), "prog -3 '", shell="tcsh")), {"\"'"}) def test_shellcode_utility(self): with NamedTemporaryFile() as fh: sc = shellcode(["prog"], use_defaults=True, shell="bash", complete_arguments=None) fh.write(sc.encode()) fh.flush() subprocess.check_call(["bash", "-n", fh.name]) with NamedTemporaryFile() as fh: sc = shellcode(["prog", "prog2"], use_defaults=False, shell="bash", complete_arguments=["-o", "nospace"]) fh.write(sc.encode()) fh.flush() subprocess.check_call(["bash", "-n", fh.name]) with NamedTemporaryFile() as fh: sc = shellcode( ["prog"], use_defaults=True, shell="bash", complete_arguments=None, argcomplete_script="~/.bash_completion.d/prog.py", ) fh.write(sc.encode()) fh.flush() subprocess.check_call(["bash", "-n", fh.name]) sc = shellcode(["prog"], use_defaults=False, shell="tcsh", complete_arguments=["-o", "nospace"]) sc = shellcode(["prog"], use_defaults=False, shell="woosh", complete_arguments=["-o", "nospace"]) sc = shellcode(["prog"], shell="fish") sc = shellcode(["prog"], shell="fish", argcomplete_script="~/.bash_completion.d/prog.py") def test_option_help(self): os.environ["_ARGCOMPLETE_DFS"] = "\t" os.environ["_ARGCOMPLETE_SUPPRESS_SPACE"] = "1" p = ArgumentParser() p.add_argument("--foo", help="foo" + IFS + "help") p.add_argument("--bar", "--bar2", help="bar help") subparsers = p.add_subparsers() subparsers.add_parser("subcommand", help="subcommand help") subparsers.add_parser("subcommand 2", help="subcommand 2 help") completions = self.run_completer(p, "prog --f", shell="fish") self.assertEqual(set(completions), {"--foo\tfoo help"}) completions = self.run_completer(p, "prog --b", shell="fish") self.assertEqual(set(completions), {"--bar\tbar help", "--bar2\tbar help"}) completions = self.run_completer(p, "prog sub", shell="fish") self.assertEqual(set(completions), {"subcommand\tsubcommand help", "subcommand 2\tsubcommand 2 help"}) os.environ["_ARGCOMPLETE_DFS"] = "invalid" self.assertRaises(Exception, self.run_completer, p, "prog --b", shell="fish") class TestArgcompleteREPL(unittest.TestCase): def setUp(self): pass def tearDown(self): pass def run_completer(self, parser, completer, command, point=None, **kwargs): cword_prequote, cword_prefix, cword_suffix, comp_words, first_colon_pos = split_line(command) completions = completer._get_completions(comp_words, cword_prefix, cword_prequote, first_colon_pos) return completions def test_repl_multiple_complete(self): p = ArgumentParser() p.add_argument("--foo") p.add_argument("--bar") c = CompletionFinder(p, always_complete_options=True) completions = self.run_completer(p, c, "prog ") assert set(completions) == set(["-h", "--help", "--foo", "--bar"]) completions = self.run_completer(p, c, "prog --") assert set(completions) == set(["--help", "--foo", "--bar"]) def test_repl_parse_after_complete(self): p = ArgumentParser() p.add_argument("--foo", required=True) p.add_argument("bar", choices=["bar"]) c = CompletionFinder(p, always_complete_options=True) completions = self.run_completer(p, c, "prog ") assert set(completions) == set(["-h", "--help", "--foo", "bar"]) args = p.parse_args(["--foo", "spam", "bar"]) assert args.foo == "spam" assert args.bar == "bar" # Both options are required - check the parser still enforces this. with self.assertRaises(SystemExit): p.parse_args(["--foo", "spam"]) with self.assertRaises(SystemExit): p.parse_args(["bar"]) def test_repl_subparser_parse_after_complete(self): p = ArgumentParser() sp = p.add_subparsers().add_parser("foo") sp.add_argument("bar", choices=["bar"]) c = CompletionFinder(p, always_complete_options=True) completions = self.run_completer(p, c, "prog foo ") assert set(completions) == set(["-h", "--help", "bar"]) args = p.parse_args(["foo", "bar"]) assert args.bar == "bar" # "bar" is required - check the parser still enforces this. with self.assertRaises(SystemExit): p.parse_args(["foo"]) def test_repl_subcommand(self): p = ArgumentParser() p.add_argument("--foo") p.add_argument("--bar") s = p.add_subparsers() s.add_parser("list") s.add_parser("set") show = s.add_parser("show") def abc(): pass show.add_argument("--test") ss = show.add_subparsers() de = ss.add_parser("depth") de.set_defaults(func=abc) c = CompletionFinder(p, always_complete_options=True) expected_outputs = ( ("prog ", ["-h", "--help", "--foo", "--bar", "list", "show", "set"]), ("prog li", ["list "]), ("prog s", ["show", "set"]), ("prog show ", ["--test", "depth", "-h", "--help"]), ("prog show d", ["depth "]), ("prog show depth ", ["-h", "--help"]), ) for cmd, output in expected_outputs: self.assertEqual(set(self.run_completer(p, c, cmd)), set(output)) def test_repl_reuse_parser_with_positional(self): p = ArgumentParser() p.add_argument("foo", choices=["aa", "bb", "cc"]) p.add_argument("bar", choices=["d", "e"]) c = CompletionFinder(p, always_complete_options=True) self.assertEqual(set(self.run_completer(p, c, "prog ")), set(["-h", "--help", "aa", "bb", "cc"])) self.assertEqual(set(self.run_completer(p, c, "prog aa ")), set(["-h", "--help", "d", "e"])) self.assertEqual(set(self.run_completer(p, c, "prog ")), set(["-h", "--help", "aa", "bb", "cc"])) class TestSplitLine(unittest.TestCase): def setUp(self): self._os_environ = os.environ os.environ = os.environ.copy() os.environ["_ARGCOMPLETE_COMP_WORDBREAKS"] = COMP_WORDBREAKS def tearDown(self): os.environ = self._os_environ def prefix(self, line): return split_line(line)[1] def wordbreak(self, line): return split_line(line)[4] def test_simple(self): self.assertEqual(self.prefix("a b c"), "c") def test_escaped_special(self): self.assertEqual(self.prefix(r"a\$b"), "a$b") self.assertEqual(self.prefix(r"a\`b"), "a`b") def test_unescaped_special(self): self.assertEqual(self.prefix("a$b"), "a$b") self.assertEqual(self.prefix("a`b"), "a`b") @unittest.expectedFailure def test_escaped_special_in_double_quotes(self): self.assertEqual(self.prefix(r'"a\$b'), "a$b") self.assertEqual(self.prefix(r'"a\`b'), "a`b") def test_punctuation(self): self.assertEqual(self.prefix("a,"), "a,") def test_last_wordbreak_pos(self): self.assertEqual(self.wordbreak("a"), None) self.assertEqual(self.wordbreak("a b:c"), 1) self.assertEqual(self.wordbreak("a b:c=d"), 3) self.assertEqual(self.wordbreak("a b:c=d "), None) self.assertEqual(self.wordbreak("a b:c=d e"), None) self.assertEqual(self.wordbreak('"b:c'), None) self.assertEqual(self.wordbreak('"b:c=d'), None) self.assertEqual(self.wordbreak('"b:c=d"'), None) self.assertEqual(self.wordbreak('"b:c=d" '), None) class TestCheckModule(unittest.TestCase): def setUp(self): self.dir = TempDir(prefix="test_dir_module", dir=".") self.dir.__enter__() # There is some odd bug that seems to only come up in Python 3.4 where # using "." in sys.path sometimes won't find modules, so we'll use the # full path each time. sys.path.insert(0, os.getcwd()) def tearDown(self): sys.path.pop(0) self.dir.__exit__() def test_module(self): self._mkfile("module.py") path = _check_module.find("module") self.assertEqual(path, os.path.abspath("module.py")) self.assertNotIn("module", sys.modules) def test_package(self): os.mkdir("package") self._mkfile("package/__init__.py") self._mkfile("package/module.py") path = _check_module.find("package.module") self.assertEqual(path, os.path.abspath("package/module.py")) self.assertNotIn("package", sys.modules) self.assertNotIn("package.module", sys.modules) def test_subpackage(self): os.mkdir("package") self._mkfile("package/__init__.py") os.mkdir("package/subpackage") self._mkfile("package/subpackage/__init__.py") self._mkfile("package/subpackage/module.py") path = _check_module.find("package.subpackage.module") self.assertEqual(path, os.path.abspath("package/subpackage/module.py")) self.assertNotIn("package", sys.modules) self.assertNotIn("package.subpackage", sys.modules) self.assertNotIn("package.subpackage.module", sys.modules) def test_package_main(self): os.mkdir("package") self._mkfile("package/__init__.py") self._mkfile("package/__main__.py") path = _check_module.find("package") self.assertEqual(path, os.path.abspath("package/__main__.py")) self.assertNotIn("package", sys.modules) def test_not_package(self): self._mkfile("module.py") with self.assertRaisesRegex(Exception, "module is not a package"): _check_module.find("module.bad") self.assertNotIn("module", sys.modules) def _mkfile(self, path): open(path, "w").close() class TestShellBase: """ Contains tests which should work in any shell using argcomplete. Tests use the test program in this directory named ``prog``. All commands are expected to input one of the valid choices which is then printed and collected by the shell wrapper. Any tabs in the input line simulate the user pressing tab. For example, ``self.sh.run_command('prog basic "b\tr\t')`` will simulate having the user: 1. Type ``prog basic "b`` 2. Push tab, which returns ``['bar', 'baz']``, filling in ``a`` 3. Type ``r`` 4. Push tab, which returns ``['bar']``, filling in ``" `` 5. Push enter, submitting ``prog basic "bar" `` The end result should be ``bar`` being printed to the screen. """ sh = None expected_failures = [] skipped = [] @classmethod def setUpClass(cls, *args, **kwargs): for name in cls.expected_failures: test = getattr(cls, name) @unittest.expectedFailure def wrapped(self, test=test): test(self) setattr(cls, name, wrapped) for name in cls.skipped: test = getattr(cls, name) @unittest.skip("skip") def wrapped(self, test=test): pass setattr(cls, name, wrapped) super().setUpClass(*args, **kwargs) def setUp(self): raise NotImplementedError def tearDown(self): with self.assertRaises((pexpect.EOF, OSError)): self.sh.run_command("exit") def test_simple_completion(self): self.assertEqual(self.sh.run_command("prog basic f\t"), "foo\r\n") def test_partial_completion(self): self.assertEqual(self.sh.run_command("prog basic b\tr"), "bar\r\n") def test_single_quoted_completion(self): self.assertEqual(self.sh.run_command("prog basic 'f\t"), "foo\r\n") def test_double_quoted_completion(self): self.assertEqual(self.sh.run_command('prog basic "f\t'), "foo\r\n") def test_unquoted_space(self): self.assertEqual(self.sh.run_command("prog space f\t"), "foo bar\r\n") def test_quoted_space(self): self.assertEqual(self.sh.run_command('prog space "f\t'), "foo bar\r\n") def test_continuation(self): # This produces 'prog basic foo --', and '--' is ignored. self.assertEqual(self.sh.run_command("prog basic f\t--"), "foo\r\n") # These do not insert a space, so the '--' is part of the token. self.assertEqual(self.sh.run_command("prog cont f\t--"), "foo=--\r\n") self.assertEqual(self.sh.run_command("prog cont bar\t--"), "bar/--\r\n") self.assertEqual(self.sh.run_command("prog cont baz\t--"), "baz:--\r\n") def test_quoted_exact(self): self.assertEqual(self.sh.run_command('prog basic "f\t--'), "foo\r\n") def test_special_characters(self): self.assertEqual(self.sh.run_command("prog spec d\tf"), "d$e$f\r\n") self.assertEqual(self.sh.run_command("prog spec x\t"), "x!x\r\n") self.assertEqual(self.sh.run_command("prog spec y\t"), "y\\y\r\n") def test_special_characters_single_quoted(self): self.assertEqual(self.sh.run_command("prog spec 'd\tf'"), "d$e$f\r\n") def test_special_characters_double_quoted(self): self.assertEqual(self.sh.run_command('prog spec "d\tf"'), "d$e$f\r\n") def test_parse_special_characters(self): self.assertEqual(self.sh.run_command("prog spec d$e$\tf"), "d$e$f\r\n") self.assertEqual(self.sh.run_command("prog spec d$e\tf"), "d$e$f\r\n") self.assertEqual(self.sh.run_command("prog spec 'd$e\tf\t"), "d$e$f\r\n") def test_parse_special_characters_dollar(self): # First tab expands to 'd\$e\$'; completion works with 'd$' but not 'd\$'. self.assertEqual(self.sh.run_command('prog spec "d$e\tf\t'), "d$e$f\r\n") def test_exclamation_in_double_quotes(self): # Exclamation marks cannot be properly escaped within double quotes. # 'a!b' == "a"\!"b" self.assertEqual(self.sh.run_command('prog spec "x\t'), "x!x\r\n") def test_quotes(self): self.assertEqual(self.sh.run_command("prog quote 1\t"), "1'1\r\n") self.assertEqual(self.sh.run_command("prog quote 2\t"), '2"2\r\n') def test_single_quotes_in_double_quotes(self): self.assertEqual(self.sh.run_command('prog quote "1\t'), "1'1\r\n") def test_single_quotes_in_single_quotes(self): # Single quotes cannot be escaped within single quotes. # "a'b" == 'a'\''b' self.assertEqual(self.sh.run_command("prog quote '1\t"), "1'1\r\n") def test_wordbreak_chars(self): self.assertEqual(self.sh.run_command("prog break a\tc"), "a:b:c\r\n") self.assertEqual(self.sh.run_command("prog break a:b:\tc"), "a:b:c\r\n") self.assertEqual(self.sh.run_command("prog break a:b\tc"), "a:b:c\r\n") self.assertEqual(self.sh.run_command("prog break 'a\tc'"), "a:b:c\r\n") self.assertEqual(self.sh.run_command("prog break 'a:b\tc\t"), "a:b:c\r\n") self.assertEqual(self.sh.run_command('prog break "a\tc"'), "a:b:c\r\n") self.assertEqual(self.sh.run_command('prog break "a:b\tc\t'), "a:b:c\r\n") def test_completion_environment(self): self.assertEqual(self.sh.run_command("prog env o\t"), "ok\r\n") def test_comp_point(self): # Use environment variable to change how prog behaves self.assertEqual(self.sh.run_command("export POINT=1"), "") self.assertEqual(self.sh.run_command("prog point hi\t"), "13\r\n") self.assertEqual(self.sh.run_command("prog point hi \t"), "14\r\n") self.assertEqual(self.sh.run_command("prog point 你好嘚瑟\t"), "15\r\n") self.assertEqual(self.sh.run_command("prog point 你好嘚瑟 \t"), "16\r\n") class TestBashZshBase(TestShellBase): maxDiff = None init_cmd = None # 'dummy' argument unused; checks multi-command registration works # by passing 'prog' as the second argument. install_cmd = 'eval "$(register-python-argcomplete dummy prog)"' def setUp(self): sh = self.repl_provider() output = sh.run_command("echo ready") self.assertEqual(output, "ready\r\n") path = ":".join([os.path.join(BASE_DIR, "scripts"), TEST_DIR, "$PATH"]) sh.run_command("export PATH={0}".format(path)) sh.run_command("export PYTHONPATH={0}".format(BASE_DIR)) if self.init_cmd is not None: output = sh.run_command(self.init_cmd) self.assertEqual(output, "") if self.install_cmd is not None: output = sh.run_command(self.install_cmd) self.assertEqual(output, "") # Register a dummy completion with an external argcomplete script # to ensure this doesn't overwrite our previous registration. output = sh.run_command('eval "$(register-python-argcomplete dummy --external-argcomplete-script dummy)"') self.assertEqual(output, "") self.sh = sh class TestBash(TestBashZshBase, unittest.TestCase): expected_failures = [ "test_parse_special_characters_dollar", "test_exclamation_in_double_quotes", ] if BASH_MAJOR_VERSION < 4: # This requires compopt which is not available in 3.x. expected_failures.append("test_quoted_exact") def repl_provider(self): return bash_repl() def test_one_space_after_exact(self): """Test exactly one space is appended after an exact match.""" # Actual command run is 'echo "prog basic foo "'. result = self.sh.run_command('prog basic f\t"\1echo "') self.assertEqual(result, "prog basic foo \r\n") def test_debug_output(self): self.assertEqual(self.sh.run_command("prog debug f\t"), "foo\r\n") self.sh.run_command("export _ARC_DEBUG=1") output = self.sh.run_command("prog debug f\t") self.assertIn("PYTHON_ARGCOMPLETE_STDOUT\r\n", output) self.assertIn("PYTHON_ARGCOMPLETE_STDERR\r\n", output) self.assertTrue(output.endswith("foo\r\n")) def test_temp_file(self): self.sh.run_command("export ARGCOMPLETE_USE_TEMPFILES=1") self.assertEqual(self.sh.run_command("prog basic f\t"), "foo\r\n") # Confirm we used a temp file by searching for the debug message. self.sh.run_command("export _ARC_DEBUG=1") output = self.sh.run_command("prog basic f\t") self.assertIn("Using output file ", output) def test_nounset(self): self.sh.run_command("set -o nounset") self.test_simple_completion() class TestZsh(TestBashZshBase, unittest.TestCase): init_cmd = "autoload compinit; compinit -u" skipped = [ "test_parse_special_characters", "test_parse_special_characters_dollar", "test_comp_point", # FIXME "test_completion_environment", # FIXME ] def repl_provider(self): return zsh_repl() class TestBashZshGlobalBase(TestBashZshBase): install_cmd = 'eval "$(activate-global-python-argcomplete --dest=-)"' def test_redirection_completion(self): with TempDir(prefix="test_dir_py", dir="."): self.sh.run_command("cd " + os.getcwd()) self.sh.run_command("echo failure > ./foo.txt") self.sh.run_command("echo success > ./foo.\t") with open("foo.txt") as f: msg = f.read() self.assertEqual(msg, "success\n") def test_python_completion(self): self.sh.run_command("cd " + TEST_DIR) self.assertEqual(self.sh.run_command("python3 ./prog basic f\t"), "foo\r\n") def test_python_filename_completion(self): self.sh.run_command("cd " + TEST_DIR) self.assertEqual(self.sh.run_command("python3 ./pro\tbasic f\t"), "foo\r\n") def test_python_stuck(self): self.sh.run_command("cd " + TEST_DIR) self.sh.run_command("python3 ./stuck no\t-input") def test_python_not_executable(self): """Test completing a script that cannot be run directly.""" prog = os.path.join(TEST_DIR, "prog") with TempDir(prefix="test_dir_py", dir="."): shutil.copy(prog, ".") self.sh.run_command("cd " + os.getcwd()) self.sh.run_command("chmod -x ./prog") # Ensure prog is no longer able to be run as "./prog". self.assertIn("<<126>>", self.sh.run_command('./prog; echo "<<$?>>"')) # Ensure completion still functions when run via python. self.assertEqual(self.sh.run_command("python3 ./prog basic f\t"), "foo\r\n") def test_python_module(self): """Test completing a module run with python -m.""" prog = os.path.join(TEST_DIR, "prog") with TempDir(prefix="test_dir_py", dir="."): os.mkdir("package") open("package/__init__.py", "w").close() shutil.copy(prog, "package/prog.py") self.sh.run_command("cd " + os.getcwd()) self.assertEqual(self.sh.run_command("python3 -m package.prog basic f\t"), "foo\r\n") def _test_console_script(self, package=False, wheel=False): with TempDir(prefix="test_dir_py", dir="."): self.sh.run_command("cd " + os.getcwd()) self.sh.run_command("export PATH=$PATH:./bin") self.sh.run_command("export PYTHONPATH=.:$PYTHONPATH") test_package = os.path.join(TEST_DIR, "test_package") command = "pip install {} --target .".format(test_package) if not wheel: command += " --no-binary :all:" install_output = self.sh.run_command(command) self.assertEqual(self.sh.run_command("echo $?"), "0\r\n", install_output) command = "test-module" if package: command = "test-package" command += " a\t" self.assertEqual(self.sh.run_command(command), "arg\r\n") def test_console_script_module(self): """Test completing a console_script for a module.""" self._test_console_script() def test_console_script_package(self): """Test completing a console_script for a package.""" self._test_console_script(package=True) def test_console_script_module_wheel(self): """Test completing a console_script for a module from a wheel.""" self._test_console_script(wheel=True) def test_console_script_package_wheel(self): """Test completing a console_script for a package from a wheel.""" self._test_console_script(package=True, wheel=True) @unittest.skipIf(BASH_MAJOR_VERSION < 4, "complete -D not supported") class TestBashGlobal(TestBash, TestBashZshGlobalBase): pass class TestZshGlobalExplicit(TestZsh, TestBashZshGlobalBase): pass class TestZshGlobalImplicit(TestZsh, TestBashZshGlobalBase): # In zsh, the file is typically not sourced directly; # it is added to fpath and autoloaded by the completion system. zsh_fpath = os.path.join(os.path.abspath(os.path.dirname(argcomplete.__file__)), "bash_completion.d") init_cmd = f'fpath=( {zsh_fpath} "${{fpath[@]}}" ); autoload compinit; compinit -u' install_cmd = None class Shell: def __init__(self, shell): self.child = pexpect.spawn(shell, encoding="utf-8") def run_command(self, command): try: self.child.sendline(r"echo -n \#\#\#; {0}; echo -n \#\#\#".format(command)) self.child.expect_exact("###", timeout=5) self.child.expect_exact("###", timeout=5) return self.child.before finally: # Send Ctrl+C in case we get stuck. self.child.sendline("\x03") class Warn(unittest.TestCase): def test_warn(self): @contextlib.contextmanager def redirect_debug_stream(stream): debug_stream = argcomplete.io.debug_stream argcomplete.io.debug_stream = stream try: yield finally: argcomplete.io.debug_stream = debug_stream test_stream = StringIO() with redirect_debug_stream(test_stream): warn("My hands are tied") self.assertEqual("\nMy hands are tied\n", test_stream.getvalue()) if __name__ == "__main__": unittest.main()