python / cpython

The Python programming language
https://www.python.org
Other
63.14k stars 30.23k forks source link

ast.FormattedValue.format_spec unnecessarily wrapped in JoinedStr #83941

Open 1942af3c-882a-4626-ae71-938da6ad4d99 opened 4 years ago

1942af3c-882a-4626-ae71-938da6ad4d99 commented 4 years ago
BPO 39760
Nosy @ericvsmith, @serhiy-storchaka, @ikamensh, @isidentical

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields: ```python assignee = None closed_at = None created_at = labels = ['type-bug', 'library', '3.9'] title = 'ast.FormattedValue.format_spec unnecessarily wrapped in JoinedStr' updated_at = user = 'https://github.com/ikamensh' ``` bugs.python.org fields: ```python activity = actor = 'BTaskaya' assignee = 'none' closed = False closed_date = None closer = None components = ['Library (Lib)'] creation = creator = 'Ilya Kamenshchikov' dependencies = [] files = [] hgrepos = [] issue_num = 39760 keywords = [] message_count = 4.0 messages = ['362691', '362696', '362699', '362741'] nosy_count = 4.0 nosy_names = ['eric.smith', 'serhiy.storchaka', 'Ilya Kamenshchikov', 'BTaskaya'] pr_nums = [] priority = 'normal' resolution = None stage = None status = 'open' superseder = None type = 'behavior' url = 'https://bugs.python.org/issue39760' versions = ['Python 3.9'] ```

1942af3c-882a-4626-ae71-938da6ad4d99 commented 4 years ago

Most usual usecase for format_spec is to specify it as a constant, that would be logical to represent as ast.Constant. However, ast.parse wraps value of ast.FormattedValue.format_spec into a JoinedStr with a single constant value, as can be seen from example below:

import ast

code = '''f"is {x:d}"'''
tree = ast.parse(code)

for n in ast.walk(tree):
    if isinstance(n, ast.FormattedValue):
        print(
            type(n.format_spec),
            len(n.format_spec.values),
            set(type(v) for v in n.format_spec.values),
        )

This is confusing for programmatically analyzing the ast, and likely creates some overhead in any modules using ast and FormattedValue.

Proposal: represent ast.FormattedValue.format_spec as ast.Constant in most cases.

ericvsmith commented 4 years ago

I agree that could probably be simplified, if the format_spec is constant (which it need not be).

>> ast.dump(ast.parse('f"is {x:d}"'))

"Module(body=[Expr(value=JoinedStr(values=[Str(s='is '), FormattedValue(value=Name(id='x', ctx=Load()), conversion=-1, format_spec=JoinedStr(values=[Str(s='d')]))]))])"

But:

ast.dump(ast.parse('f"is {x:{length+1}d}"'))

"Module(body=[Expr(value=JoinedStr(values=[Str(s='is '), FormattedValue(value=Name(id='x', ctx=Load()), conversion=-1, format_spec=JoinedStr(values=[FormattedValue(value=BinOp(left=Name(id='length', ctx=Load()), op=Add(), right=Num(n=1)), conversion=-1, format_spec=None), Str(s='d')]))]))])"

serhiy-storchaka commented 4 years ago

FYI you can use ast CLI:

$ echo 'f"is {x:d}"' | ./python -m ast
Module(
   body=[
      Expr(
         value=JoinedStr(
            values=[
               Constant(value='is ', kind=None),
               FormattedValue(
                  value=Name(id='x', ctx=Load()),
                  conversion=-1,
                  format_spec=JoinedStr(
                     values=[
                        Constant(value='d', kind=None)]))]))],
   type_ignores=[])
ericvsmith commented 4 years ago

FYI you can use ast CLI:

I did not know that. That's awesome, thanks for pointing it out.