-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
bpo-40334: Support type comments #19780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 7 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
e17bb92
First steps towards supporting type comments
gvanrossum 2e4126c
Support func_type_input in new parser
gvanrossum 7b446fb
Support '# type: ignore <tag>'
gvanrossum ed95a14
Support per-argument type comments ('long form')
gvanrossum acb69b3
Fix segfault in long-form argument type comments
gvanrossum 1e8f7fd
Fix failing test_ast by refactoring param rules
gvanrossum da253f3
Merge remote-tracking branch 'origin/master' into type-comments
gvanrossum 86d9f69
Fix bug in add_type_comment()
gvanrossum c8a2888
Suggestions from code review
gvanrossum fd2794b
Refactoring some things in response to review
gvanrossum d081578
More thorough error checks in NEW_TYPE_COMMENT
gvanrossum fecd8e8
Add CHECK() around _PyPegen_seq_append_to_end() call
gvanrossum File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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 | ||||
---|---|---|---|---|---|---|
|
@@ -17,6 +17,8 @@ _PyPegen_parse(Parser *p) | |||||
result = interactive_rule(p); | ||||||
} else if (p->start_rule == Py_eval_input) { | ||||||
result = eval_rule(p); | ||||||
} else if (p->start_rule == Py_func_type_input) { | ||||||
result = func_type_rule(p); | ||||||
} else if (p->start_rule == Py_fstring_input) { | ||||||
result = fstring_rule(p); | ||||||
} | ||||||
|
@@ -26,11 +28,20 @@ _PyPegen_parse(Parser *p) | |||||
|
||||||
// The end | ||||||
''' | ||||||
file[mod_ty]: a=[statements] ENDMARKER { Module(a, NULL, p->arena) } | ||||||
file[mod_ty]: a=[statements] ENDMARKER { _PyPegen_make_module(p, a) } | ||||||
interactive[mod_ty]: a=statement_newline { Interactive(a, p->arena) } | ||||||
eval[mod_ty]: a=expressions NEWLINE* ENDMARKER { Expression(a, p->arena) } | ||||||
func_type[mod_ty]: '(' a=[type_expressions] ')' '->' b=expression { FunctionType(a, b, p->arena) } | ||||||
fstring[expr_ty]: star_expressions | ||||||
|
||||||
# type_expressions allow */** but ignore them | ||||||
type_expressions[asdl_seq*]: | ||||||
| a=','.expression+ ',' '*' b=expression ',' '**' c=expression { | ||||||
_PyPegen_seq_append_to_end(p, _PyPegen_seq_append_to_end(p, a, b), c) } | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| a=','.expression+ ',' '*' b=expression { _PyPegen_seq_append_to_end(p, a, b) } | ||||||
| a=','.expression+ ',' '**' b=expression { _PyPegen_seq_append_to_end(p, a, b) } | ||||||
| ','.expression+ | ||||||
|
||||||
statements[asdl_seq*]: a=statement+ { _PyPegen_seq_flatten(p, a) } | ||||||
statement[asdl_seq*]: a=compound_stmt { _PyPegen_singleton_seq(p, a) } | simple_stmt | ||||||
statement_newline[asdl_seq*]: | ||||||
|
@@ -73,8 +84,8 @@ assignment: | |||||
| a=('(' b=inside_paren_ann_assign_target ')' { b } | ||||||
| ann_assign_subscript_attribute_target) ':' b=expression c=['=' d=annotated_rhs { d }] { | ||||||
_Py_AnnAssign(a, b, c, 0, EXTRA)} | ||||||
| a=(z=star_targets '=' { z })+ b=(yield_expr | star_expressions) { | ||||||
_Py_Assign(a, b, NULL, EXTRA) } | ||||||
| a=(z=star_targets '=' { z })+ b=(yield_expr | star_expressions) tc=[TYPE_COMMENT] { | ||||||
_Py_Assign(a, b, NEW_TYPE_COMMENT(tc), EXTRA) } | ||||||
| a=target b=augassign c=(yield_expr | star_expressions) { | ||||||
_Py_AugAssign(a, b->kind, c, EXTRA) } | ||||||
| invalid_assignment | ||||||
|
@@ -145,14 +156,14 @@ while_stmt[stmt_ty]: | |||||
| 'while' a=named_expression ':' b=block c=[else_block] { _Py_While(a, b, c, EXTRA) } | ||||||
|
||||||
for_stmt[stmt_ty]: | ||||||
| is_async=[ASYNC] 'for' t=star_targets 'in' ex=star_expressions ':' b=block el=[else_block] { | ||||||
(is_async ? _Py_AsyncFor : _Py_For)(t, ex, b, el, NULL, EXTRA) } | ||||||
| is_async=[ASYNC] 'for' t=star_targets 'in' ex=star_expressions ':' tc=[TYPE_COMMENT] b=block el=[else_block] { | ||||||
(is_async ? _Py_AsyncFor : _Py_For)(t, ex, b, el, NEW_TYPE_COMMENT(tc), EXTRA) } | ||||||
|
||||||
with_stmt[stmt_ty]: | ||||||
| is_async=[ASYNC] 'with' '(' a=','.with_item+ ')' ':' b=block { | ||||||
(is_async ? _Py_AsyncWith : _Py_With)(a, b, NULL, EXTRA) } | ||||||
| is_async=[ASYNC] 'with' a=','.with_item+ ':' b=block { | ||||||
(is_async ? _Py_AsyncWith : _Py_With)(a, b, NULL, EXTRA) } | ||||||
| is_async=[ASYNC] 'with' a=','.with_item+ ':' tc=[TYPE_COMMENT] b=block { | ||||||
(is_async ? _Py_AsyncWith : _Py_With)(a, b, NEW_TYPE_COMMENT(tc), EXTRA) } | ||||||
with_item[withitem_ty]: | ||||||
| e=expression o=['as' t=target { t }] { _Py_withitem(e, o, p->arena) } | ||||||
|
||||||
|
@@ -177,43 +188,74 @@ function_def[stmt_ty]: | |||||
| function_def_raw | ||||||
|
||||||
function_def_raw[stmt_ty]: | ||||||
| is_async=[ASYNC] 'def' n=NAME '(' params=[params] ')' a=['->' z=annotation { z }] ':' b=block { | ||||||
| is_async=[ASYNC] 'def' n=NAME '(' params=[params] ')' a=['->' z=expression { z }] ':' tc=[func_type_comment] b=block { | ||||||
(is_async ? _Py_AsyncFunctionDef : _Py_FunctionDef)(n->v.Name.id, | ||||||
(params) ? params : CHECK(_PyPegen_empty_arguments(p)), | ||||||
b, NULL, a, NULL, EXTRA) } | ||||||
b, NULL, a, NEW_TYPE_COMMENT(tc), EXTRA) } | ||||||
func_type_comment[PyObject *]: | ||||||
gvanrossum marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| NEWLINE t=TYPE_COMMENT &(NEWLINE INDENT) { t } # Must be followed by indented block | ||||||
| invalid_double_type_comments | ||||||
| TYPE_COMMENT | ||||||
|
||||||
params[arguments_ty]: | ||||||
| invalid_parameters | ||||||
| parameters | ||||||
|
||||||
parameters[arguments_ty]: | ||||||
| a=slash_without_default b=[',' x=plain_names { x }] c=[',' y=names_with_default { y }] d=[',' z=[star_etc] { z }] { | ||||||
| a=slash_no_default b=param_no_default* c=param_with_default* d=[star_etc] { | ||||||
_PyPegen_make_arguments(p, a, NULL, b, c, d) } | ||||||
| a=slash_with_default b=[',' y=names_with_default { y }] c=[',' z=[star_etc] { z }] { | ||||||
| a=slash_with_default b=param_with_default* c=[star_etc] { | ||||||
_PyPegen_make_arguments(p, NULL, a, NULL, b, c) } | ||||||
| a=plain_names b=[',' y=names_with_default { y }] c=[',' z=[star_etc] { z }] { | ||||||
| a=param_no_default+ b=param_with_default* c=[star_etc] { | ||||||
_PyPegen_make_arguments(p, NULL, NULL, a, b, c) } | ||||||
| a=names_with_default b=[',' z=[star_etc] { z }] { _PyPegen_make_arguments(p, NULL, NULL, NULL, a, b)} | ||||||
| a=param_with_default+ b=[star_etc] { _PyPegen_make_arguments(p, NULL, NULL, NULL, a, b)} | ||||||
| a=star_etc { _PyPegen_make_arguments(p, NULL, NULL, NULL, NULL, a) } | ||||||
slash_without_default[asdl_seq*]: a=plain_names ',' '/' { a } | ||||||
slash_with_default[SlashWithDefault*]: a=[n=plain_names ',' { n }] b=names_with_default ',' '/' { | ||||||
_PyPegen_slash_with_default(p, a, b) } | ||||||
|
||||||
# Some duplication here because we can't write (',' | &')'), | ||||||
# which is because we don't support empty alternatives (yet). | ||||||
# | ||||||
slash_no_default[asdl_seq*]: | ||||||
| a=param_no_default+ '/' ',' { a } | ||||||
| a=param_no_default+ '/' &')' { a } | ||||||
slash_with_default[SlashWithDefault*]: | ||||||
| a=param_no_default* b=param_with_default+ '/' ',' { _PyPegen_slash_with_default(p, a, b) } | ||||||
| a=param_no_default* b=param_with_default+ '/' &')' { _PyPegen_slash_with_default(p, a, b) } | ||||||
|
||||||
star_etc[StarEtc*]: | ||||||
| '*' a=plain_name b=name_with_optional_default* c=[',' d=kwds { d }] [','] { | ||||||
| '*' a=param_no_default b=param_maybe_default* c=[kwds] { | ||||||
_PyPegen_star_etc(p, a, b, c) } | ||||||
| '*' b=name_with_optional_default+ c=[',' d=kwds { d }] [','] { | ||||||
| '*' ',' b=param_maybe_default+ c=[kwds] { | ||||||
_PyPegen_star_etc(p, NULL, b, c) } | ||||||
| a=kwds [','] { _PyPegen_star_etc(p, NULL, NULL, a) } | ||||||
name_with_optional_default[NameDefaultPair*]: | ||||||
| ',' a=plain_name b=['=' e=expression { e }] { _PyPegen_name_default_pair(p, a, b) } | ||||||
names_with_default[asdl_seq*]: a=','.name_with_default+ { a } | ||||||
name_with_default[NameDefaultPair*]: | ||||||
| n=plain_name '=' e=expression { _PyPegen_name_default_pair(p, n, e) } | ||||||
plain_names[asdl_seq*] (memo): a=','.(plain_name !'=')+ { a } | ||||||
plain_name[arg_ty]: | ||||||
| a=NAME b=[':' z=annotation { z }] { _Py_arg(a->v.Name.id, b, NULL, EXTRA) } | ||||||
| a=kwds { _PyPegen_star_etc(p, NULL, NULL, a) } | ||||||
|
||||||
kwds[arg_ty]: | ||||||
| '**' a=plain_name { a } | ||||||
annotation[expr_ty]: expression | ||||||
| '**' a=param_no_default { a } | ||||||
|
||||||
# One parameter. This *includes* a following comma and type comment. | ||||||
# | ||||||
# There are three styles: | ||||||
# - No default | ||||||
# - With default | ||||||
# - Maybe with default | ||||||
# | ||||||
# There are two alternative forms of each, to deal with type comments: | ||||||
# - Ends in a comma followed by an optional type comment | ||||||
# - No comma, optional type comment, must be followed by close paren | ||||||
# The latter form is for a final parameter without trailing comma. | ||||||
# | ||||||
param_no_default[arg_ty]: | ||||||
| a=param ',' tc=TYPE_COMMENT? { _PyPegen_add_type_comment(p, a, tc) } | ||||||
| a=param tc=TYPE_COMMENT? &')' { _PyPegen_add_type_comment(p, a, tc) } | ||||||
param_with_default[NameDefaultPair*]: | ||||||
| a=param c=default ',' tc=TYPE_COMMENT? { _PyPegen_name_default_pair(p, a, c, tc) } | ||||||
| a=param c=default tc=TYPE_COMMENT? &')' { _PyPegen_name_default_pair(p, a, c, tc) } | ||||||
param_maybe_default[NameDefaultPair*]: | ||||||
| a=param c=default? ',' tc=TYPE_COMMENT? { _PyPegen_name_default_pair(p, a, c, tc) } | ||||||
| a=param c=default? tc=TYPE_COMMENT? &')' { _PyPegen_name_default_pair(p, a, c, tc) } | ||||||
param[arg_ty]: a=NAME b=annotation? { _Py_arg(a->v.Name.id, b, NULL, EXTRA) } | ||||||
|
||||||
annotation[expr_ty]: ':' a=expression { a } | ||||||
default[expr_ty]: '=' a=expression { a } | ||||||
|
||||||
decorators[asdl_seq*]: a=('@' f=named_expression NEWLINE { f })+ { a } | ||||||
|
||||||
|
@@ -284,10 +326,10 @@ lambda_star_etc[StarEtc*]: | |||||
_PyPegen_star_etc(p, NULL, b, c) } | ||||||
| a=lambda_kwds [','] { _PyPegen_star_etc(p, NULL, NULL, a) } | ||||||
lambda_name_with_optional_default[NameDefaultPair*]: | ||||||
| ',' a=lambda_plain_name b=['=' e=expression { e }] { _PyPegen_name_default_pair(p, a, b) } | ||||||
| ',' a=lambda_plain_name b=['=' e=expression { e }] { _PyPegen_name_default_pair(p, a, b, NULL) } | ||||||
lambda_names_with_default[asdl_seq*]: a=','.lambda_name_with_default+ { a } | ||||||
lambda_name_with_default[NameDefaultPair*]: | ||||||
| n=lambda_plain_name '=' e=expression { _PyPegen_name_default_pair(p, n, e) } | ||||||
| n=lambda_plain_name '=' e=expression { _PyPegen_name_default_pair(p, n, e, NULL) } | ||||||
lambda_plain_names[asdl_seq*]: a=','.(lambda_plain_name !'=')+ { a } | ||||||
lambda_plain_name[arg_ty]: a=NAME { _Py_arg(a->v.Name.id, NULL, NULL, EXTRA) } | ||||||
lambda_kwds[arg_ty]: '**' a=lambda_plain_name { a } | ||||||
|
@@ -552,5 +594,8 @@ invalid_comprehension: | |||||
| ('[' | '(' | '{') '*' expression for_if_clauses { | ||||||
RAISE_SYNTAX_ERROR("iterable unpacking cannot be used in comprehension") } | ||||||
invalid_parameters: | ||||||
| [plain_names ','] (slash_with_default | names_with_default) ',' plain_names { | ||||||
| param_no_default* (slash_with_default | param_with_default+) param_no_default { | ||||||
RAISE_SYNTAX_ERROR("non-default argument follows default argument") } | ||||||
invalid_double_type_comments: | ||||||
| TYPE_COMMENT NEWLINE TYPE_COMMENT NEWLINE INDENT { | ||||||
RAISE_SYNTAX_ERROR("Cannot have two type comments on def") } |
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.