Skip to content
Closed
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
20 changes: 20 additions & 0 deletions Lib/test/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,26 @@ def test_bad_numerical_literals(self):
check("1e2_", "invalid decimal literal")
check("1e+", "invalid decimal literal")

def test_end_of_numerical_literals_offset(self):
# gh-149277: verify the error caret points at the first invalid
# character, not the last valid digit.
cases = [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the cases here all go through verify_end_of_number. Can we add 0xI/0bz too so we lock the behaviour for the prefix-only path, and maybe assert .msg as well?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the cases here all go through verify_end_of_number. Can we add 0xI/0bz too so we lock the behaviour for the prefix-only path, and maybe assert .msg as well?

("0xfg", 4),
("0x9g", 4),
("0b1z", 4),
("0o7q", 4),
("9spam", 2),
("0xfspam", 4),
("1.0x", 4),
("1e3w", 4),
("1jz", 3),
]
for source, expected_offset in cases:
Comment on lines +143 to +157

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use @subTests decorator for cases instead of this loop and subTest().

with self.subTest(source=source):
with self.assertRaises(SyntaxError) as cm:
compile(source, "<test>", "eval")
self.assertEqual(cm.exception.offset, expected_offset)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also test the rendered carret not just the carret offset. Use check_syntax_error for that.


Comment on lines +143 to +162

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def test_end_of_numerical_literals_offset(self):
# gh-149277: verify the error caret points at the first invalid
# character, not the last valid digit.
cases = [
("0xfg", 4),
("0x9g", 4),
("0b1z", 4),
("0o7q", 4),
("9spam", 2),
("0xfspam", 4),
("1.0x", 4),
("1e3w", 4),
("1jz", 3),
]
for source, expected_offset in cases:
with self.subTest(source=source):
with self.assertRaises(SyntaxError) as cm:
compile(source, "<test>", "eval")
self.assertEqual(cm.exception.offset, expected_offset)
@subTests('source,offset,msg',
[("0xfg", 4, "hexadecimal"),
("0x9g", 4, "hexadecimal"),
("0b1z", 4, "binary"),
("0o7q", 4, "octal"),
("9spam", 2, " decimal"),
("0xfspam", 4, "hexadecimal"),
("1.0x", 4, " decimal"),
("1e3w", 4, " decimal"),
("1jz", 3, "imaginary"),
("0xI", 3, "hexadecimal"),
("0bz", 3, "binary"),
])
def test_end_of_numerical_literals_offset(self, source, offset, msg):
# gh-149277: verify the error caret points at the first invalid
# character, not the last valid digit.
with self.assertRaises(SyntaxError) as cm:
compile(source, "<test>", "eval")
self.assertEqual(cm.exception.offset, offset)
self.assertIn(msg, cm.exception.msg)

def test_end_of_numerical_literals(self):
def check(test, error=False):
with self.subTest(expr=test):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix the :exc:`SyntaxError` caret position for invalid numeric literals to
point at the first invalid character instead of the last valid one.
1 change: 0 additions & 1 deletion Parser/lexer/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,6 @@ verify_end_of_number(struct tok_state *tok, int c, const char *kind) {
}
else /* In future releases, only error will remain. */
if (c < 128 && is_potential_identifier_char(c)) {
tok_backup(tok, c);
_PyTokenizer_syntaxerror(tok, "invalid %s literal", kind);
return 0;
}
Expand Down
Loading