-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
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
bpo-43224: Draft implementation of PEP 646 #30398
Changes from 12 commits
64abc0a
b725671
ea329b9
681dc4e
69f575f
095a620
6713748
038b8f0
f028339
38c17d3
9991937
9017d22
07ed599
74e5252
6cb3aa5
daef59b
3c2a7e7
456156a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -308,6 +308,8 @@ slash_with_default[SlashWithDefault*]: | |
star_etc[StarEtc*]: | ||
| '*' a=param_no_default b=param_maybe_default* c=[kwds] { | ||
_PyPegen_star_etc(p, a, b, c) } | ||
| '*' a=param_no_default_star_annotation 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) } | ||
|
@@ -331,14 +333,19 @@ kwds[arg_ty]: '**' a=param_no_default { a } | |
param_no_default[arg_ty]: | ||
| a=param ',' tc=TYPE_COMMENT? { _PyPegen_add_type_comment_to_arg(p, a, tc) } | ||
| a=param tc=TYPE_COMMENT? &')' { _PyPegen_add_type_comment_to_arg(p, a, tc) } | ||
param_no_default_star_annotation[arg_ty]: | ||
| a=param_star_annotation ',' tc=TYPE_COMMENT? { _PyPegen_add_type_comment_to_arg(p, a, tc) } | ||
| a=param_star_annotation tc=TYPE_COMMENT? &')' { _PyPegen_add_type_comment_to_arg(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? { _PyAST_arg(a->v.Name.id, b, NULL, EXTRA) } | ||
param_star_annotation[arg_ty]: a=NAME b=star_annotation { _PyAST_arg(a->v.Name.id, b, NULL, EXTRA) } | ||
annotation[expr_ty]: ':' a=expression { a } | ||
star_annotation[expr_ty]: ':' a=star_expression { a } | ||
default[expr_ty]: '=' a=expression { a } | ||
|
||
# If statement | ||
|
@@ -780,7 +787,7 @@ primary[expr_ty]: | |
|
||
slices[expr_ty]: | ||
| a=slice !',' { a } | ||
| a[asdl_expr_seq*]=','.slice+ [','] { _PyAST_Tuple(a, Load, EXTRA) } | ||
| a[asdl_expr_seq*]=','.(slice | starred_expression)+ [','] { _PyAST_Tuple(a, Load, EXTRA) } | ||
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. I would prefer to move 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. I just tried this, but then I get I think what happens is, if we move I can't see any way to make sure |
||
|
||
slice[expr_ty]: | ||
| a=[expression] ':' b=[expression] c=[':' d=[expression] { d }] { _PyAST_Slice(a, b, c, EXTRA) } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
doctests = """ | ||
|
||
Setup | ||
|
||
>>> class A: | ||
... def __class_getitem__(cls, x): | ||
... return ParameterisedA(x) | ||
>>> class ParameterisedA: | ||
... def __init__(self, params): | ||
... self._params = params | ||
... def __repr__(self): | ||
... return f"A[{self._params}]" | ||
... def __iter__(self): | ||
... for p in self._params: | ||
... yield p | ||
>>> class B: | ||
... def __iter__(self): | ||
... yield StarredB() | ||
... def __repr__(self): | ||
... return "B" | ||
>>> class StarredB: | ||
... def __repr__(self): | ||
... return "StarredB" | ||
>>> b = B() | ||
|
||
Slices that are supposed to work, starring our custom B class | ||
|
||
>>> A[*b] | ||
A[(StarredB,)] | ||
>>> A[*b, *b] | ||
A[(StarredB, StarredB)] | ||
>>> A[b, *b] | ||
A[(B, StarredB)] | ||
>>> A[*b, b] | ||
A[(StarredB, B)] | ||
>>> A[b, b, *b] | ||
A[(B, B, StarredB)] | ||
>>> A[*b, b, b] | ||
A[(StarredB, B, B)] | ||
>>> A[b, *b, b] | ||
A[(B, StarredB, B)] | ||
>>> A[b, b, *b, b] | ||
A[(B, B, StarredB, B)] | ||
>>> A[b, *b, b, b] | ||
A[(B, StarredB, B, B)] | ||
>>> A[A[b, *b, b]] | ||
A[A[(B, StarredB, B)]] | ||
>>> A[*A[b, *b, b]] | ||
A[(B, StarredB, B)] | ||
>>> A[b, ...] | ||
A[(B, Ellipsis)] | ||
>>> A[*A[b, ...]] | ||
A[(B, Ellipsis)] | ||
|
||
Slices that are supposed to work, starring a list | ||
|
||
>>> l = [1, 2, 3] | ||
>>> A[*l] | ||
A[(1, 2, 3)] | ||
>>> A[*l, 4] | ||
A[(1, 2, 3, 4)] | ||
>>> A[0, *l] | ||
A[(0, 1, 2, 3)] | ||
>>> A[1:2, *l] | ||
A[(slice(1, 2, None), 1, 2, 3)] | ||
>>> repr(A[1:2, *l]) == repr(A[1:2, 1, 2, 3]) | ||
True | ||
|
||
Slices that are supposed to work, starring a tuple | ||
|
||
>>> t = (1, 2, 3) | ||
>>> A[*t] | ||
A[(1, 2, 3)] | ||
>>> A[*t, 4] | ||
A[(1, 2, 3, 4)] | ||
>>> A[0, *t] | ||
A[(0, 1, 2, 3)] | ||
>>> A[1:2, *t] | ||
A[(slice(1, 2, None), 1, 2, 3)] | ||
>>> repr(A[1:2, *t]) == repr(A[1:2, 1, 2, 3]) | ||
True | ||
|
||
Starring an expression (rather than a name) in a slice | ||
|
||
>>> def returns_list(): | ||
... return [1, 2, 3] | ||
>>> A[returns_list()] | ||
A[[1, 2, 3]] | ||
>>> A[returns_list(), 4] | ||
A[([1, 2, 3], 4)] | ||
>>> A[*returns_list()] | ||
A[(1, 2, 3)] | ||
>>> A[*returns_list(), 4] | ||
A[(1, 2, 3, 4)] | ||
>>> A[0, *returns_list()] | ||
A[(0, 1, 2, 3)] | ||
>>> A[*returns_list(), *returns_list()] | ||
A[(1, 2, 3, 1, 2, 3)] | ||
|
||
Slices that should fail | ||
|
||
>>> A[:*b] | ||
Traceback (most recent call last): | ||
... | ||
SyntaxError: invalid syntax | ||
>>> A[*b:] | ||
Traceback (most recent call last): | ||
... | ||
SyntaxError: invalid syntax | ||
>>> A[*b:*b] | ||
Traceback (most recent call last): | ||
... | ||
SyntaxError: invalid syntax | ||
>>> A[**b] | ||
Traceback (most recent call last): | ||
... | ||
SyntaxError: invalid syntax | ||
|
||
*args annotated as starred expression | ||
|
||
>>> def f1(*args: *b): pass | ||
>>> f1.__annotations__ | ||
{'args': StarredB} | ||
|
||
>>> def f2(*args: *b, arg1): pass | ||
>>> f2.__annotations__ | ||
{'args': StarredB} | ||
|
||
>>> def f3(*args: *b, arg1: int): pass | ||
>>> f3.__annotations__ | ||
{'args': StarredB, 'arg1': <class 'int'>} | ||
|
||
>>> def f4(*args: *b, arg1: int = 2): pass | ||
>>> f4.__annotations__ | ||
{'args': StarredB, 'arg1': <class 'int'>} | ||
|
||
Other uses of starred expressions as annotations should fail | ||
|
||
>>> def f6(x: *b): pass | ||
Traceback (most recent call last): | ||
... | ||
SyntaxError: invalid syntax | ||
>>> x: *b | ||
Traceback (most recent call last): | ||
... | ||
SyntaxError: invalid syntax | ||
|
||
""" | ||
|
||
__test__ = {'doctests' : doctests} | ||
|
||
def test_main(verbose=False): | ||
from test import support | ||
from test import test_pep646_syntax | ||
support.run_doctest(test_pep646_syntax, verbose) | ||
|
||
if __name__ == "__main__": | ||
test_main(verbose=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not join these to
param_no_default
? That way we can remove the extra rule instar_etc
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, wouldn't that mess with other things that use
param_no_default
though? E.g.kwds
usesparam_no_default
, so if we joined these toparam_no_default
, wouldn't we accidentally allowdef foo(***args)
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Friendly poke :)