Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error reporting involving f-strings (PEP 498) #73237

Closed
yan12125 mannequin opened this issue Dec 23, 2016 · 7 comments
Closed

Improve error reporting involving f-strings (PEP 498) #73237

yan12125 mannequin opened this issue Dec 23, 2016 · 7 comments
Labels
3.7 (EOL) end of life interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement

Comments

@yan12125
Copy link
Mannequin

yan12125 mannequin commented Dec 23, 2016

BPO 29051
Nosy @ericvsmith, @bitdancer, @PCManticore, @markshannon, @vadmium, @ilevkivskyi, @yan12125, @DimitrisJim, @mbdevpl, @LiraNuna

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:

assignee = None
closed_at = <Date 2018-09-11.22:29:00.183>
created_at = <Date 2016-12-23.12:01:01.793>
labels = ['interpreter-core', 'type-feature', '3.7']
title = 'Improve error reporting involving f-strings (PEP 498)'
updated_at = <Date 2018-09-11.22:29:00.181>
user = 'https://github.com/yan12125'

bugs.python.org fields:

activity = <Date 2018-09-11.22:29:00.181>
actor = 'eric.smith'
assignee = 'none'
closed = True
closed_date = <Date 2018-09-11.22:29:00.183>
closer = 'eric.smith'
components = ['Interpreter Core']
creation = <Date 2016-12-23.12:01:01.793>
creator = 'yan12125'
dependencies = []
files = []
hgrepos = []
issue_num = 29051
keywords = []
message_count = 7.0
messages = ['283874', '283878', '285896', '285898', '289501', '308685', '325083']
nosy_count = 10.0
nosy_names = ['eric.smith', 'r.david.murray', 'Claudiu.Popa', 'Mark.Shannon', 'martin.panter', 'levkivskyi', 'yan12125', 'Jim Fasarakis-Hilliard', 'mbdevpl', 'Liran Nuna']
pr_nums = []
priority = 'normal'
resolution = 'duplicate'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue29051'
versions = ['Python 3.6', 'Python 3.7']

@yan12125
Copy link
Mannequin Author

yan12125 mannequin commented Dec 23, 2016

Here are the two examples I found confusing when playing with f-strings. The first one involves with a NameError:

$ cat test2
f'''
{
FOO
}
'''
$ python3.7m test2
Traceback (most recent call last):
  File "test2", line 5, in <module>
    '''
NameError: name 'FOO' is not defined

It would be better if the error reporter points to the actual line of the error:

$ python3.7m test2
Traceback (most recent call last):
  File "test2", line 3, in <module>
    FOO
NameError: name 'FOO' is not defined

The second one involves a SyntaxError:

$ cat test2 
f'''
{
a b c
}
'''

$ python3.7m test2
  File "<fstring>", line 2
    a b c
      ^
SyntaxError: invalid syntax

It would be better if the line number is relative to the file instead of the expression in f-strings:

$ python3.7m test2
  File "test2", line 3
    a b c
      ^
SyntaxError: invalid syntax

By the way, external checkers like pyflakes also suffers. They rely on ASTs. Nodes in f-strings have their lineno relative to the {...} expression instead of the whole code string. For example:

import ast

code = '''
f'{LOL}'
'''

for node in ast.walk(ast.parse(code, "<stdin>", "exec")):
    if isinstance(node, ast.Name):
        print(node.lineno)

Prints 1 instead of 2.

Another by the way, ruby reports correct line numbers:

$ cat test3 
"
#{
FOO
}
"

$ ruby test3 
test3:3:in `<main>': uninitialized constant FOO (NameError)


$ cat test3 
"
#{
@@
}
"

$ ruby test3
test3:3: `@@' without identifiers is not allowed as a class variable name
test3:3: syntax error, unexpected end-of-input

Added the author and the primary reviewer of bpo-24965.

@yan12125 yan12125 mannequin added 3.7 (EOL) end of life interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement labels Dec 23, 2016
@bitdancer
Copy link
Member

These are not problems with f-strings in particular, they are problems in general with the way python parsing and error reporting happens. The second is presumably (I haven't gotten around to understanding how f-strings work under the hood) an example of error reporting from a separately evaled string.

Improvements in this area are certainly welcome. There is an open issue relevant to your first example, bpo-12458. I'm sure that f-strings complicate the solution at least slightly, but I think there are more fundamental pre-requisites to be addressed first in solving it.

@markshannon
Copy link
Member

This problem is the parsing of f-strings.

The expressions in an f-string are not "eval"ed in the sense of the eval() function. They are evaluated exactly the same as any other Python expression. However the parsing of f-strings does not provide correct line numbers.

This problem also manifests itself in the ast and tokenize modules.

>>> m = ast.parse("""f'''
... {
... FOO
... }
... '''
... """)

>>> m.body[0].value.values[1].value.id
'FOO'
>>> m.body[0].value.values[1].value.lineno
2

That 2 should be a 3, and yet
eval(compile(m, "test2", "exec"))
File "<stdin>", line 1, in <module>
File "test2", line 5, in <module>
NameError: name 'FOO' is not defined

gives line 5 for the error, so not only are the line numbers wrong they are inconsistent.

The problem is that the internals of the f-string are not tokenized and parsed using the normal mechanism, but in an ad-hoc fashion in Python-ast.c as demonstrated when tokenizing the source

$ python3.6 -m tokenize test2
0,0-0,0:            ENCODING       'utf-8'        
1,0-5,3:            STRING         "f'''\n{\nFOO\n}\n'''"
5,3-5,4:            NEWLINE        '\n'           
6,0-6,0:            ENDMARKER      ''

The f-string could should be tokenized as something like:
FSTRING_START f'''
STRING_PART \n
LEFT_BRACE {
NEWLINE
IDENTIFIER FOO
NEWLINE
RIGHT_BRACE }
STRING_PART \n
FSTRING_END '''

Although this would complicate the tokenizer, it would mean that the internals of f-strings could be made explicit in the grammar, and that the compiler could generate correct offsets.

@markshannon
Copy link
Member

It is also worth mentioning that incorrect line numbers means that tools like pyflakes, pylint, mypy, lgtm, etc, need to reimplement parsing of f-strings.

@PCManticore
Copy link
Mannequin

PCManticore mannequin commented Mar 12, 2017

I'm adding another example here, where the lineno reporting is wrong:

  from ast import parse 
  n = parse('''

  def test():
     return f"{a}"
  ''')
  f = n.body[0].body[0].value.values[0]
  n = f.value
  print("name lineno", n.lineno)

In this example, the line number of the f-string inner variable is 1, while it should be 3.
As Mark Shannon said, this bug is affecting tools such as pyflakes and pylint.

@ericvsmith
Copy link
Member

The example above (msg289501) has been fixed. I think it was in bpo-30465.

It also fixes some of these other cases, but I haven't reviewed them all.

@ericvsmith
Copy link
Member

Because this issue describes two different problems, I'm going to close it.

The part of it that involves errors during the evaluation of a syntactically valid expression was at least partially fixed in bpo-30465. I will probably re-work how this fix was implemented.

I'm working on the syntax error part of it in bpo-34364.

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.7 (EOL) end of life interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

3 participants