mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-138774: use value to ast.unparse code when str is None in ast.Interpolation (#139415)
This commit is contained in:
parent
6be6f8ff59
commit
4d0849426f
4 changed files with 102 additions and 12 deletions
|
|
@ -363,6 +363,11 @@ Literals
|
||||||
function call).
|
function call).
|
||||||
This has the same meaning as ``FormattedValue.value``.
|
This has the same meaning as ``FormattedValue.value``.
|
||||||
* ``str`` is a constant containing the text of the interpolation expression.
|
* ``str`` is a constant containing the text of the interpolation expression.
|
||||||
|
|
||||||
|
If ``str`` is set to ``None``, then ``value`` is used to generate code
|
||||||
|
when calling :func:`ast.unparse`. This no longer guarantees that the
|
||||||
|
generated code is identical to the original and is intended for code
|
||||||
|
generation.
|
||||||
* ``conversion`` is an integer:
|
* ``conversion`` is an integer:
|
||||||
|
|
||||||
* -1: no conversion
|
* -1: no conversion
|
||||||
|
|
|
||||||
|
|
@ -658,9 +658,9 @@ def _unparse_interpolation_value(self, inner):
|
||||||
unparser.set_precedence(_Precedence.TEST.next(), inner)
|
unparser.set_precedence(_Precedence.TEST.next(), inner)
|
||||||
return unparser.visit(inner)
|
return unparser.visit(inner)
|
||||||
|
|
||||||
def _write_interpolation(self, node, is_interpolation=False):
|
def _write_interpolation(self, node, use_str_attr=False):
|
||||||
with self.delimit("{", "}"):
|
with self.delimit("{", "}"):
|
||||||
if is_interpolation:
|
if use_str_attr:
|
||||||
expr = node.str
|
expr = node.str
|
||||||
else:
|
else:
|
||||||
expr = self._unparse_interpolation_value(node.value)
|
expr = self._unparse_interpolation_value(node.value)
|
||||||
|
|
@ -678,7 +678,8 @@ def visit_FormattedValue(self, node):
|
||||||
self._write_interpolation(node)
|
self._write_interpolation(node)
|
||||||
|
|
||||||
def visit_Interpolation(self, node):
|
def visit_Interpolation(self, node):
|
||||||
self._write_interpolation(node, is_interpolation=True)
|
# If `str` is set to `None`, use the `value` to generate the source code.
|
||||||
|
self._write_interpolation(node, use_str_attr=node.str is not None)
|
||||||
|
|
||||||
def visit_Name(self, node):
|
def visit_Name(self, node):
|
||||||
self.write(node.id)
|
self.write(node.id)
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,97 @@ def test_tstrings(self):
|
||||||
self.check_ast_roundtrip("t'foo'")
|
self.check_ast_roundtrip("t'foo'")
|
||||||
self.check_ast_roundtrip("t'foo {bar}'")
|
self.check_ast_roundtrip("t'foo {bar}'")
|
||||||
self.check_ast_roundtrip("t'foo {bar!s:.2f}'")
|
self.check_ast_roundtrip("t'foo {bar!s:.2f}'")
|
||||||
|
self.check_ast_roundtrip("t'{a + b}'")
|
||||||
|
self.check_ast_roundtrip("t'{a + b:x}'")
|
||||||
|
self.check_ast_roundtrip("t'{a + b!s}'")
|
||||||
|
self.check_ast_roundtrip("t'{ {a}}'")
|
||||||
|
self.check_ast_roundtrip("t'{ {a}=}'")
|
||||||
|
self.check_ast_roundtrip("t'{{a}}'")
|
||||||
|
self.check_ast_roundtrip("t''")
|
||||||
|
self.check_ast_roundtrip('t""')
|
||||||
|
self.check_ast_roundtrip("t'{(lambda x: x)}'")
|
||||||
|
self.check_ast_roundtrip("t'{t'{x}'}'")
|
||||||
|
|
||||||
|
def test_tstring_with_nonsensical_str_field(self):
|
||||||
|
# `value` suggests that the original code is `t'{test1}`, but `str` suggests otherwise
|
||||||
|
self.assertEqual(
|
||||||
|
ast.unparse(
|
||||||
|
ast.TemplateStr(
|
||||||
|
values=[
|
||||||
|
ast.Interpolation(
|
||||||
|
value=ast.Name(id="test1", ctx=ast.Load()), str="test2", conversion=-1
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"t'{test2}'",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_tstring_with_none_str_field(self):
|
||||||
|
self.assertEqual(
|
||||||
|
ast.unparse(
|
||||||
|
ast.TemplateStr(
|
||||||
|
[ast.Interpolation(value=ast.Name(id="test1"), str=None, conversion=-1)]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"t'{test1}'",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
ast.unparse(
|
||||||
|
ast.TemplateStr(
|
||||||
|
[
|
||||||
|
ast.Interpolation(
|
||||||
|
value=ast.Lambda(
|
||||||
|
args=ast.arguments(args=[ast.arg(arg="x")]),
|
||||||
|
body=ast.Name(id="x"),
|
||||||
|
),
|
||||||
|
str=None,
|
||||||
|
conversion=-1,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"t'{(lambda x: x)}'",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
ast.unparse(
|
||||||
|
ast.TemplateStr(
|
||||||
|
values=[
|
||||||
|
ast.Interpolation(
|
||||||
|
value=ast.TemplateStr(
|
||||||
|
# `str` field kept here
|
||||||
|
[ast.Interpolation(value=ast.Name(id="x"), str="y", conversion=-1)]
|
||||||
|
),
|
||||||
|
str=None,
|
||||||
|
conversion=-1,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'''t"{t'{y}'}"''',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
ast.unparse(
|
||||||
|
ast.TemplateStr(
|
||||||
|
values=[
|
||||||
|
ast.Interpolation(
|
||||||
|
value=ast.TemplateStr(
|
||||||
|
[ast.Interpolation(value=ast.Name(id="x"), str=None, conversion=-1)]
|
||||||
|
),
|
||||||
|
str=None,
|
||||||
|
conversion=-1,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'''t"{t'{x}'}"''',
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
ast.unparse(ast.TemplateStr(
|
||||||
|
[ast.Interpolation(value=ast.Constant(value="foo"), str=None, conversion=114)]
|
||||||
|
)),
|
||||||
|
'''t"{'foo'!r}"''',
|
||||||
|
)
|
||||||
|
|
||||||
def test_strings(self):
|
def test_strings(self):
|
||||||
self.check_ast_roundtrip("u'foo'")
|
self.check_ast_roundtrip("u'foo'")
|
||||||
|
|
@ -813,15 +904,6 @@ def test_type_params(self):
|
||||||
self.check_ast_roundtrip("def f[T: int = int, **P = int, *Ts = *int]():\n pass")
|
self.check_ast_roundtrip("def f[T: int = int, **P = int, *Ts = *int]():\n pass")
|
||||||
self.check_ast_roundtrip("class C[T: int = int, **P = int, *Ts = *int]():\n pass")
|
self.check_ast_roundtrip("class C[T: int = int, **P = int, *Ts = *int]():\n pass")
|
||||||
|
|
||||||
def test_tstr(self):
|
|
||||||
self.check_ast_roundtrip("t'{a + b}'")
|
|
||||||
self.check_ast_roundtrip("t'{a + b:x}'")
|
|
||||||
self.check_ast_roundtrip("t'{a + b!s}'")
|
|
||||||
self.check_ast_roundtrip("t'{ {a}}'")
|
|
||||||
self.check_ast_roundtrip("t'{ {a}=}'")
|
|
||||||
self.check_ast_roundtrip("t'{{a}}'")
|
|
||||||
self.check_ast_roundtrip("t''")
|
|
||||||
|
|
||||||
|
|
||||||
class ManualASTCreationTestCase(unittest.TestCase):
|
class ManualASTCreationTestCase(unittest.TestCase):
|
||||||
"""Test that AST nodes created without a type_params field unparse correctly."""
|
"""Test that AST nodes created without a type_params field unparse correctly."""
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
:func:`ast.unparse` now generates full source code when handling
|
||||||
|
:class:`ast.Interpolation` nodes that do not have a specified source.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue