8000 gh-91210: Improve error message when non-default param follows defaul… · python/cpython@7e36abb · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7e36abb

Browse files
authored
gh-91210: Improve error message when non-default param follows default (GH-95933)
- Improve error message when parameter without a default follows one with a default - Show same error message when positional-only params precede the default/non-default sequence
1 parent 78359b1 commit 7e36abb

File tree

5 files changed

+391
-365
lines changed

5 files changed

+391
-365
lines changed

Grammar/python.gram

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,14 +1162,14 @@ invalid_dict_comprehension:
11621162
| '{' a='**' bitwise_or for_if_clauses '}' {
11631163
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "dict unpacking cannot be used in dict comprehension") }
11641164
invalid_parameters:
1165-
| param_no_default* invalid_parameters_helper a=param_no_default {
1166-
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
1167-
| param_no_default* a='(' param_no_default+ ','? b=')' {
1168-
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Function parameters cannot be parenthesized") }
11691165
| a="/" ',' {
11701166
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
11711167
| (slash_no_default | slash_with_default) param_maybe_default* a='/' {
11721168
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
1169+
| slash_no_default? param_no_default* invalid_parameters_helper a=param_no_default {
1170+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "parameter without a default follows parameter with a default") }
1171+
| param_no_default* a='(' param_no_default+ ','? b=')' {
1172+
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Function parameters cannot be parenthesized") }
11731173
| (slash_no_default | slash_with_default)? param_maybe_default* '*' (',' | param_no_default) param_maybe_default* a='/' {
11741174
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ must be ahead of *") }
11751175
| param_maybe_default+ '/' a='*' {
@@ -1190,14 +1190,14 @@ invalid_parameters_helper: # This is only there to avoid type errors
11901190
| a=slash_with_default { _PyPegen_singleton_seq(p, a) }
11911191
| param_with_default+
11921192
invalid_lambda_parameters:
1193-
| lambda_param_no_default* invalid_lambda_parameters_helper a=lambda_param_no_default {
1194-
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
1195-
| lambda_param_no_default* a='(' ','.lambda_param+ ','? b=')' {
1196-
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Lambda expression parameters cannot be parenthesized") }
11971193
| a="/" ',' {
11981194
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
11991195
| (lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* a='/' {
12001196
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
1197+
| lambda_slash_no_default? lambda_param_no_default* invalid_lambda_parameters_helper a=lambda_param_no_default {
1198+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "parameter without a default follows parameter with a default") }
1199+
| lambda_param_no_defau 8000 lt* a='(' ','.lambda_param+ ','? b=')' {
1200+
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Lambda expression parameters cannot be parenthesized") }
12011201
| (lambda_slash_no_default | lambda_slash_with_default)? lambda_param_maybe_default* '*' (',' | lambda_param_no_default) lambda_param_maybe_default* a='/' {
12021202
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ must be ahead of *") }
12031203
| lambda_param_maybe_default+ '/' a='*' {

Lib/test/test_positional_only_arg.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ def assertRaisesSyntaxError(self, codestr, regex="invalid syntax"):
2323
compile(codestr + "\n", "<test>", "single")
2424

2525
def test_invalid_syntax_errors(self):
26-
check_syntax_error(self, "def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
27-
check_syntax_error(self, "def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
28-
check_syntax_error(self, "def f(a = 5, b=1, /, c, *, d=2): pass", "non-default argument follows default argument")
29-
check_syntax_error(self, "def f(a = 5, b, /): pass", "non-default argument follows default argument")
26+
check_syntax_error(self, "def f(a, b = 5, /, c): pass", "parameter without a default follows parameter with a default")
27+
check_syntax_error(self, "def f(a = 5, b, /, c): pass", "parameter without a default follows parameter with a default")
28+
check_syntax_error(self, "def f(a = 5, b=1, /, c, *, d=2): pass", "parameter without a default follows parameter with a default")
29+
check_syntax_error(self, "def f(a = 5, b, /): pass", "parameter without a default follows parameter with a default")
30+
check_syntax_error(self, "def f(a, /, b = 5, c): pass", "parameter without a default follows parameter with a default")
3031
check_syntax_error(self, "def f(*args, /): pass")
3132
check_syntax_error(self, "def f(*args, a, /): pass")
3233
check_syntax_error(self, "def f(**kwargs, /): pass")
@@ -44,10 +45,11 @@ def test_invalid_syntax_errors(self):
4445
check_syntax_error(self, "def f(a, *, c, /, d, e): pass")
4546

4647
def test_invalid_syntax_errors_async(self):
47-
check_syntax_error(self, "async def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
48-
check_syntax_error(self, "async def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
49-
check_syntax_error(self, "async def f(a = 5, b=1, /, c, d=2): pass", "non-default argument follows default argument")
50-
check_syntax_error(self, "async def f(a = 5, b, /): pass", "non-default argument follows default argument")
48+
check_syntax_error(self, "async def f(a, b = 5, /, c): pass", "parameter without a default follows parameter with a default")
49+
check_syntax_error(self, "async def f(a = 5, b, /, c): pass", "parameter without a default follows parameter with a default")
50+
check_syntax_error(self, "async def f(a = 5, b=1, /, c, d=2): pass", "parameter without a default follows parameter with a default")
51+
check_syntax_error(self, "async def f(a = 5, b, /): pass", "parameter without a default follows parameter with a default")
52+
check_syntax_error(self, "async def f(a, /, b = 5, c): pass", "parameter without a default follows parameter with a default")
5153
check_syntax_error(self, "async def f(*args, /): pass")
5254
check_syntax_error(self, "async def f(*args, a, /): pass")
5355
check_syntax_error(self, "async def f(**kwargs, /): pass")
@@ -231,9 +233,11 @@ def test_lambdas(self):
231233
self.assertEqual(x(1, 2), 3)
232234

233235
def test_invalid_syntax_lambda(self):
234-
check_syntax_error(self, "lambda a, b = 5, /, c: None", "non-default argument follows default argument")
235-
check_syntax_error(self, "lambda a = 5, b, /, c: None", "non-default argument follows default argument")
236-
check_syntax_error(self, "lambda a = 5, b, /: None", "non-default argument follows default argument")
236+
check_syntax_error(self, "lambda a, b = 5, /, c: None", "parameter without a default follows parameter with a default")
237+
check_syntax_error(self, "lambda a = 5, b, /, c: None", "parameter without a default follows parameter with a default")
238+
check_syntax_error(self, "lambda a = 5, b=1, /, c, *, d=2: None", "parameter without a default follows parameter with a default")
239+
check_syntax_error(self, "lambda a = 5, b, /: None", "parameter without a default follows parameter with a default")
240+
check_syntax_error(self, "lambda a, /, b = 5, c: None", "parameter without a default follows parameter with a default")
237241
check_syntax_error(self, "lambda *args, /: None")
238242
check_syntax_error(self, "lambda *args, a, /: None")
239243
check_syntax_error(self, "lambda **kwargs, /: None")

Lib/test/test_syntax.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,12 @@
334334
>>> def f(x, y=1, z):
335335
... pass
336336
Traceback (most recent call last):
337-
SyntaxError: non-default argument follows default argument
337+
SyntaxError: parameter without a default follows parameter with a default
338+
339+
>>> def f(x, /, y=1, z):
340+
... pass
341+
Traceback (most recent call last):
342+
SyntaxError: parameter without a default follows parameter with a default
338343
339344
>>> def f(x, None):
340345
... pass
@@ -555,6 +560,14 @@
555560
Traceback (most recent call last):
556561
SyntaxError: expected default value expression
557562
563+
>>> lambda a,d=3,c: None
564+
Traceback (most recent call last):
565+
SyntaxError: parameter without a default follows parameter with a default
566+
567+
>>> lambda a,/,d=3,c: None
568+
Traceback (most recent call last):
569+
SyntaxError: parameter without a default follows parameter with a default
570+
558571
>>> import ast; ast.parse('''
559572
... def f(
560573
... *, # type: int
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve error message when a parameter without a default value follows one with a default value, and show the same message, even when the non-default/default sequence is preceded by positional-only parameters.

0 commit comments

Comments
 (0)
0