Implement support for PEP 646 (#3071)

This commit is contained in:
Batuhan Taskaya 2022-05-26 19:45:22 +03:00 committed by GitHub
parent 9fe788d870
commit 1e557184b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 233 additions and 6 deletions

View File

@ -56,6 +56,8 @@
- [PEP 654](https://peps.python.org/pep-0654/#except) syntax (for example, - [PEP 654](https://peps.python.org/pep-0654/#except) syntax (for example,
`except *ExceptionGroup:`) is now supported (#3016) `except *ExceptionGroup:`) is now supported (#3016)
- [PEP 646](https://peps.python.org/pep-0646) syntax (for example,
`Array[Batch, *Shape]` or `def fn(*args: *T) -> None`) is now supported (#3071)
<!-- Changes to the parser or to version autodetection --> <!-- Changes to the parser or to version autodetection -->

View File

@ -1308,6 +1308,18 @@ def get_features_used( # noqa: C901
): ):
features.add(Feature.EXCEPT_STAR) features.add(Feature.EXCEPT_STAR)
elif n.type in {syms.subscriptlist, syms.trailer} and any(
child.type == syms.star_expr for child in n.children
):
features.add(Feature.VARIADIC_GENERICS)
elif (
n.type == syms.tname_star
and len(n.children) == 3
and n.children[2].type == syms.star_expr
):
features.add(Feature.VARIADIC_GENERICS)
return features return features

View File

@ -49,6 +49,7 @@ class Feature(Enum):
UNPACKING_ON_FLOW = 12 UNPACKING_ON_FLOW = 12
ANN_ASSIGN_EXTENDED_RHS = 13 ANN_ASSIGN_EXTENDED_RHS = 13
EXCEPT_STAR = 14 EXCEPT_STAR = 14
VARIADIC_GENERICS = 15
FORCE_OPTIONAL_PARENTHESES = 50 FORCE_OPTIONAL_PARENTHESES = 50
# __future__ flags # __future__ flags
@ -132,6 +133,7 @@ class Feature(Enum):
Feature.ANN_ASSIGN_EXTENDED_RHS, Feature.ANN_ASSIGN_EXTENDED_RHS,
Feature.PATTERN_MATCHING, Feature.PATTERN_MATCHING,
Feature.EXCEPT_STAR, Feature.EXCEPT_STAR,
Feature.VARIADIC_GENERICS,
}, },
} }

View File

@ -120,6 +120,7 @@
syms.term, syms.term,
syms.power, syms.power,
} }
TYPED_NAMES: Final = {syms.tname, syms.tname_star}
ASSIGNMENTS: Final = { ASSIGNMENTS: Final = {
"=", "=",
"+=", "+=",
@ -243,6 +244,14 @@ def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa: C901
# that, too. # that, too.
return prevp.prefix return prevp.prefix
elif (
prevp.type == token.STAR
and parent_type(prevp) == syms.star_expr
and parent_type(prevp.parent) == syms.subscriptlist
):
# No space between typevar tuples.
return NO
elif prevp.type in VARARGS_SPECIALS: elif prevp.type in VARARGS_SPECIALS:
if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS): if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS):
return NO return NO
@ -281,7 +290,7 @@ def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa: C901
return NO return NO
if t == token.EQUAL: if t == token.EQUAL:
if prev.type != syms.tname: if prev.type not in TYPED_NAMES:
return NO return NO
elif prev.type == token.EQUAL: elif prev.type == token.EQUAL:
@ -292,7 +301,7 @@ def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa: C901
elif prev.type != token.COMMA: elif prev.type != token.COMMA:
return NO return NO
elif p.type == syms.tname: elif p.type in TYPED_NAMES:
# type names # type names
if not prev: if not prev:
prevp = preceding_leaf(p) prevp = preceding_leaf(p)

View File

@ -24,7 +24,7 @@ parameters: '(' [typedargslist] ')'
# arguments = argument (',' argument)* # arguments = argument (',' argument)*
# argument = tfpdef ['=' test] # argument = tfpdef ['=' test]
# kwargs = '**' tname [','] # kwargs = '**' tname [',']
# args = '*' [tname] # args = '*' [tname_star]
# kwonly_kwargs = (',' argument)* [',' [kwargs]] # kwonly_kwargs = (',' argument)* [',' [kwargs]]
# args_kwonly_kwargs = args kwonly_kwargs | kwargs # args_kwonly_kwargs = args kwonly_kwargs | kwargs
# poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]] # poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]]
@ -34,14 +34,15 @@ parameters: '(' [typedargslist] ')'
# It needs to be fully expanded to allow our LL(1) parser to work on it. # It needs to be fully expanded to allow our LL(1) parser to work on it.
typedargslist: tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [ typedargslist: tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [
',' [((tfpdef ['=' test] ',')* ('*' [tname] (',' tname ['=' test])* ',' [((tfpdef ['=' test] ',')* ('*' [tname_star] (',' tname ['=' test])*
[',' ['**' tname [',']]] | '**' tname [',']) [',' ['**' tname [',']]] | '**' tname [','])
| tfpdef ['=' test] (',' tfpdef ['=' test])* [','])] | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])]
] | ((tfpdef ['=' test] ',')* ('*' [tname] (',' tname ['=' test])* ] | ((tfpdef ['=' test] ',')* ('*' [tname_star] (',' tname ['=' test])*
[',' ['**' tname [',']]] | '**' tname [',']) [',' ['**' tname [',']]] | '**' tname [','])
| tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
tname: NAME [':' test] tname: NAME [':' test]
tname_star: NAME [':' (test|star_expr)]
tfpdef: tname | '(' tfplist ')' tfpdef: tname | '(' tfplist ')'
tfplist: tfpdef (',' tfpdef)* [','] tfplist: tfpdef (',' tfpdef)* [',']
@ -163,7 +164,7 @@ listmaker: (namedexpr_test|star_expr) ( old_comp_for | (',' (namedexpr_test|star
testlist_gexp: (namedexpr_test|star_expr) ( old_comp_for | (',' (namedexpr_test|star_expr))* [','] ) testlist_gexp: (namedexpr_test|star_expr) ( old_comp_for | (',' (namedexpr_test|star_expr))* [','] )
lambdef: 'lambda' [varargslist] ':' test lambdef: 'lambda' [varargslist] ':' test
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
subscriptlist: subscript (',' subscript)* [','] subscriptlist: (subscript|star_expr) (',' (subscript|star_expr))* [',']
subscript: test [':=' test] | [test] ':' [test] [sliceop] subscript: test [':=' test] | [test] ':' [test] [sliceop]
sliceop: ':' [test] sliceop: ':' [test]
exprlist: (expr|star_expr) (',' (expr|star_expr))* [','] exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']

View File

@ -123,6 +123,7 @@ class _python_symbols(Symbols):
tfpdef: int tfpdef: int
tfplist: int tfplist: int
tname: int tname: int
tname_star: int
trailer: int trailer: int
try_stmt: int try_stmt: int
typedargslist: int typedargslist: int

View File

@ -0,0 +1,194 @@
A[*b]
A[*b] = 1
A
del A[*b]
A
A[*b, *b]
A[*b, *b] = 1
A
del A[*b, *b]
A
A[b, *b]
A[b, *b] = 1
A
del A[b, *b]
A
A[*b, b]
A[*b, b] = 1
A
del A[*b, b]
A
A[b, b, *b]
A[b, b, *b] = 1
A
del A[b, b, *b]
A
A[*b, b, b]
A[*b, b, b] = 1
A
del A[*b, b, b]
A
A[b, *b, b]
A[b, *b, b] = 1
A
del A[b, *b, b]
A
A[b, b, *b, b]
A[b, b, *b, b] = 1
A
del A[b, b, *b, b]
A
A[b, *b, b, b]
A[b, *b, b, b] = 1
A
del A[b, *b, b, b]
A
A[A[b, *b, b]]
A[A[b, *b, b]] = 1
A
del A[A[b, *b, b]]
A
A[*A[b, *b, b]]
A[*A[b, *b, b]] = 1
A
del A[*A[b, *b, b]]
A
A[b, ...]
A[b, ...] = 1
A
del A[b, ...]
A
A[*A[b, ...]]
A[*A[b, ...]] = 1
A
del A[*A[b, ...]]
A
l = [1, 2, 3]
A[*l]
A[*l] = 1
A
del A[*l]
A
A[*l, 4]
A[*l, 4] = 1
A
del A[*l, 4]
A
A[0, *l]
A[0, *l] = 1
A
del A[0, *l]
A
A[1:2, *l]
A[1:2, *l] = 1
A
del A[1:2, *l]
A
repr(A[1:2, *l]) == repr(A[1:2, 1, 2, 3])
t = (1, 2, 3)
A[*t]
A[*t] = 1
A
del A[*t]
A
A[*t, 4]
A[*t, 4] = 1
A
del A[*t, 4]
A
A[0, *t]
A[0, *t] = 1
A
del A[0, *t]
A
A[1:2, *t]
A[1:2, *t] = 1
A
del A[1:2, *t]
A
repr(A[1:2, *t]) == repr(A[1:2, 1, 2, 3])
def returns_list():
return [1, 2, 3]
A[returns_list()]
A[returns_list()] = 1
A
del A[returns_list()]
A
A[returns_list(), 4]
A[returns_list(), 4] = 1
A
del A[returns_list(), 4]
A
A[*returns_list()]
A[*returns_list()] = 1
A
del A[*returns_list()]
A
A[*returns_list(), 4]
A[*returns_list(), 4] = 1
A
del A[*returns_list(), 4]
A
A[0, *returns_list()]
A[0, *returns_list()] = 1
A
del A[0, *returns_list()]
A
A[*returns_list(), *returns_list()]
A[*returns_list(), *returns_list()] = 1
A
del A[*returns_list(), *returns_list()]
A
A[1:2, *b]
A[*b, 1:2]
A[1:2, *b, 1:2]
A[*b, 1:2, *b]
A[1:, *b]
A[*b, 1:]
A[1:, *b, 1:]
A[*b, 1:, *b]
A[:1, *b]
A[*b, :1]
A[:1, *b, :1]
A[*b, :1, *b]
A[:, *b]
A[*b, :]
A[:, *b, :]
A[*b, :, *b]
A[a * b()]
A[a * b(), *c, *d(), e * f(g * h)]
A[a * b(), :]
A[a * b(), *c, *d(), e * f(g * h) :]
A[[b] * len(c), :]
def f1(*args: *b):
pass
f1.__annotations__
def f2(*args: *b, arg1):
pass
f2.__annotations__
def f3(*args: *b, arg1: int):
pass
f3.__annotations__
def f4(*args: *b, arg1: int = 2):
pass
f4.__annotations__

View File

@ -804,6 +804,12 @@ def test_get_features_used(self) -> None:
self.assertEqual(black.get_features_used(node), set()) self.assertEqual(black.get_features_used(node), set())
node = black.lib2to3_parse("try: pass\nexcept *Group: pass") node = black.lib2to3_parse("try: pass\nexcept *Group: pass")
self.assertEqual(black.get_features_used(node), {Feature.EXCEPT_STAR}) self.assertEqual(black.get_features_used(node), {Feature.EXCEPT_STAR})
node = black.lib2to3_parse("a[*b]")
self.assertEqual(black.get_features_used(node), {Feature.VARIADIC_GENERICS})
node = black.lib2to3_parse("a[x, *y(), z] = t")
self.assertEqual(black.get_features_used(node), {Feature.VARIADIC_GENERICS})
node = black.lib2to3_parse("def fn(*args: *T): pass")
self.assertEqual(black.get_features_used(node), {Feature.VARIADIC_GENERICS})
def test_get_features_used_for_future_flags(self) -> None: def test_get_features_used_for_future_flags(self) -> None:
for src, features in [ for src, features in [