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
This commit is contained in:
rocky
2022-04-12 16:49:58 -04:00
parent a1fe069c8c
commit 04510ac2f8
5 changed files with 58 additions and 42 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -2,13 +2,17 @@
# String interpolation tests # String interpolation tests
# RUNNABLE! # RUNNABLE!
var1 = 'x' """This program is self-checking!"""
var2 = 'y'
abc = 'def' var1 = "x"
assert (f"interpolate {var1} strings {var2!r} {var2!s} 'py36" == var2 = "y"
"interpolate x strings 'y' y 'py36") abc = "def"
assert 'def0' == f'{abc}0' assert (
assert 'defdef' == f'{abc}{abc!s}' 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 # From 3.6 functools.py
# Bug was handling format operator strings. # Bug was handling format operator strings.
@@ -21,51 +25,51 @@ assert y == "functools.1=['2'](2)"
# From 3.6 http/client.py # From 3.6 http/client.py
# Bug is in handling X # Bug is in handling X
chunk = ['a', 'b', 'c'] chunk = ["a", "b", "c"]
chunk2 = 'd' chunk2 = "d"
chunk = f'{len(chunk):X}' + chunk2 chunk = f"{len(chunk):X}" + chunk2
assert chunk == '3d' assert chunk == "3d"
chunk = b'abc' chunk = b"abc"
chunk2 = 'd' chunk2 = "d"
chunk = f'{len(chunk):X}\r\n'.encode('ascii') + chunk \ chunk = f"{len(chunk):X}\r\n".encode("ascii") + chunk + b"\r\n"
+ b'\r\n' assert chunk == b"3\r\nabc\r\n"
assert chunk == b'3\r\nabc\r\n'
# From 3.6.8 idlelib/pyshell.py # From 3.6.8 idlelib/pyshell.py
# Bug was handling ''' # Bug was handling '''
import os import os
filename = '.'
source = 'foo' filename = "."
source = (f"__file__ = r'''{os.path.abspath(filename)}'''\n" source = "foo"
+ source + "\ndel __file__") source = f"__file__ = r'''{os.path.abspath(filename)}'''\n" + source + "\ndel __file__"
# Note how { and } are *not* escaped here # Note how { and } are *not* escaped here
f = 'one' f = "one"
name = 'two' name = "two"
assert(f"{f}{'{{name}}'} {f}{'{name}'}") == 'one{{name}} one{name}' assert (f"{f}{'{{name}}'} {f}{'{name}'}") == "one{{name}} one{name}"
# From 3.7.3 dataclasses.py # From 3.7.3 dataclasses.py
log_rounds = 5 log_rounds = 5
assert "05$" == f'{log_rounds:02d}$' assert "05$" == f"{log_rounds:02d}$"
def testit(a, b, l): def testit(a, b, l):
# print(l) # print(l)
return l return l
# The call below shows the need for BUILD_STRING to count expr arguments. # 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 # Also note that we use {{ }} to escape braces in contrast to the example
# above. # above.
def _repr_fn(fields): def _repr_fn(fields):
return testit('__repr__', return testit(
('self',), "__repr__",
['return xx + f"(' + ("self",),
', '.join([f"{f}={{self.{f}!r}}" ['return xx + f"(' + ", ".join([f"{f}={{self.{f}!r}}" for f in fields]) + ')"'],
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})"'] 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" assert False, "f'{lambda x:x}' should be a syntax error"
(x, y, width) = ("foo", 2, 10) (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 # Why the fact that the distinction of docstring versus stmt is a
# string expression is important academic, but we will decompile an # string expression is important academic, but we will decompile an
# equivalent thing. For compatiblity with older Python we'll use "%" # equivalent thing. For compatiblity with older Python we'll use "%"
# instead of a format string # instead of a format string
def f(): def f():
f'''Not a docstring''' f"""Not a docstring"""
def g(): def g():
'''Not a docstring''' \ """Not a docstring""" f""
f''
assert f.__doc__ is None assert f.__doc__ is None
assert g.__doc__ is None assert g.__doc__ is None
import decimal 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. # 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}.{precision}}" == "result: 12.35"
assert f'result: {value:{width:0}.{precision:1}}' == 'result: 12.35' assert f"result: {value:{width:0}.{precision:1}}" == "result: 12.35"
assert f'{2}\t' == '2\t' assert f"{2}\t" == "2\t"
# But below we *do* need the additional f".." # But below we *do* need the additional f".."
assert f'{f"{0}"*3}' == "000" 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 # The former, {{ confuses the format strings so dictionary/set comprehensions
# don't work. # 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}"

View File

@@ -2553,7 +2553,16 @@ class SourceWalker(GenericASTTraversal, object):
else: else:
self.customize(customize) self.customize(customize)
self.text = self.traverse(ast, is_lambda=is_lambda) 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.name = old_name
self.return_none = rn self.return_none = rn