Repository navigation
unparse() of manually created or modified Interpolation is difficult #138774
Description
Bug report
Bug description:
Hi, I generated some code and experienced a difficulty generating the ast of template-strings.
ast.unparse uses the str attribute of the Interpolation to unparse it and ignores the value attribute.
from ast import *
tree = TemplateStr(
values=[
Interpolation(value=Name(id="test1", ctx=Load()), str="test2", conversion=-1)
]
)
print(unparse(tree))output (Python 3.14.0rc2+):
t'{test2}'I know that it is important to preserve the original code because the formatting might be important, but this makes it difficult to generate correct AST.
from ast import *
code=Lambda(args=arguments(args=[arg(arg='a')]), body=Name(id='a', ctx=Load()))
tree = TemplateStr(
values=[
Interpolation(value=code, str=unparse(code), conversion=-1)
]
)
compile(unparse(tree),"<string>","exec")output (Python 3.14.0rc2+):
Traceback (most recent call last):
File "/home/frank/projects/pysource-playground/pysource-codegen/example.py", line 12, in <module>
compile(unparse(tree),"<string>","exec")
~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<string>", line 1
t'{lambda a: a}'
^^^^^^^^^
SyntaxError: t-string: lambda expressions are not allowed without parenthesesThe solution here can be to add "()" around all interpolations.
Another problem can be that you use interpolations inside interpolations, which means that you have to process the childs first.
This is my current workaround which fixes these issues for me:
def walk_childs_first(node):
for e in ast.iter_child_nodes(node):
yield from walk_childs_first(e)
yield e
def fix_result(node):
if sys.version_info >= (3, 14):
for n in walk_childs_first(node):
if isinstance(n, ast.Interpolation):
f_str = ast.JoinedStr(
[ast.FormattedValue(value=n.value, conversion=-1, format_spec=None)]
)
f_str_repr = ast.unparse(f_str)
if f_str_repr.startswith(("f'''", 'f"""')):
n.str = ast.unparse(f_str)[5:-4] # strip f"""{...}"""
else:
n.str = ast.unparse(f_str)[3:-2] # strip f"{...}"I'm using the unparsing code for f-strings here to generate the correct source code.
I would like to know if there is something which I missed, or if this problem was just overlooked untill now.
I experienced this problem during my work on pysoruce-codegen and pysource-minimize. This might be the reason why this examples could look like some edge cases, but I think it can also be problematic for other people who just want to change the AST and unparse the code, because unparse would use the old code from str and ignore the changed value.
I have no idea how this could be solved but I think it should at least be mentioned in the docs.
Being able to set Interpolation.str to None could help with the unparse use case (it should unparse Interpolation.value in this case), but I don't know if this could cause other problems.
CPython versions tested on:
3.14
Operating systems tested on:
No response
Activity
iritkatriel commentedon Sep 17, 2025
CC @lysnikolaou @AA-Turner
George-Ogden commentedon Sep 26, 2025
@15r10nk - here's an alternative workaround:
This is modified from the
_ast_unparse.Unparsersource codeSome more permanent alternatives:
is_interpolation=Falseifstr == ""is_interpolation=Falseifstr is Nonetrywithis_interpolation=Trueexceptdo useis_interpolation=False` but reraise the original error if both fail.I'd be happy to implement this in a PR and update docs, etc, if you think that one of these could work @iritkatriel.
15r10nk commentedon Sep 27, 2025
I think this could work, but I don't know if there are other things to consider when making this change.
The user would still need to know to set
node.str=Nonewhen he is changing the value, but it would improve the situation.George-Ogden commentedon Sep 27, 2025
I agree - I think that's the best way as it's least ambiguous. We can put this detail in the docs, but it would only apply when generating the AST, as the
strfield would be filled in when parsing code.gh-138774: use `value` to `ast.unparse` code when `str` is `None` in …
pythongh-138774: use `value` to `ast.unparse` code when `str` is `Non…
[3.14] gh-138774: use `value` to `ast.unparse` code when `str` is `No…
pythongh-138774: use `value` to `ast.unparse` code when `str` is `Non…