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/parso/python
Viewing File: /opt/imh-python/lib/python3.9/site-packages/parso/python/pep8.py
import re from contextlib import contextmanager from typing import Tuple from parso.python.errors import ErrorFinder, ErrorFinderConfig from parso.normalizer import Rule from parso.python.tree import Flow, Scope _IMPORT_TYPES = ('import_name', 'import_from') _SUITE_INTRODUCERS = ('classdef', 'funcdef', 'if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt') _NON_STAR_TYPES = ('term', 'import_from', 'power') _OPENING_BRACKETS = '(', '[', '{' _CLOSING_BRACKETS = ')', ']', '}' _FACTOR = '+', '-', '~' _ALLOW_SPACE = '*', '+', '-', '**', '/', '//', '@' _BITWISE_OPERATOR = '<<', '>>', '|', '&', '^' _NEEDS_SPACE: Tuple[str, ...] = ( '=', '%', '->', '<', '>', '==', '>=', '<=', '<>', '!=', '+=', '-=', '*=', '@=', '/=', '%=', '&=', '|=', '^=', '<<=', '>>=', '**=', '//=') _NEEDS_SPACE += _BITWISE_OPERATOR _IMPLICIT_INDENTATION_TYPES = ('dictorsetmaker', 'argument') _POSSIBLE_SLICE_PARENTS = ('subscript', 'subscriptlist', 'sliceop') class IndentationTypes: VERTICAL_BRACKET = object() HANGING_BRACKET = object() BACKSLASH = object() SUITE = object() IMPLICIT = object() class IndentationNode(object): type = IndentationTypes.SUITE def __init__(self, config, indentation, parent=None): self.bracket_indentation = self.indentation = indentation self.parent = parent def __repr__(self): return '<%s>' % self.__class__.__name__ def get_latest_suite_node(self): n = self while n is not None: if n.type == IndentationTypes.SUITE: return n n = n.parent class BracketNode(IndentationNode): def __init__(self, config, leaf, parent, in_suite_introducer=False): self.leaf = leaf # Figure out here what the indentation is. For chained brackets # we can basically use the previous indentation. previous_leaf = leaf n = parent if n.type == IndentationTypes.IMPLICIT: n = n.parent while True: if hasattr(n, 'leaf') and previous_leaf.line != n.leaf.line: break previous_leaf = previous_leaf.get_previous_leaf() if not isinstance(n, BracketNode) or previous_leaf != n.leaf: break n = n.parent parent_indentation = n.indentation next_leaf = leaf.get_next_leaf() if '\n' in next_leaf.prefix or '\r' in next_leaf.prefix: # This implies code like: # foobarbaz( # a, # b, # ) self.bracket_indentation = parent_indentation \ + config.closing_bracket_hanging_indentation self.indentation = parent_indentation + config.indentation self.type = IndentationTypes.HANGING_BRACKET else: # Implies code like: # foobarbaz( # a, # b, # ) expected_end_indent = leaf.end_pos[1] if '\t' in config.indentation: self.indentation = None else: self.indentation = ' ' * expected_end_indent self.bracket_indentation = self.indentation self.type = IndentationTypes.VERTICAL_BRACKET if in_suite_introducer and parent.type == IndentationTypes.SUITE \ and self.indentation == parent_indentation + config.indentation: self.indentation += config.indentation # The closing bracket should have the same indentation. self.bracket_indentation = self.indentation self.parent = parent class ImplicitNode(BracketNode): """ Implicit indentation after keyword arguments, default arguments, annotations and dict values. """ def __init__(self, config, leaf, parent): super().__init__(config, leaf, parent) self.type = IndentationTypes.IMPLICIT next_leaf = leaf.get_next_leaf() if leaf == ':' and '\n' not in next_leaf.prefix and '\r' not in next_leaf.prefix: self.indentation += ' ' class BackslashNode(IndentationNode): type = IndentationTypes.BACKSLASH def __init__(self, config, parent_indentation, containing_leaf, spacing, parent=None): expr_stmt = containing_leaf.search_ancestor('expr_stmt') if expr_stmt is not None: equals = expr_stmt.children[-2] if '\t' in config.indentation: # TODO unite with the code of BracketNode self.indentation = None else: # If the backslash follows the equals, use normal indentation # otherwise it should align with the equals. if equals.end_pos == spacing.start_pos: self.indentation = parent_indentation + config.indentation else: # +1 because there is a space. self.indentation = ' ' * (equals.end_pos[1] + 1) else: self.indentation = parent_indentation + config.indentation self.bracket_indentation = self.indentation self.parent = parent def _is_magic_name(name): return name.value.startswith('__') and name.value.endswith('__') class PEP8Normalizer(ErrorFinder): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._previous_part = None self._previous_leaf = None self._on_newline = True self._newline_count = 0 self._wanted_newline_count = None self._max_new_lines_in_prefix = 0 self._new_statement = True self._implicit_indentation_possible = False # The top of stack of the indentation nodes. self._indentation_tos = self._last_indentation_tos = \ IndentationNode(self._config, indentation='') self._in_suite_introducer = False if ' ' in self._config.indentation: self._indentation_type = 'spaces' self._wrong_indentation_char = '\t' else: self._indentation_type = 'tabs' self._wrong_indentation_char = ' ' @contextmanager def visit_node(self, node): with super().visit_node(node): with self._visit_node(node): yield @contextmanager def _visit_node(self, node): typ = node.type if typ in 'import_name': names = node.get_defined_names() if len(names) > 1: for name in names[:1]: self.add_issue(name, 401, 'Multiple imports on one line') elif typ == 'lambdef': expr_stmt = node.parent # Check if it's simply defining a single name, not something like # foo.bar or x[1], where using a lambda could make more sense. if expr_stmt.type == 'expr_stmt' and any(n.type == 'name' for n in expr_stmt.children[:-2:2]): self.add_issue(node, 731, 'Do not assign a lambda expression, use a def') elif typ == 'try_stmt': for child in node.children: # Here we can simply check if it's an except, because otherwise # it would be an except_clause. if child.type == 'keyword' and child.value == 'except': self.add_issue(child, 722, 'Do not use bare except, specify exception instead') elif typ == 'comparison': for child in node.children: if child.type not in ('atom_expr', 'power'): continue if len(child.children) > 2: continue trailer = child.children[1] atom = child.children[0] if trailer.type == 'trailer' and atom.type == 'name' \ and atom.value == 'type': self.add_issue(node, 721, "Do not compare types, use 'isinstance()") break elif typ == 'file_input': endmarker = node.children[-1] prev = endmarker.get_previous_leaf() prefix = endmarker.prefix if (not prefix.endswith('\n') and not prefix.endswith('\r') and ( prefix or prev is None or prev.value not in {'\n', '\r\n', '\r'})): self.add_issue(endmarker, 292, "No newline at end of file") if typ in _IMPORT_TYPES: simple_stmt = node.parent module = simple_stmt.parent if module.type == 'file_input': index = module.children.index(simple_stmt) for child in module.children[:index]: children = [child] if child.type == 'simple_stmt': # Remove the newline. children = child.children[:-1] found_docstring = False for c in children: if c.type == 'string' and not found_docstring: continue found_docstring = True if c.type == 'expr_stmt' and \ all(_is_magic_name(n) for n in c.get_defined_names()): continue if c.type in _IMPORT_TYPES or isinstance(c, Flow): continue self.add_issue(node, 402, 'Module level import not at top of file') break else: continue break implicit_indentation_possible = typ in _IMPLICIT_INDENTATION_TYPES in_introducer = typ in _SUITE_INTRODUCERS if in_introducer: self._in_suite_introducer = True elif typ == 'suite': if self._indentation_tos.type == IndentationTypes.BACKSLASH: self._indentation_tos = self._indentation_tos.parent self._indentation_tos = IndentationNode( self._config, self._indentation_tos.indentation + self._config.indentation, parent=self._indentation_tos ) elif implicit_indentation_possible: self._implicit_indentation_possible = True yield if typ == 'suite': assert self._indentation_tos.type == IndentationTypes.SUITE self._indentation_tos = self._indentation_tos.parent # If we dedent, no lines are needed anymore. self._wanted_newline_count = None elif implicit_indentation_possible: self._implicit_indentation_possible = False if self._indentation_tos.type == IndentationTypes.IMPLICIT: self._indentation_tos = self._indentation_tos.parent elif in_introducer: self._in_suite_introducer = False if typ in ('classdef', 'funcdef'): self._wanted_newline_count = self._get_wanted_blank_lines_count() def _check_tabs_spaces(self, spacing): if self._wrong_indentation_char in spacing.value: self.add_issue(spacing, 101, 'Indentation contains ' + self._indentation_type) return True return False def _get_wanted_blank_lines_count(self): suite_node = self._indentation_tos.get_latest_suite_node() return int(suite_node.parent is None) + 1 def _reset_newlines(self, spacing, leaf, is_comment=False): self._max_new_lines_in_prefix = \ max(self._max_new_lines_in_prefix, self._newline_count) wanted = self._wanted_newline_count if wanted is not None: # Need to substract one blank_lines = self._newline_count - 1 if wanted > blank_lines and leaf.type != 'endmarker': # In case of a comment we don't need to add the issue, yet. if not is_comment: # TODO end_pos wrong. code = 302 if wanted == 2 else 301 message = "expected %s blank line, found %s" \ % (wanted, blank_lines) self.add_issue(spacing, code, message) self._wanted_newline_count = None else: self._wanted_newline_count = None if not is_comment: wanted = self._get_wanted_blank_lines_count() actual = self._max_new_lines_in_prefix - 1 val = leaf.value needs_lines = ( val == '@' and leaf.parent.type == 'decorator' or ( val == 'class' or val == 'async' and leaf.get_next_leaf() == 'def' or val == 'def' and self._previous_leaf != 'async' ) and leaf.parent.parent.type != 'decorated' ) if needs_lines and actual < wanted: func_or_cls = leaf.parent suite = func_or_cls.parent if suite.type == 'decorated': suite = suite.parent # The first leaf of a file or a suite should not need blank # lines. if suite.children[int(suite.type == 'suite')] != func_or_cls: code = 302 if wanted == 2 else 301 message = "expected %s blank line, found %s" \ % (wanted, actual) self.add_issue(spacing, code, message) self._max_new_lines_in_prefix = 0 self._newline_count = 0 def visit_leaf(self, leaf): super().visit_leaf(leaf) for part in leaf._split_prefix(): if part.type == 'spacing': # This part is used for the part call after for. break self._visit_part(part, part.create_spacing_part(), leaf) self._analyse_non_prefix(leaf) self._visit_part(leaf, part, leaf) # Cleanup self._last_indentation_tos = self._indentation_tos self._new_statement = leaf.type == 'newline' # TODO does this work? with brackets and stuff? if leaf.type == 'newline' and \ self._indentation_tos.type == IndentationTypes.BACKSLASH: self._indentation_tos = self._indentation_tos.parent if leaf.value == ':' and leaf.parent.type in _SUITE_INTRODUCERS: self._in_suite_introducer = False elif leaf.value == 'elif': self._in_suite_introducer = True if not self._new_statement: self._reset_newlines(part, leaf) self._max_blank_lines = 0 self._previous_leaf = leaf return leaf.value def _visit_part(self, part, spacing, leaf): value = part.value type_ = part.type if type_ == 'error_leaf': return if value == ',' and part.parent.type == 'dictorsetmaker': self._indentation_tos = self._indentation_tos.parent node = self._indentation_tos if type_ == 'comment': if value.startswith('##'): # Whole blocks of # should not raise an error. if value.lstrip('#'): self.add_issue(part, 266, "Too many leading '#' for block comment.") elif self._on_newline: if not re.match(r'#:? ', value) and not value == '#' \ and not (value.startswith('#!') and part.start_pos == (1, 0)): self.add_issue(part, 265, "Block comment should start with '# '") else: if not re.match(r'#:? [^ ]', value): self.add_issue(part, 262, "Inline comment should start with '# '") self._reset_newlines(spacing, leaf, is_comment=True) elif type_ == 'newline': if self._newline_count > self._get_wanted_blank_lines_count(): self.add_issue(part, 303, "Too many blank lines (%s)" % self._newline_count) elif leaf in ('def', 'class') \ and leaf.parent.parent.type == 'decorated': self.add_issue(part, 304, "Blank lines found after function decorator") self._newline_count += 1 if type_ == 'backslash': # TODO is this enough checking? What about ==? if node.type != IndentationTypes.BACKSLASH: if node.type != IndentationTypes.SUITE: self.add_issue(part, 502, 'The backslash is redundant between brackets') else: indentation = node.indentation if self._in_suite_introducer and node.type == IndentationTypes.SUITE: indentation += self._config.indentation self._indentation_tos = BackslashNode( self._config, indentation, part, spacing, parent=self._indentation_tos ) elif self._on_newline: indentation = spacing.value if node.type == IndentationTypes.BACKSLASH \ and self._previous_part.type == 'newline': self._indentation_tos = self._indentation_tos.parent if not self._check_tabs_spaces(spacing): should_be_indentation = node.indentation if type_ == 'comment': # Comments can be dedented. So we have to care for that. n = self._last_indentation_tos while True: if len(indentation) > len(n.indentation): break should_be_indentation = n.indentation self._last_indentation_tos = n if n == node: break n = n.parent if self._new_statement: if type_ == 'newline': if indentation: self.add_issue(spacing, 291, 'Trailing whitespace') elif indentation != should_be_indentation: s = '%s %s' % (len(self._config.indentation), self._indentation_type) self.add_issue(part, 111, 'Indentation is not a multiple of ' + s) else: if value in '])}': should_be_indentation = node.bracket_indentation else: should_be_indentation = node.indentation if self._in_suite_introducer and indentation == \ node.get_latest_suite_node().indentation \ + self._config.indentation: self.add_issue(part, 129, "Line with same indent as next logical block") elif indentation != should_be_indentation: if not self._check_tabs_spaces(spacing) and part.value not in \ {'\n', '\r\n', '\r'}: if value in '])}': if node.type == IndentationTypes.VERTICAL_BRACKET: self.add_issue( part, 124, "Closing bracket does not match visual indentation" ) else: self.add_issue( part, 123, "Losing bracket does not match " "indentation of opening bracket's line" ) else: if len(indentation) < len(should_be_indentation): if node.type == IndentationTypes.VERTICAL_BRACKET: self.add_issue( part, 128, 'Continuation line under-indented for visual indent' ) elif node.type == IndentationTypes.BACKSLASH: self.add_issue( part, 122, 'Continuation line missing indentation or outdented' ) elif node.type == IndentationTypes.IMPLICIT: self.add_issue(part, 135, 'xxx') else: self.add_issue( part, 121, 'Continuation line under-indented for hanging indent' ) else: if node.type == IndentationTypes.VERTICAL_BRACKET: self.add_issue( part, 127, 'Continuation line over-indented for visual indent' ) elif node.type == IndentationTypes.IMPLICIT: self.add_issue(part, 136, 'xxx') else: self.add_issue( part, 126, 'Continuation line over-indented for hanging indent' ) else: self._check_spacing(part, spacing) self._check_line_length(part, spacing) # ------------------------------- # Finalizing. Updating the state. # ------------------------------- if value and value in '()[]{}' and type_ != 'error_leaf' \ and part.parent.type != 'error_node': if value in _OPENING_BRACKETS: self._indentation_tos = BracketNode( self._config, part, parent=self._indentation_tos, in_suite_introducer=self._in_suite_introducer ) else: assert node.type != IndentationTypes.IMPLICIT self._indentation_tos = self._indentation_tos.parent elif value in ('=', ':') and self._implicit_indentation_possible \ and part.parent.type in _IMPLICIT_INDENTATION_TYPES: indentation = node.indentation self._indentation_tos = ImplicitNode( self._config, part, parent=self._indentation_tos ) self._on_newline = type_ in ('newline', 'backslash', 'bom') self._previous_part = part self._previous_spacing = spacing def _check_line_length(self, part, spacing): if part.type == 'backslash': last_column = part.start_pos[1] + 1 else: last_column = part.end_pos[1] if last_column > self._config.max_characters \ and spacing.start_pos[1] <= self._config.max_characters: # Special case for long URLs in multi-line docstrings or comments, # but still report the error when the 72 first chars are whitespaces. report = True if part.type == 'comment': splitted = part.value[1:].split() if len(splitted) == 1 \ and (part.end_pos[1] - len(splitted[0])) < 72: report = False if report: self.add_issue( part, 501, 'Line too long (%s > %s characters)' % (last_column, self._config.max_characters), ) def _check_spacing(self, part, spacing): def add_if_spaces(*args): if spaces: return self.add_issue(*args) def add_not_spaces(*args): if not spaces: return self.add_issue(*args) spaces = spacing.value prev = self._previous_part if prev is not None and prev.type == 'error_leaf' or part.type == 'error_leaf': return type_ = part.type if '\t' in spaces: self.add_issue(spacing, 223, 'Used tab to separate tokens') elif type_ == 'comment': if len(spaces) < self._config.spaces_before_comment: self.add_issue(spacing, 261, 'At least two spaces before inline comment') elif type_ == 'newline': add_if_spaces(spacing, 291, 'Trailing whitespace') elif len(spaces) > 1: self.add_issue(spacing, 221, 'Multiple spaces used') else: if prev in _OPENING_BRACKETS: message = "Whitespace after '%s'" % part.value add_if_spaces(spacing, 201, message) elif part in _CLOSING_BRACKETS: message = "Whitespace before '%s'" % part.value add_if_spaces(spacing, 202, message) elif part in (',', ';') or part == ':' \ and part.parent.type not in _POSSIBLE_SLICE_PARENTS: message = "Whitespace before '%s'" % part.value add_if_spaces(spacing, 203, message) elif prev == ':' and prev.parent.type in _POSSIBLE_SLICE_PARENTS: pass # TODO elif prev in (',', ';', ':'): add_not_spaces(spacing, 231, "missing whitespace after '%s'") elif part == ':': # Is a subscript # TODO pass elif part in ('*', '**') and part.parent.type not in _NON_STAR_TYPES \ or prev in ('*', '**') \ and prev.parent.type not in _NON_STAR_TYPES: # TODO pass elif prev in _FACTOR and prev.parent.type == 'factor': pass elif prev == '@' and prev.parent.type == 'decorator': pass # TODO should probably raise an error if there's a space here elif part in _NEEDS_SPACE or prev in _NEEDS_SPACE: if part == '=' and part.parent.type in ('argument', 'param') \ or prev == '=' and prev.parent.type in ('argument', 'param'): if part == '=': param = part.parent else: param = prev.parent if param.type == 'param' and param.annotation: add_not_spaces(spacing, 252, 'Expected spaces around annotation equals') else: add_if_spaces( spacing, 251, 'Unexpected spaces around keyword / parameter equals' ) elif part in _BITWISE_OPERATOR or prev in _BITWISE_OPERATOR: add_not_spaces( spacing, 227, 'Missing whitespace around bitwise or shift operator' ) elif part == '%' or prev == '%': add_not_spaces(spacing, 228, 'Missing whitespace around modulo operator') else: message_225 = 'Missing whitespace between tokens' add_not_spaces(spacing, 225, message_225) elif type_ == 'keyword' or prev.type == 'keyword': add_not_spaces(spacing, 275, 'Missing whitespace around keyword') else: prev_spacing = self._previous_spacing if prev in _ALLOW_SPACE and spaces != prev_spacing.value \ and '\n' not in self._previous_leaf.prefix \ and '\r' not in self._previous_leaf.prefix: message = "Whitespace before operator doesn't match with whitespace after" self.add_issue(spacing, 229, message) if spaces and part not in _ALLOW_SPACE and prev not in _ALLOW_SPACE: message_225 = 'Missing whitespace between tokens' # self.add_issue(spacing, 225, message_225) # TODO why only brackets? if part in _OPENING_BRACKETS: message = "Whitespace before '%s'" % part.value add_if_spaces(spacing, 211, message) def _analyse_non_prefix(self, leaf): typ = leaf.type if typ == 'name' and leaf.value in ('l', 'O', 'I'): if leaf.is_definition(): message = "Do not define %s named 'l', 'O', or 'I' one line" if leaf.parent.type == 'class' and leaf.parent.name == leaf: self.add_issue(leaf, 742, message % 'classes') elif leaf.parent.type == 'function' and leaf.parent.name == leaf: self.add_issue(leaf, 743, message % 'function') else: self.add_issuadd_issue(741, message % 'variables', leaf) elif leaf.value == ':': if isinstance(leaf.parent, (Flow, Scope)) and leaf.parent.type != 'lambdef': next_leaf = leaf.get_next_leaf() if next_leaf.type != 'newline': if leaf.parent.type == 'funcdef': self.add_issue(next_leaf, 704, 'Multiple statements on one line (def)') else: self.add_issue(next_leaf, 701, 'Multiple statements on one line (colon)') elif leaf.value == ';': if leaf.get_next_leaf().type in ('newline', 'endmarker'): self.add_issue(leaf, 703, 'Statement ends with a semicolon') else: self.add_issue(leaf, 702, 'Multiple statements on one line (semicolon)') elif leaf.value in ('==', '!='): comparison = leaf.parent index = comparison.children.index(leaf) left = comparison.children[index - 1] right = comparison.children[index + 1] for node in left, right: if node.type == 'keyword' or node.type == 'name': if node.value == 'None': message = "comparison to None should be 'if cond is None:'" self.add_issue(leaf, 711, message) break elif node.value in ('True', 'False'): message = "comparison to False/True should be " \ "'if cond is True:' or 'if cond:'" self.add_issue(leaf, 712, message) break elif leaf.value in ('in', 'is'): comparison = leaf.parent if comparison.type == 'comparison' and comparison.parent.type == 'not_test': if leaf.value == 'in': self.add_issue(leaf, 713, "test for membership should be 'not in'") else: self.add_issue(leaf, 714, "test for object identity should be 'is not'") elif typ == 'string': # Checking multiline strings for i, line in enumerate(leaf.value.splitlines()[1:]): indentation = re.match(r'[ \t]*', line).group(0) start_pos = leaf.line + i, len(indentation) # TODO check multiline indentation. start_pos elif typ == 'endmarker': if self._newline_count >= 2: self.add_issue(leaf, 391, 'Blank line at end of file') def add_issue(self, node, code, message): if self._previous_leaf is not None: if self._previous_leaf.search_ancestor('error_node') is not None: return if self._previous_leaf.type == 'error_leaf': return if node.search_ancestor('error_node') is not None: return if code in (901, 903): # 901 and 903 are raised by the ErrorFinder. super().add_issue(node, code, message) else: # Skip ErrorFinder here, because it has custom behavior. super(ErrorFinder, self).add_issue(node, code, message) class PEP8NormalizerConfig(ErrorFinderConfig): normalizer_class = PEP8Normalizer """ Normalizing to PEP8. Not really implemented, yet. """ def __init__(self, indentation=' ' * 4, hanging_indentation=None, max_characters=79, spaces_before_comment=2): self.indentation = indentation if hanging_indentation is None: hanging_indentation = indentation self.hanging_indentation = hanging_indentation self.closing_bracket_hanging_indentation = '' self.break_after_binary = False self.max_characters = max_characters self.spaces_before_comment = spaces_before_comment # TODO this is not yet ready. # @PEP8Normalizer.register_rule(type='endmarker') class BlankLineAtEnd(Rule): code = 392 message = 'Blank line at end of file' def is_issue(self, leaf): return self._newline_count >= 2