Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions Lib/test/test_fstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -1004,10 +1004,14 @@ def test_str_format_differences(self):
self.assertEqual('{d[0]}'.format(d=d), 'integer')

def test_invalid_expressions(self):
self.assertAllRaise(SyntaxError, 'invalid syntax',
[r"f'{a[4)}'",
r"f'{a(4]}'",
])
self.assertAllRaise(SyntaxError,
r"closing parenthesis '\)' does not match "
r"opening parenthesis '\[' \(<fstring>, line 1\)",
[r"f'{a[4)}'"])
self.assertAllRaise(SyntaxError,
r"closing parenthesis '\]' does not match "
r"opening parenthesis '\(' \(<fstring>, line 1\)",
[r"f'{a(4]}'"])

def test_errors(self):
# see issue 26287
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def make_pth(self, contents, pth_dir='.', pth_name=TESTFN):

def test_addpackage_import_bad_syntax(self):
# Issue 10642
pth_dir, pth_fn = self.make_pth("import bad)syntax\n")
pth_dir, pth_fn = self.make_pth("import bad-syntax\n")
with captured_stderr() as err_out:
site.addpackage(pth_dir, pth_fn, set())
self.assertRegex(err_out.getvalue(), "line 1")
Expand All @@ -143,7 +143,7 @@ def test_addpackage_import_bad_syntax(self):
# order doesn't matter. The next three could be a single check
# but my regex foo isn't good enough to write it.
self.assertRegex(err_out.getvalue(), 'Traceback')
self.assertRegex(err_out.getvalue(), r'import bad\)syntax')
self.assertRegex(err_out.getvalue(), r'import bad-syntax')
self.assertRegex(err_out.getvalue(), 'SyntaxError')

def test_addpackage_import_bad_exec(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improved syntax error messages for unbalanced parentheses.
32 changes: 32 additions & 0 deletions Parser/tokenizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1842,12 +1842,44 @@ tok_get(struct tok_state *tok, char **p_start, char **p_end)
case '(':
case '[':
case '{':
#ifndef PGEN
if (tok->level >= MAXLEVEL) {
return syntaxerror(tok, "too many nested parentheses");
}
tok->parenstack[tok->level] = c;
tok->parenlinenostack[tok->level] = tok->lineno;
#endif
tok->level++;
break;
case ')':
case ']':
case '}':
#ifndef PGEN
if (!tok->level) {
return syntaxerror(tok, "unmatched '%c'", c);
}
#endif
tok->level--;
#ifndef PGEN
int opening = tok->parenstack[tok->level];
if (!((opening == '(' && c == ')') ||
(opening == '[' && c == ']') ||
(opening == '{' && c == '}')))
{
if (tok->parenlinenostack[tok->level] != tok->lineno) {
return syntaxerror(tok,
"closing parenthesis '%c' does not match "
"opening parenthesis '%c' on line %d",
c, opening, tok->parenlinenostack[tok->level]);
}
else {
return syntaxerror(tok,
"closing parenthesis '%c' does not match "
"opening parenthesis '%c'",
c, opening);
}
}
#endif
break;
}

Expand Down
5 changes: 4 additions & 1 deletion Parser/tokenizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ extern "C" {
#include "token.h" /* For token types */

#define MAXINDENT 100 /* Max indentation level */
#define MAXLEVEL 200 /* Max parentheses level */

enum decoding_state {
STATE_INIT,
Expand Down Expand Up @@ -39,14 +40,16 @@ struct tok_state {
int lineno; /* Current line number */
int level; /* () [] {} Parentheses nesting level */
/* Used to allow free continuations inside them */
/* Stuff for checking on different tab sizes */
#ifndef PGEN
char parenstack[MAXLEVEL];
int parenlinenostack[MAXLEVEL];
/* pgen doesn't have access to Python codecs, it cannot decode the input
filename. The bytes filename might be kept, but it is only used by
indenterror() and it is not really needed: pgen only compiles one file
(Grammar/Grammar). */
PyObject *filename;
#endif
/* Stuff for checking on different tab sizes */
int altindstack[MAXINDENT]; /* Stack of alternate indents */
/* Stuff for PEP 0263 */
enum decoding_state decoding_state;
Expand Down