Skip to content

Commit 4e04e6a

Browse files
committed
Small fixes
1 parent 4f4f326 commit 4e04e6a

File tree

6 files changed

+564
-474
lines changed

6 files changed

+564
-474
lines changed

Grammar/python.gram

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ _PyPegen_parse(Parser *p)
8787
# ==============
8888

8989
file[mod_ty]: a=[statements] ENDMARKER { _PyPegen_make_module(p, a) }
90-
interactive[mod_ty]: a=statement_newline { _PyAST_Interactive(_PyPegen_register_stmts(p, a), p->arena) }
90+
interactive[mod_ty]: a=statement_newline { _PyAST_Interactive(a, p->arena) }
9191
eval[mod_ty]: a=expressions NEWLINE* ENDMARKER { _PyAST_Expression(a, p->arena) }
9292
func_type[mod_ty]: '(' a=[type_expressions] ')' '->' b=expression NEWLINE* ENDMARKER { _PyAST_FunctionType(a, b, p->arena) }
9393

@@ -100,8 +100,12 @@ statement[asdl_stmt_seq*]:
100100
| a=compound_stmt { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, a) }
101101
| a[asdl_stmt_seq*]=simple_stmts { a }
102102

103+
single_compound_stmt[asdl_stmt_seq*]:
104+
| a=compound_stmt {
105+
_PyPegen_register_stmts(p, (asdl_stmt_seq*)_PyPegen_singleton_seq(p, a)) }
106+
103107
statement_newline[asdl_stmt_seq*]:
104-
| a=compound_stmt NEWLINE { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, a) }
108+
| a=single_compound_stmt NEWLINE { a }
105109
| simple_stmts
106110
| NEWLINE { (asdl_stmt_seq*)_PyPegen_singleton_seq(p, CHECK(stmt_ty, _PyAST_Pass(EXTRA))) }
107111
| ENDMARKER { _PyPegen_interactive_exit(p) }

Lib/test/test_syntax.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1720,26 +1720,66 @@
17201720
Traceback (most recent call last):
17211721
SyntaxError: invalid syntax. Did you mean 'for'?
17221722
1723+
>>> for a in b:
1724+
... pass
1725+
... elso:
1726+
... pass
1727+
Traceback (most recent call last):
1728+
SyntaxError: invalid syntax. Did you mean 'else'?
1729+
17231730
>>> whille True:
17241731
... pass
17251732
Traceback (most recent call last):
17261733
SyntaxError: invalid syntax. Did you mean 'while'?
17271734
1735+
>>> while True:
1736+
... pass
1737+
... elso:
1738+
... pass
1739+
Traceback (most recent call last):
1740+
SyntaxError: invalid syntax. Did you mean 'else'?
1741+
17281742
>>> iff x > 5:
17291743
... pass
17301744
Traceback (most recent call last):
17311745
SyntaxError: invalid syntax. Did you mean 'if'?
17321746
1747+
>>> if x:
1748+
... pass
1749+
... elseif y:
1750+
... pass
1751+
Traceback (most recent call last):
1752+
SyntaxError: invalid syntax. Did you mean 'elif'?
1753+
1754+
>>> if x:
1755+
... pass
1756+
... elif y:
1757+
... pass
1758+
... elso:
1759+
... pass
1760+
Traceback (most recent call last):
1761+
SyntaxError: invalid syntax. Did you mean 'else'?
1762+
1763+
>>> tyo:
1764+
... pass
1765+
... except y:
1766+
... pass
1767+
Traceback (most recent call last):
1768+
SyntaxError: invalid syntax. Did you mean 'try'?
1769+
17331770
>>> classe MyClass:
17341771
... pass
17351772
Traceback (most recent call last):
17361773
SyntaxError: invalid syntax. Did you mean 'class'?
17371774
1738-
17391775
>>> impor math
17401776
Traceback (most recent call last):
17411777
SyntaxError: invalid syntax. Did you mean 'import'?
17421778
1779+
>>> form x import y
1780+
Traceback (most recent call last):
1781+
SyntaxError: invalid syntax. Did you mean 'from'?
1782+
17431783
>>> defn calculate_sum(a, b):
17441784
... return a + b
17451785
Traceback (most recent call last):

Lib/traceback.py

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,7 +1302,7 @@ def _find_keyword_typos(self):
13021302
if self.filename:
13031303
try:
13041304
with open(self.filename) as f:
1305-
lines = f.readlines()
1305+
lines = f.read().splitlines()
13061306
except Exception:
13071307
line, end_line, offset = 0,1,0
13081308
else:
@@ -1313,49 +1313,56 @@ def _find_keyword_typos(self):
13131313

13141314
error_code = lines[line -1 if line > 0 else 0:end_line]
13151315
error_code[0] = error_code[0][offset:]
1316-
error_code = textwrap.dedent(''.join(error_code))
1316+
error_code = textwrap.dedent('\n'.join(error_code))
13171317

13181318
# Do not continue if the source is too large
13191319
if len(error_code) > 1024:
13201320
return
13211321

13221322
tokens = tokenize.generate_tokens(io.StringIO(error_code).readline)
13231323
tokens_left_to_process = 10
1324+
import difflib
13241325
for token in tokens:
1325-
tokens_left_to_process -= 1
1326-
if tokens_left_to_process < 0:
1327-
break
13281326
start, end = token.start, token.end
13291327
if token.type != tokenize.NAME:
13301328
continue
1329+
# Only consider NAME tokens on the same line as the error
13311330
if from_filename and token.start[0]+line != end_line+1:
13321331
continue
13331332
wrong_name = token.string
13341333
if wrong_name in keyword.kwlist:
13351334
continue
1336-
suggestion = _suggestions._generate_suggestions(keyword.kwlist, wrong_name)
1337-
if not suggestion or suggestion == wrong_name:
1338-
continue
1339-
# Try to replace the token with the keyword
1340-
the_lines = error_code.splitlines()
1341-
the_line = the_lines[start[0] - 1]
1342-
chars = list(the_line)
1343-
chars[token.start[1]:token.end[1]] = suggestion
1344-
the_lines[start[0] - 1] = ''.join(chars)
1345-
code = ''.join(the_lines)
1346-
# Check if it works
1347-
try:
1348-
codeop.compile_command(code, symbol="exec", flags=codeop.PyCF_ONLY_AST)
1349-
except SyntaxError as e:
1350-
continue
1351-
# Keep token.line but handle offsets correctly
1352-
self.text = token.line
1353-
self.offset = token.start[1] + 1
1354-
self.end_offset = token.end[1] + 1
1355-
self.lineno = start[0]
1356-
self.end_lineno = end[0]
1357-
self.msg = f"invalid syntax. Did you mean '{suggestion}'?"
1358-
return
1335+
1336+
# Limit the number of valid tokens to consider to not spend
1337+
# to much time in this function
1338+
tokens_left_to_process -= 1
1339+
if tokens_left_to_process < 0:
1340+
break
1341+
# Limit the number of possible matches to try
1342+
matches = difflib.get_close_matches(wrong_name, keyword.kwlist, n=3)
1343+
for suggestion in matches:
1344+
if not suggestion or suggestion == wrong_name:
1345+
continue
1346+
# Try to replace the token with the keyword
1347+
the_lines = error_code.splitlines()
1348+
the_line = the_lines[start[0] - 1]
1349+
chars = list(the_line)
1350+
chars[token.start[1]:token.end[1]] = suggestion
1351+
the_lines[start[0] - 1] = ''.join(chars)
1352+
code = '\n'.join(the_lines)
1353+
# Check if it works
1354+
try:
1355+
codeop.compile_command(code, symbol="exec", flags=codeop.PyCF_ONLY_AST)
1356+
except SyntaxError as e:
1357+
continue
1358+
# Keep token.line but handle offsets correctly
1359+
self.text = token.line
1360+
self.offset = token.start[1] + 1
1361+
self.end_offset = token.end[1] + 1
1362+
self.lineno = start[0]
1363+
self.end_lineno = end[0]
1364+
self.msg = f"invalid syntax. Did you mean '{suggestion}'?"
1365+
return
13591366

13601367

13611368
def _format_syntax_error(self, stype, **kwargs):

Parser/action_helpers.c

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,17 +1712,11 @@ _PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq * na
17121712
return _PyAST_ImportFrom(module, names, level, lineno, col_offset, end_lineno, end_col_offset, arena);
17131713
}
17141714

1715-
stmt_ty
1716-
_PyPegen_register_stmt(Parser *p, stmt_ty stmt) {
1717-
p->last_stmt_location.lineno = stmt->lineno;
1718-
p->last_stmt_location.col_offset = stmt->col_offset;
1719-
p->last_stmt_location.end_lineno = stmt->end_lineno;
1720-
p->last_stmt_location.end_col_offset = stmt->end_col_offset;
1721-
return stmt;
1722-
}
1723-
17241715
asdl_stmt_seq*
17251716
_PyPegen_register_stmts(Parser *p, asdl_stmt_seq* stmts) {
1717+
if (!p->call_invalid_rules) {
1718+
return stmts;
1719+
}
17261720
Py_ssize_t len = asdl_seq_LEN(stmts);
17271721
if (len == 0) {
17281722
return stmts;
@@ -1733,4 +1727,4 @@ _PyPegen_register_stmts(Parser *p, asdl_stmt_seq* stmts) {
17331727
p->last_stmt_location.end_lineno = last_stmt->end_lineno;
17341728
p->last_stmt_location.end_col_offset = last_stmt->end_col_offset;
17351729
return stmts;
1736-
}
1730+
}

0 commit comments

Comments
 (0)