FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

bpo-46838: Syntax error improvements for function definitions by pablogsal · Pull Request #31590 · python/cpython · GitHub

/ cpython Public
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .c  (1) .gram  (1) .py  (1) .rst  (1) All 4 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
54 changes: 45 additions & 9 deletions Grammar/python.gram
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,16 @@ slash_with_default[SlashWithDefault*]:
| a=param_no_default* b=param_with_default+ '/' &')' { _PyPegen_slash_with_default(p, (asdl_arg_seq *)a, b) }

star_etc[StarEtc*]:
| invalid_star_etc
| '*' a=param_no_default b=param_maybe_default* c=[kwds] {
_PyPegen_star_etc(p, a, b, c) }
| '*' ',' b=param_maybe_default+ c=[kwds] {
_PyPegen_star_etc(p, NULL, b, c) }
| a=kwds { _PyPegen_star_etc(p, NULL, NULL, a) }
| invalid_star_etc

kwds[arg_ty]: '**' a=param_no_default { a }
kwds[arg_ty]:
| invalid_kwds
| '**' a=param_no_default { a }

# One parameter. This *includes* a following comma and type comment.
#
Expand All @@ -339,7 +341,7 @@ param_maybe_default[NameDefaultPair*]:
| a=param c=default? tc=TYPE_COMMENT? &')' { _PyPegen_name_default_pair(p, a, c, tc) }
param[arg_ty]: a=NAME b=annotation? { _PyAST_arg(a->v.Name.id, b, NULL, EXTRA) }
annotation[expr_ty]: ':' a=expression { a }
default[expr_ty]: '=' a=expression { a }
default[expr_ty]: '=' a=expression { a } | invalid_default

# If statement
# ------------
Expand Down Expand Up @@ -836,14 +838,16 @@ lambda_slash_with_default[SlashWithDefault*]:
| a=lambda_param_no_default* b=lambda_param_with_default+ '/' &':' { _PyPegen_slash_with_default(p, (asdl_arg_seq *)a, b) }

lambda_star_etc[StarEtc*]:
| invalid_lambda_star_etc
| '*' a=lambda_param_no_default b=lambda_param_maybe_default* c=[lambda_kwds] {
_PyPegen_star_etc(p, a, b, c) }
| '*' ',' b=lambda_param_maybe_default+ c=[lambda_kwds] {
_PyPegen_star_etc(p, NULL, b, c) }
| a=lambda_kwds { _PyPegen_star_etc(p, NULL, NULL, a) }
| invalid_lambda_star_etc

lambda_kwds[arg_ty]: '**' a=lambda_param_no_default { a }
lambda_kwds[arg_ty]:
| invalid_lambda_kwds
| '**' a=lambda_param_no_default { a }

lambda_param_no_default[arg_ty]:
| a=lambda_param ',' { a }
Expand Down Expand Up @@ -1151,6 +1155,26 @@ invalid_parameters:
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
| param_no_default* a='(' param_no_default+ ','? b=')' {
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Function parameters cannot be parenthesized") }
| a="/" ',' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
| (slash_no_default | slash_with_default) param_maybe_default* a='/' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
| (slash_no_default | slash_with_default)? param_maybe_default* '*' (',' | param_no_default) param_maybe_default* a='/' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ must be ahead of *") }
| param_maybe_default+ '/' a='*' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected comma between / and *") }
invalid_default:
| a='=' &(')'|',') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected default value expression") }
invalid_star_etc:
| a='*' (')' | ',' (')' | '**')) { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "named arguments must follow bare *") }
| '*' ',' TYPE_COMMENT { RAISE_SYNTAX_ERROR("bare * has associated type comment") }
| '*' param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-positional argument cannot have default value") }
| '*' (param_no_default | ',') param_maybe_default* a='*' (param_no_default | ',') {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "* argument may appear only once") }
invalid_kwds:
| '**' param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-keyword argument cannot have default value") }
| '**' param ',' a=param { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
| '**' param ',' a[Token*]=('*'|'**'|'/') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
invalid_parameters_helper: # This is only there to avoid type errors
| a=slash_with_default { _PyPegen_singleton_seq(p, a) }
| param_with_default+
Expand All @@ -1159,14 +1183,26 @@ invalid_lambda_parameters:
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
| lambda_param_no_default* a='(' ','.lambda_param+ ','? b=')' {
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Lambda expression parameters cannot be parenthesized") }
| a="/" ',' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
| (lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* a='/' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
| (lambda_slash_no_default | lambda_slash_with_default)? lambda_param_maybe_default* '*' (',' | lambda_param_no_default) lambda_param_maybe_default* a='/' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ must be ahead of *") }
| lambda_param_maybe_default+ '/' a='*' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected comma between / and *") }
invalid_lambda_parameters_helper:
| a=lambda_slash_with_default { _PyPegen_singleton_seq(p, a) }
| lambda_param_with_default+
invalid_star_etc:
| a='*' (')' | ',' (')' | '**')) { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "named arguments must follow bare *") }
| '*' ',' TYPE_COMMENT { RAISE_SYNTAX_ERROR("bare * has associated type comment") }
invalid_lambda_star_etc:
| '*' (':' | ',' (':' | '**')) { RAISE_SYNTAX_ERROR("named arguments must follow bare *") }
| '*' lambda_param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-positional argument cannot have default value") }
| '*' (lambda_param_no_default | ',') lambda_param_maybe_default* a='*' (lambda_param_no_default | ',') {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "* argument may appear only once") }
invalid_lambda_kwds:
| '**' lambda_param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-keyword argument cannot have default value") }
| '**' lambda_param ',' a=lambda_param { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
| '**' lambda_param ',' a[Token*]=('*'|'**'|'/') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
invalid_double_type_comments:
| TYPE_COMMENT NEWLINE TYPE_COMMENT NEWLINE INDENT {
RAISE_SYNTAX_ERROR("Cannot have two type comments on def") }
Expand Down Expand Up @@ -1269,4 +1305,4 @@ invalid_kvpair:
| a=expression !(':') {
RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, a->lineno, a->end_col_offset - 1, a->end_lineno, -1, "':' expected after dictionary key") }
| expression ':' a='*' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") }
| expression a=':' {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
| expression a=':' {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
204 changes: 204 additions & 0 deletions Lib/test/test_syntax.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,210 @@
Traceback (most recent call last):
SyntaxError: invalid syntax

>>> def foo(/,a,b=,c):
... pass
Traceback (most recent call last):
SyntaxError: at least one argument must precede /

>>> def foo(a,/,/,b,c):
... pass
Traceback (most recent call last):
SyntaxError: / may appear only once

>>> def foo(a,/,a1,/,b,c):
... pass
Traceback (most recent call last):
SyntaxError: / may appear only once

>>> def foo(a=1,/,/,*b,/,c):
... pass
Traceback (most recent call last):
SyntaxError: / may appear only once

>>> def foo(a,/,a1=1,/,b,c):
... pass
Traceback (most recent call last):
SyntaxError: / may appear only once

>>> def foo(a,*b,c,/,d,e):
... pass
Traceback (most recent call last):
SyntaxError: / must be ahead of *

>>> def foo(a=1,*b,c=3,/,d,e):
... pass
Traceback (most recent call last):
SyntaxError: / must be ahead of *

>>> def foo(a,*b=3,c):
... pass
Traceback (most recent call last):
SyntaxError: var-positional argument cannot have default value

>>> def foo(a,*b: int=,c):
... pass
Traceback (most recent call last):
SyntaxError: var-positional argument cannot have default value

>>> def foo(a,**b=3):
... pass
Traceback (most recent call last):
SyntaxError: var-keyword argument cannot have default value

>>> def foo(a,**b: int=3):
... pass
Traceback (most recent call last):
SyntaxError: var-keyword argument cannot have default value

>>> def foo(a,*a, b, **c, d):
... pass
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> def foo(a,*a, b, **c, d=4):
... pass
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> def foo(a,*a, b, **c, *d):
... pass
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> def foo(a,*a, b, **c, **d):
... pass
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> def foo(a=1,/,**b,/,c):
... pass
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> def foo(*b,*d):
... pass
Traceback (most recent call last):
SyntaxError: * argument may appear only once

>>> def foo(a,*b,c,*d,*e,c):
... pass
Traceback (most recent call last):
SyntaxError: * argument may appear only once

>>> def foo(a,b,/,c,*b,c,*d,*e,c):
... pass
Traceback (most recent call last):
SyntaxError: * argument may appear only once

>>> def foo(a,b,/,c,*b,c,*d,**e):
... pass
Traceback (most recent call last):
SyntaxError: * argument may appear only once

>>> def foo(a=1,/*,b,c):
... pass
Traceback (most recent call last):
SyntaxError: expected comma between / and *

>>> def foo(a=1,d=,c):
... pass
Traceback (most recent call last):
SyntaxError: expected default value expression

>>> def foo(a,d=,c):
... pass
Traceback (most recent call last):
SyntaxError: expected default value expression

>>> def foo(a,d: int=,c):
... pass
Traceback (most recent call last):
SyntaxError: expected default value expression

>>> lambda /,a,b,c: None
Traceback (most recent call last):
SyntaxError: at least one argument must precede /

>>> lambda a,/,/,b,c: None
Traceback (most recent call last):
SyntaxError: / may appear only once

>>> lambda a,/,a1,/,b,c: None
Traceback (most recent call last):
SyntaxError: / may appear only once

>>> lambda a=1,/,/,*b,/,c: None
Traceback (most recent call last):
SyntaxError: / may appear only once

>>> lambda a,/,a1=1,/,b,c: None
Traceback (most recent call last):
SyntaxError: / may appear only once

>>> lambda a,*b,c,/,d,e: None
Traceback (most recent call last):
SyntaxError: / must be ahead of *

>>> lambda a=1,*b,c=3,/,d,e: None
Traceback (most recent call last):
SyntaxError: / must be ahead of *

>>> lambda a=1,/*,b,c: None
Traceback (most recent call last):
SyntaxError: expected comma between / and *

>>> lambda a,*b=3,c: None
Traceback (most recent call last):
SyntaxError: var-positional argument cannot have default value

>>> lambda a,**b=3: None
Traceback (most recent call last):
SyntaxError: var-keyword argument cannot have default value

>>> lambda a, *a, b, **c, d: None
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> lambda a,*a, b, **c, d=4: None
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> lambda a,*a, b, **c, *d: None
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> lambda a,*a, b, **c, **d: None
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> lambda a=1,/,**b,/,c: None
Traceback (most recent call last):
SyntaxError: arguments cannot follow var-keyword argument

>>> lambda *b,*d: None
Traceback (most recent call last):
SyntaxError: * argument may appear only once

>>> lambda a,*b,c,*d,*e,c: None
Traceback (most recent call last):
SyntaxError: * argument may appear only once

>>> lambda a,b,/,c,*b,c,*d,*e,c: None
Traceback (most recent call last):
SyntaxError: * argument may appear only once

>>> lambda a,b,/,c,*b,c,*d,**e: None
Traceback (most recent call last):
SyntaxError: * argument may appear only once

>>> lambda a=1,d=,c: None
Traceback (most recent call last):
SyntaxError: expected default value expression

>>> lambda a,d=,c: None
Traceback (most recent call last):
SyntaxError: expected default value expression

>>> import ast; ast.parse('''
... def f(
... *, # type: int
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve syntax errors for incorrect function definitions. Patch by Pablo
Galindo
Loading

Back | FazBrowse Home | New Git URL