From 04510ac2f87ecc726e5197166bf40cd81109204a Mon Sep 17 00:00:00 2001 From: rocky Date: Tue, 12 Apr 2022 16:49:58 -0400 Subject: [PATCH] lambda formatting in f-string In a formatted string using "lambda', we should not add "\n". For example in: f'{(lambda x:x)("8")!r}' Adding a "\n" after "lambda x: x" will give an error message: SyntaxError: f-string expression part cannot include a backslash --- test/bytecode_3.6_run/10_fstring.pyc | Bin 2888 -> 1526 bytes test/bytecode_3.7_run/10_fstring.pyc | Bin 2877 -> 1520 bytes test/bytecode_3.8_run/10_fstring.pyc | Bin 2933 -> 1534 bytes test/simple_source/bug36/10_fstring.py | 89 +++++++++++++------------ uncompyle6/semantics/pysource.py | 11 ++- 5 files changed, 58 insertions(+), 42 deletions(-) diff --git a/test/bytecode_3.6_run/10_fstring.pyc b/test/bytecode_3.6_run/10_fstring.pyc index 2b936f01935674d516be553679fe664e0f30c34b..b54bc4915c6d5d47f46d0f73b50da8cb08d7d842 100644 GIT binary patch delta 977 zcmZ`%%}!H66rOYM-1c_*!%s_FC=^hk784>rq9I6F@CL?+6BE+*%&je@B~u`PF@Dk-HZ-dgV0ZIMxT{=NOLXtSXI}RzfClLMAaQQP-Fm(Agv#N3AAr4L z?F?rse<1WvV0OP|ZD)_+(i`ecM&%?vzy(S%hKpDtJ2fn+APaaOAF4ug8q0lf8BJzV z&d4HWUa;~b17`-#jf*NUuwr2K2A*Gt;G|tU?sQt+@QWKS8K@Yj8mJjS1AASU#$Kt` zE}Wz=UZq=WG`bFj!UEs%2A*Y~_+mOnL<<4~EuFSbI{e0$lh23_^~}-PP}A<8 z&hlde!EdS6PY~RQtyZIZTleTmj=cG+Tv6CA1t7pPM6uGS- z{3sTJJ2l(E8-MGtgC{Mu9VM^3L%@M{JOL@lM+EUv*G7t-?^ru+Jz7335#^o(RvFT^HpJK5APP9en+ literal 2888 zcmb^y-C7&Rb!K+8S}hU~EDIZhJx~%^F~vdz$8mu=wewqtwsHH#-8xZ)&PWz4T6K5j zhyiYjAHe*mT;(Zx(ThI7T(-U84f1i-Gb4og+}<^dX6DTK|C_^VF6Z6)Xzr^Z zOUS>-g>M|;`?%s6ix40_0qRp=J_AdTJ^@?MK7$nQ6r4dC(z`5PN~}Lpa0NqpX21}$ zX)vC~;GzcixxtM=wn-WBZ9gS!q_Lz#y zG=aG@iR&owy^**9JjnSunD@tF!JmL1iQLZQ&KqLf__a8K{iEky@PL;1I+>}SOl z*f^$?d_md;n~1Bs-b)ral<2?TlJ?{i0-G;cTu3bV=`qDh|K=&y%Xj@5_?d8onin5uVq?)XpsMT^H$3HC;Fsm6?~2v{^B9g6-Sw0Bh}J5!1(!NQhK6jPtkp4foRBVtDT6b z69%sJbN6D;vKnR*GNB{44Sq>Sm>+Xid>=MeB-!qECFb3NgNE zQroUemh)7q7S~&?m?0|X#V#JJIS+|B?*w78rRcJ<8eu3Ws?|m-5Y;N*;8C$yls0aj z%y}RJKBDu;n;GkXG6GunV83OV$(nd(GVM9Mjp&4nF*A{)NT!B-%Sl9mP zPoEUm16;9<+abf8)2(kv$%@AC9^u>~iUv_ZU-Wi5AGh{8=w=*tqq?}g-EFSkxxG@Z zHip$$?(8ck5pmK=){x!AO;~gl;kq`ZKYgZg8MAL8kl^?O7c8)MD5Su7K@N#-iDQb- zKRr#F^w>hjGF!^6RwL1gs#RsjB525oNF?2;&3iq5i#HZb&Cc!UoIAK4!@bVt zeXZS?qH=cVU8B9c9)$H;5N{$dVw9}3P@>T_=rWV>lf;q}# z%CHU4Sxv@6bNrWc>p^XA8)}=6kQn14(E@IY-j(U%UHujRe=A1iXCnhXTPaKLOgZQL z2hzVa(l0&ZH)`fwPBe-K!w7lrUhk;*p}hGThR4ssZUB5+@EVWz+exj*MHGe6iO$!F zhnr-*4Vmq2-~-2>)>}vH`KQx+o_U}YyHa=JmYmoM6I2ReefX>{B>(&=nof-7P{mic zhA)kp`wwhG(dO?jSkN!_L{AIpu2hy+-&wo!u5=?2cZ1{}ms2B12g^0X+9}OQR>_=L zD1$SoT$U-(>qHyp<2l0?aIkWZSB_BICB#LGdV3SMZ&-9|guw1!Lbx>V+s|v! zirFj5!U?X%-;ZMvC9Saifv&ouyNW&^`gB8!YojVvf7Wg9`i?nJFc59!h;}^$p;#Cz zR}XzXsFgDusmkuul4mMi+m1D03aV_);d?>YtVUtCjiW+OR5Jx4uFcruc`Z<5k4+a2 z-AR4j0+iR~Qt4_Hd#PIW(;(`ty;|U>AD#i3PJ~d5QK%H^=!#cq9zuHHDR+G@gl-@< z^?#E%z|F={WYYrm=oHPTvUHqYp;?->J;YAZEJG^0j(1n{u~|!VbKDD-n|57~<{cX) hX6OuKC^tzRHqJac>$uFdC+Q?}k(1L!nzsthe*nFil-U3P diff --git a/test/bytecode_3.7_run/10_fstring.pyc b/test/bytecode_3.7_run/10_fstring.pyc index 719730e272a0b27cf22f0111b66c232dfc34be73..4288b0f6dc752b841fdd5080d209aa93ef437736 100644 GIT binary patch delta 885 zcmZ`%%}-N75Z`%kU;Fyn*HT(q`auO0upEedh=veC4DsOA7;!aeT4vh-{UBQ*fH52} z-p#v7nv{!wfmihQ!^x-^_3Jx3e>o_a*Zq>Ob;*kI~xt_N=z@ z!Oxwo0%OPoLlA(Lu+T={0E-D*aA{vKbR@s9dbV&dBpu>mbo(L6FM<3>&k8Gqbui;q3iME4J_H;r+RYOg~jt11Q9bDP@V2W=z1BV74eB`sK2sz5kUqe%+3`qrF z`GWtD+^$8yER_l3&gW%wShlpaa8E=1kCZ}cjNr;!Z|-(q?{r!R8(YMOH$yD=>CDtJ z#5LdR?6%9GL-k$R#O{sj+N~&gpeptDrn&4AFtCPOmB>F$)aTwbtF0PVCy&>T-_Dh7 zm8AH<6gcpLQ+I9tx_06>a;rSWYV=)q11fLimH){#H0u9|r%b|0f&nKubr+WwmRIht zJ}BQ3{HUrHhq$EeYK=y`ui;41&&yOX_#SyOZC=*AU2RF>H9L)p>KwMIcQcS-TWTVB z>6N&7BY5x4)Nj;e2CLomRtFE8awANvyGg)-b~FZY7!P^mkJu(sa$L*UqG7aLT3(nt R78qrSdtsNmVb{vUe*?-Rvg`l= literal 2877 zcmb_d&tDtI6`wb|TCEld2$qG7!Cr_HS+$D=1jliKII)v7twYkdKhmueRp^bd%%Y{c zD@P1)DgFc8PkU4K`QT%6>R-`A5B&q?*!IFXmwq_)%?L0bx3^|tX5PFX-}mOdd879W z1&`5p{iCU`!aQUD)SHvfCE_lPWWr($S-=niKpt?k1nV)h1@t&(Xa;opSTEUjBX4#xve42j%6;Dw$oym1!fL=G>K=3kBG#H%`Z zsm~AcIQarZZk*xNvBhPw__wjRf__*C3OF4M<4iDuuZzNy(Sz5-u!-wx-x4Ea_XgRG z;G4l1mVsq{RWPdTT+b4W{r{~}uBl(+={OF9t z#w8>9mzHej#RXj62VlQq?UK#J#SQN{j~!t8@7Jt7`US(4=R7H;7QVX=RO!2qs9u2^ zOyGOM5pI!*i5~0OeU9%FE>D(>$9s66PBVrd;D@wc5@Sy$(cf@M{z%KkrPFS~>@Ge*Yn>{8B+z z!M3tcG-bZkPDQ*Gh4obUGUq30+-k2U{t@`n@ylD=3%BI-S)J~wN_o5NZx2)go3N@1 zs?-dkN&gDw6wDvNvhI&8r zG!&qMF9N=R3H3CquQenucq&s*8m(5siB<3t-S-OxkAwy9kZ885U{+bpD3T+!TC)|3 zTFqbf<8rwyZJIn;@KA*Qi7c?9wg{~B>qxpgJMH?W*xQrNfRN`&cEa>EZRCd;awDzk z;?=;a&f^hWZRq&)r&pkHk49qCw96^yu=)+FSn&|;V|urkf-o-Wjh>FClh)=Ig_%U1 zxFK$?b=DVd-JGk|nuBW0Zfz?k6-nAk7fIcmoALM}(GBfNe|pu4FmB%@BBkdK>FGoJ z05F5jGq%h0m3RTv{@HQ14*M1b=B_HYR*S_}T&q<;*@+07awHaMCvN-Qu7BNc&X}s5 zdeT{UXgs8mOwzPY$X^!sU{^nU%*@cY|GjpO;7iZMO~}44N1ElG!3q8Do$g-wLwW5Lj-Nb^IwAUN!ms#KFbgUcqP(K>dotvLscx&<2+tQ6i z(h1W$z8pKD^of0~uo%BV%14lsd_H$*5l!z=wJI~ByA>~=)>8{K>OC5XK50w0?C>ML z2YY;%@4_EVv%X9RWn6c*xv&50wo<2Jt1;-*ry5k3#$oYwS}IWChq?qNHaTQM*_pfJ z&+k>H1N&(`o-^~jvgpZH!|x@Dh|^Zo{y^7S!G?mT1IJf1h(=bK#^X+VBXG<@hM{OH zN3X}j>gi*Zt*<4TF!fWm)(E^XTCc@Xr%expenL&}$G&!B zi>LKa4Lvk>ZxBxA>lTu|u8zvqYIJpKwIGY4(b}wsLH7PJk=u%iO6U|SLl_7hy diff --git a/test/bytecode_3.8_run/10_fstring.pyc b/test/bytecode_3.8_run/10_fstring.pyc index fcb22f354d58429d2b134597f62d0dd6f6fb725f..8af17ed7d06bfa4f5d688e27fe87a74b6a5fe341 100644 GIT binary patch delta 937 zcmZ8f%}*0S6rb6hZI^Ag<)gHeegMUa?3?#~`+NJ|@9q3ZUPatZ*L4W4_3xYI#Sd=o zY?%@QgcG2gg24?ifmNwNxXGC?F9=wIT^L=HTM!Z!#$m8~A;B&w`U72yhrxjeM5`vZ z!Q}(O5%xKS*aZ^X|xF<5bER-&(NpLc0p5u8) zJSU}DMLUX~DAWI97UGI}ik21KQ4|#2@~_NszsOdtzJ&w#Ke9rmL^nQ@k}35skP$SB z)@4Em3I7|Lb04AGHqa15ru?MUq_+Rw+E~(*lA(;b`-)cnBp@V{Xq2;3ueA?%T8)F% zb&T5#O$ht+?NAX?n!)y3wPuN`P^Bj7(7ycvds~VFxm{_l>Ga@wP$SscUvdJm(|Bd( zP2Fph!JF(n?!28r3B!o@b*p{H|74f^U3+x)S_xveD##3CWqhu+QJKM%St$I^yvYt^ z(Q4(NjA_lCLetmsPVM=Hxd)3&%MVKvoE>{|KClbQ^vXE>eMOHY-fJRre%g7quFEUd z^csRY^;UITwvL)O20fcVpUejYaqQJqpLOi-8yP zX|FU|a8wtoVPv(}&@hTS5~VRZ7IM%ZG&Pd9Y(t+}9pSQZIbmiQR5L@&3ERvL+eR|> E8>zIyPXGV_ literal 2933 zcmb_dUw0cv6`z@1tyXKvc3s7G9XnaOZe=ybk>xr~gKbihw){yA)D+5+R>*rtisH3a z?5^_d<+$%d9VN`ixG*{51I1lc-e#527VH_<-FQ(e|GJPU@oh4T>@Jx=q~ zQ;Pg+y_9Ej3&s%fiSp()ypH^DG*TWe!Fb*9GBDvee28a`C`@)u?o1Fb!!z&(TK-;J zPF=Kt%RP3Sh3Ti1XC?^DoOxV9kH2Y;tKbGXF9);UFwA))@Fvea96f%G59@W!_Y6LQ zes7`Q2)yl$LBV^SkKu|JaHKR>?2=vz-q~SKXnap&Ui0#>$n)^Et_e#jBdDoq&2=a} zCf+4jhBAJ~VX(6aKF%j_S1Wa@t$%%nW5K(y`k26tBUOv{wC;CWcXJ?z_q!I{(waYL z&DYOqOjy&JzbK8!@<~`fqLloYG{;TC-`H`Uu*jlBy+0w%(a#BNJYn&8V!$_!C|3IR zeXN&fdsFZMx44}re5y;jW{<%)5tqelI^rF;i@OY zLj8H0KWdwM=>G|>a#Q>LsUH<(-^;6${e3O%*Z&9bDQsbee|VQ*jtTmFhFU}A0Qc{1 zk>Zae?MS*O4d6AAZ8Q@ewZfp9a93p9IEfm~dhDK3S6FVLwYPFZ%$~RDyf7*372LhP ziDx2KNzxKt242ITq|1_)PwBdFYVD?JaoCQ0eto-LU%7F8xm2n3E3(wulUBmxq>-$mx!yZr(PiX)6>9J(UL{0_ejAwt z+aO>I1oN0e3arQEfT*MKlwuo5Pmnr2GVoB?o6@dSBHoHBl_Hg9%!8U3iFneEnr^4# zUUO@63z2HTuY^-2uyAbQh^LX%5&nz(Aw5uCkZ4MK<}caLO$@?BdVOCZF9~BdI@B94 zGRc8u5fIPt^X*2ZmxuJUM6_FPQ&msx8rtp%Z94dM-C2Bv})=ZgKj zNb_OXXyRt9T7w4=UkqRmaMl!j9toi)W%xQ6#(K^S_IL9i(VHk2?_bD7^=uzi+{U82 zeKyxz+DVZXmiw{xp=SolaRT#&f&gRmWdyUpFI8}BufYRBbK6h?TnR0&U1>Q2--6|A!^ zvYmD81Nc+$K1yGCRJLD~0w~23i}#dmijmDQL3{}P{_Q)LqYK%(iV$Do=wC=0sri2w zC%T5F{}EWy`p?e6E-Kr1kesM%AnfJx(#pH5H{KI=#N&35+;YX(fKyv_mBDIMMB`JM z5v-g!F_6b@RVs-T@3f-zS4ydr3i%F>cpOPX-Q(f=?2sO^1A4%IrEl=d^h(iORoTqZral+F!oqmJf@wp82R6Eo)kq1~zs% z{6QS^C~1Vv4^^=x-I4TBKlrQyQB{{I|3SOC<5~KHjsb5y;>MH?eQ1Cac>(G^fIt{82TKLtFar^_P?SssZS) zswC5u3Z9!v#Y+SC8@tuOOW!?XGOY-?7lrWI@dsd!%o|_L-Urh houX5WA;u`R*f4YGv}H5f9HpboMoX8@)4VZm{Synnn$rLP diff --git a/test/simple_source/bug36/10_fstring.py b/test/simple_source/bug36/10_fstring.py index d06ec562..ef38dabf 100644 --- a/test/simple_source/bug36/10_fstring.py +++ b/test/simple_source/bug36/10_fstring.py @@ -2,13 +2,17 @@ # String interpolation tests # RUNNABLE! -var1 = 'x' -var2 = 'y' -abc = 'def' -assert (f"interpolate {var1} strings {var2!r} {var2!s} 'py36" == - "interpolate x strings 'y' y 'py36") -assert 'def0' == f'{abc}0' -assert 'defdef' == f'{abc}{abc!s}' +"""This program is self-checking!""" + +var1 = "x" +var2 = "y" +abc = "def" +assert ( + f"interpolate {var1} strings {var2!r} {var2!s} 'py36" + == "interpolate x strings 'y' y 'py36" +) +assert "def0" == f"{abc}0" +assert "defdef" == f"{abc}{abc!s}" # From 3.6 functools.py # Bug was handling format operator strings. @@ -21,51 +25,51 @@ assert y == "functools.1=['2'](2)" # From 3.6 http/client.py # Bug is in handling X -chunk = ['a', 'b', 'c'] -chunk2 = 'd' -chunk = f'{len(chunk):X}' + chunk2 -assert chunk == '3d' +chunk = ["a", "b", "c"] +chunk2 = "d" +chunk = f"{len(chunk):X}" + chunk2 +assert chunk == "3d" -chunk = b'abc' -chunk2 = 'd' -chunk = f'{len(chunk):X}\r\n'.encode('ascii') + chunk \ - + b'\r\n' -assert chunk == b'3\r\nabc\r\n' +chunk = b"abc" +chunk2 = "d" +chunk = f"{len(chunk):X}\r\n".encode("ascii") + chunk + b"\r\n" +assert chunk == b"3\r\nabc\r\n" # From 3.6.8 idlelib/pyshell.py # Bug was handling ''' import os -filename = '.' -source = 'foo' -source = (f"__file__ = r'''{os.path.abspath(filename)}'''\n" - + source + "\ndel __file__") + +filename = "." +source = "foo" +source = f"__file__ = r'''{os.path.abspath(filename)}'''\n" + source + "\ndel __file__" # Note how { and } are *not* escaped here -f = 'one' -name = 'two' -assert(f"{f}{'{{name}}'} {f}{'{name}'}") == 'one{{name}} one{name}' +f = "one" +name = "two" +assert (f"{f}{'{{name}}'} {f}{'{name}'}") == "one{{name}} one{name}" # From 3.7.3 dataclasses.py -log_rounds = 5 -assert "05$" == f'{log_rounds:02d}$' +log_rounds = 5 +assert "05$" == f"{log_rounds:02d}$" def testit(a, b, l): # print(l) return l + # The call below shows the need for BUILD_STRING to count expr arguments. # Also note that we use {{ }} to escape braces in contrast to the example # above. def _repr_fn(fields): - return testit('__repr__', - ('self',), - ['return xx + f"(' + - ', '.join([f"{f}={{self.{f}!r}}" - for f in fields]) + - ')"']) + return testit( + "__repr__", + ("self",), + ['return xx + f"(' + ", ".join([f"{f}={{self.{f}!r}}" for f in fields]) + ')"'], + ) -fields = ['a', 'b', 'c'] + +fields = ["a", "b", "c"] assert _repr_fn(fields) == ['return xx + f"(a={self.a!r}, b={self.b!r}, c={self.c!r})"'] @@ -85,28 +89,31 @@ else: assert False, "f'{lambda x:x}' should be a syntax error" (x, y, width) = ("foo", 2, 10) -assert f'x={x*y:{width}}' == 'x=foofoo ' +assert f"x={x*y:{width}}" == "x=foofoo " # Why the fact that the distinction of docstring versus stmt is a # string expression is important academic, but we will decompile an # equivalent thing. For compatiblity with older Python we'll use "%" # instead of a format string def f(): - f'''Not a docstring''' + f"""Not a docstring""" + + def g(): - '''Not a docstring''' \ - f'' + """Not a docstring""" f"" + assert f.__doc__ is None assert g.__doc__ is None import decimal -width, precision, value = (10, 4, decimal.Decimal('12.34567')) + +width, precision, value = (10, 4, decimal.Decimal("12.34567")) # Make sure we don't have additional f'..' inside the format strings below. -assert f'result: {value:{width}.{precision}}' == 'result: 12.35' -assert f'result: {value:{width:0}.{precision:1}}' == 'result: 12.35' -assert f'{2}\t' == '2\t' +assert f"result: {value:{width}.{precision}}" == "result: 12.35" +assert f"result: {value:{width:0}.{precision:1}}" == "result: 12.35" +assert f"{2}\t" == "2\t" # But below we *do* need the additional f".." assert f'{f"{0}"*3}' == "000" @@ -115,4 +122,4 @@ assert f'{f"{0}"*3}' == "000" # ^ # The former, {{ confuses the format strings so dictionary/set comprehensions # don't work. -assert f'expr={ {x: y for x, y in [(1, 2), ]}}' == 'expr={1: 2}' +assert f"expr={ {x: y for x, y in [(1, 2), ]}}" == "expr={1: 2}" diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index d21cc5f8..9ab6f48a 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -2553,7 +2553,16 @@ class SourceWalker(GenericASTTraversal, object): else: self.customize(customize) self.text = self.traverse(ast, is_lambda=is_lambda) - self.println(self.text) + # In a formatted string using "lambda', we should not add "\n". + # For example in: + # f'{(lambda x:x)("8")!r}' + # Adding a "\n" after "lambda x: x" will give an error message: + # SyntaxError: f-string expression part cannot include a backslash + # So avoid that. + printfn = ( + self.write if self.in_format_string and is_lambda else self.println + ) + printfn(self.text) self.name = old_name self.return_none = rn