bpo-43224: Implement PEP 646 grammar changes (GH-31018)

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
Matthew Rahtz 2022-03-26 16:55:35 +00:00 committed by GitHub
parent 26cca8067b
commit e8e737bcf6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 4324 additions and 3396 deletions

View File

@ -309,6 +309,8 @@ star_etc[StarEtc*]:
| invalid_star_etc
| '*' 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) }
@ -333,6 +335,9 @@ kwds[arg_ty]:
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) }
@ -340,7 +345,9 @@ 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 } | invalid_default
# If statement
@ -782,7 +789,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) }
slice[expr_ty]:
| a=[expression] ':' b=[expression] c=[':' d=[expression] { d }] { _PyAST_Slice(a, b, c, EXTRA) }

View File

@ -1476,20 +1476,17 @@ class _Unparser(NodeVisitor):
self.traverse(e)
def visit_Subscript(self, node):
def is_simple_tuple(slice_value):
# when unparsing a non-empty tuple, the parentheses can be safely
# omitted if there aren't any elements that explicitly requires
# parentheses (such as starred expressions).
def is_non_empty_tuple(slice_value):
return (
isinstance(slice_value, Tuple)
and slice_value.elts
and not any(isinstance(elt, Starred) for elt in slice_value.elts)
)
self.set_precedence(_Precedence.ATOM, node.value)
self.traverse(node.value)
with self.delimit("[", "]"):
if is_simple_tuple(node.slice):
if is_non_empty_tuple(node.slice):
# parentheses can be omitted if the tuple isn't empty
self.items_view(self.traverse, node.slice.elts)
else:
self.traverse(node.slice)

View File

@ -13,7 +13,7 @@ from textwrap import dedent
from test import support
def to_tuple(t):
if t is None or isinstance(t, (str, int, complex)):
if t is None or isinstance(t, (str, int, complex)) or t is Ellipsis:
return t
elif isinstance(t, list):
return [to_tuple(e) for e in t]
@ -46,10 +46,20 @@ exec_tests = [
"def f(a=0): pass",
# FunctionDef with varargs
"def f(*args): pass",
# FunctionDef with varargs as TypeVarTuple
"def f(*args: *Ts): pass",
# FunctionDef with varargs as unpacked Tuple
"def f(*args: *tuple[int, ...]): pass",
# FunctionDef with varargs as unpacked Tuple *and* TypeVarTuple
"def f(*args: *tuple[int, *Ts]): pass",
# FunctionDef with kwargs
"def f(**kwargs): pass",
# FunctionDef with all kind of args and docstring
"def f(a, b=1, c=None, d=[], e={}, *args, f=42, **kwargs): 'doc for f()'",
# FunctionDef with type annotation on return involving unpacking
"def f() -> tuple[*Ts]: pass",
"def f() -> tuple[int, *Ts]: pass",
"def f() -> tuple[int, *tuple[int, ...]]: pass",
# ClassDef
"class C:pass",
# ClassDef with docstring
@ -65,6 +75,10 @@ exec_tests = [
"a,b = c",
"(a,b) = c",
"[a,b] = c",
# AnnAssign with unpacked types
"x: tuple[*Ts]",
"x: tuple[int, *Ts]",
"x: tuple[int, *tuple[str, ...]]",
# AugAssign
"v += 1",
# For
@ -2315,8 +2329,14 @@ exec_results = [
('Module', [('FunctionDef', (1, 0, 1, 14), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, []), [('Pass', (1, 10, 1, 14))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0, 1, 16), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None)], None, [], [], None, [('Constant', (1, 8, 1, 9), 0, None)]), [('Pass', (1, 12, 1, 16))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0, 1, 18), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 11), 'args', None, None), [], [], None, []), [('Pass', (1, 14, 1, 18))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0, 1, 23), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 16), 'args', ('Starred', (1, 13, 1, 16), ('Name', (1, 14, 1, 16), 'Ts', ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 19, 1, 23))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0, 1, 36), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Constant', (1, 25, 1, 28), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0, 1, 36), 'f', ('arguments', [], [], ('arg', (1, 7, 1, 29), 'args', ('Starred', (1, 13, 1, 29), ('Subscript', (1, 14, 1, 29), ('Name', (1, 14, 1, 19), 'tuple', ('Load',)), ('Tuple', (1, 20, 1, 28), [('Name', (1, 20, 1, 23), 'int', ('Load',)), ('Starred', (1, 25, 1, 28), ('Name', (1, 26, 1, 28), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), ('Load',)), None), [], [], None, []), [('Pass', (1, 32, 1, 36))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0, 1, 21), 'f', ('arguments', [], [], None, [], [], ('arg', (1, 8, 1, 14), 'kwargs', None, None), []), [('Pass', (1, 17, 1, 21))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0, 1, 71), 'f', ('arguments', [], [('arg', (1, 6, 1, 7), 'a', None, None), ('arg', (1, 9, 1, 10), 'b', None, None), ('arg', (1, 14, 1, 15), 'c', None, None), ('arg', (1, 22, 1, 23), 'd', None, None), ('arg', (1, 28, 1, 29), 'e', None, None)], ('arg', (1, 35, 1, 39), 'args', None, None), [('arg', (1, 41, 1, 42), 'f', None, None)], [('Constant', (1, 43, 1, 45), 42, None)], ('arg', (1, 49, 1, 55), 'kwargs', None, None), [('Constant', (1, 11, 1, 12), 1, None), ('Constant', (1, 16, 1, 20), None, None), ('List', (1, 24, 1, 26), [], ('Load',)), ('Dict', (1, 30, 1, 32), [], [])]), [('Expr', (1, 58, 1, 71), ('Constant', (1, 58, 1, 71), 'doc for f()', None))], [], None, None)], []),
('Module', [('FunctionDef', (1, 0, 1, 27), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 23, 1, 27))], [], ('Subscript', (1, 11, 1, 21), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 20), [('Starred', (1, 17, 1, 20), ('Name', (1, 18, 1, 20), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None)], []),
('Module', [('FunctionDef', (1, 0, 1, 32), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 28, 1, 32))], [], ('Subscript', (1, 11, 1, 26), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 25), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 25), ('Name', (1, 23, 1, 25), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None)], []),
('Module', [('FunctionDef', (1, 0, 1, 45), 'f', ('arguments', [], [], None, [], [], None, []), [('Pass', (1, 41, 1, 45))], [], ('Subscript', (1, 11, 1, 39), ('Name', (1, 11, 1, 16), 'tuple', ('Load',)), ('Tuple', (1, 17, 1, 38), [('Name', (1, 17, 1, 20), 'int', ('Load',)), ('Starred', (1, 22, 1, 38), ('Subscript', (1, 23, 1, 38), ('Name', (1, 23, 1, 28), 'tuple', ('Load',)), ('Tuple', (1, 29, 1, 37), [('Name', (1, 29, 1, 32), 'int', ('Load',)), ('Constant', (1, 34, 1, 37), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',))], ('Load',)), ('Load',)), None)], []),
('Module', [('ClassDef', (1, 0, 1, 12), 'C', [], [], [('Pass', (1, 8, 1, 12))], [])], []),
('Module', [('ClassDef', (1, 0, 1, 32), 'C', [], [], [('Expr', (1, 9, 1, 32), ('Constant', (1, 9, 1, 32), 'docstring for class C', None))], [])], []),
('Module', [('ClassDef', (1, 0, 1, 21), 'C', [('Name', (1, 8, 1, 14), 'object', ('Load',))], [], [('Pass', (1, 17, 1, 21))], [])], []),
@ -2326,6 +2346,9 @@ exec_results = [
('Module', [('Assign', (1, 0, 1, 7), [('Tuple', (1, 0, 1, 3), [('Name', (1, 0, 1, 1), 'a', ('Store',)), ('Name', (1, 2, 1, 3), 'b', ('Store',))], ('Store',))], ('Name', (1, 6, 1, 7), 'c', ('Load',)), None)], []),
('Module', [('Assign', (1, 0, 1, 9), [('Tuple', (1, 0, 1, 5), [('Name', (1, 1, 1, 2), 'a', ('Store',)), ('Name', (1, 3, 1, 4), 'b', ('Store',))], ('Store',))], ('Name', (1, 8, 1, 9), 'c', ('Load',)), None)], []),
('Module', [('Assign', (1, 0, 1, 9), [('List', (1, 0, 1, 5), [('Name', (1, 1, 1, 2), 'a', ('Store',)), ('Name', (1, 3, 1, 4), 'b', ('Store',))], ('Store',))], ('Name', (1, 8, 1, 9), 'c', ('Load',)), None)], []),
('Module', [('AnnAssign', (1, 0, 1, 13), ('Name', (1, 0, 1, 1), 'x', ('Store',)), ('Subscript', (1, 3, 1, 13), ('Name', (1, 3, 1, 8), 'tuple', ('Load',)), ('Tuple', (1, 9, 1, 12), [('Starred', (1, 9, 1, 12), ('Name', (1, 10, 1, 12), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, 1)], []),
('Module', [('AnnAssign', (1, 0, 1, 18), ('Name', (1, 0, 1, 1), 'x', ('Store',)), ('Subscript', (1, 3, 1, 18), ('Name', (1, 3, 1, 8), 'tuple', ('Load',)), ('Tuple', (1, 9, 1, 17), [('Name', (1, 9, 1, 12), 'int', ('Load',)), ('Starred', (1, 14, 1, 17), ('Name', (1, 15, 1, 17), 'Ts', ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, 1)], []),
('Module', [('AnnAssign', (1, 0, 1, 31), ('Name', (1, 0, 1, 1), 'x', ('Store',)), ('Subscript', (1, 3, 1, 31), ('Name', (1, 3, 1, 8), 'tuple', ('Load',)), ('Tuple', (1, 9, 1, 30), [('Name', (1, 9, 1, 12), 'int', ('Load',)), ('Starred', (1, 14, 1, 30), ('Subscript', (1, 15, 1, 30), ('Name', (1, 15, 1, 20), 'tuple', ('Load',)), ('Tuple', (1, 21, 1, 29), [('Name', (1, 21, 1, 24), 'str', ('Load',)), ('Constant', (1, 26, 1, 29), Ellipsis, None)], ('Load',)), ('Load',)), ('Load',))], ('Load',)), ('Load',)), None, 1)], []),
('Module', [('AugAssign', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'v', ('Store',)), ('Add',), ('Constant', (1, 5, 1, 6), 1, None))], []),
('Module', [('For', (1, 0, 1, 15), ('Name', (1, 4, 1, 5), 'v', ('Store',)), ('Name', (1, 9, 1, 10), 'v', ('Load',)), [('Pass', (1, 11, 1, 15))], [], None)], []),
('Module', [('While', (1, 0, 1, 12), ('Name', (1, 6, 1, 7), 'v', ('Load',)), [('Pass', (1, 8, 1, 12))], [])], []),

View File

@ -175,7 +175,7 @@ class AnnotationsFutureTestCase(unittest.TestCase):
scope = {}
exec(
"from __future__ import annotations\n"
+ code, {}, scope
+ code, scope
)
return scope
@ -287,10 +287,11 @@ class AnnotationsFutureTestCase(unittest.TestCase):
eq("list[str]")
eq("dict[str, int]")
eq("set[str,]")
eq("tuple[()]")
eq("tuple[str, ...]")
eq("tuple[(str, *types)]")
eq("tuple[str, *types]")
eq("tuple[str, int, (str, int)]")
eq("tuple[(*int, str, str, (str, int))]")
eq("tuple[*int, str, str, (str, int)]")
eq("tuple[str, int, float, dict[str, int]]")
eq("slice[0]")
eq("slice[0:1]")
@ -305,6 +306,21 @@ class AnnotationsFutureTestCase(unittest.TestCase):
eq("slice[1:2, 1]")
eq("slice[1:2, 2, 3]")
eq("slice[()]")
# Note that `slice[*Ts]`, `slice[*Ts,]`, and `slice[(*Ts,)]` all have
# the same AST, but only `slice[*Ts,]` passes this test, because that's
# what the unparser produces.
eq("slice[*Ts,]")
eq("slice[1, *Ts]")
eq("slice[*Ts, 2]")
eq("slice[1, *Ts, 2]")
eq("slice[*Ts, *Ts]")
eq("slice[1, *Ts, *Ts]")
eq("slice[*Ts, 1, *Ts]")
eq("slice[*Ts, *Ts, 1]")
eq("slice[1, *Ts, *Ts, 2]")
eq("slice[1:2, *Ts]")
eq("slice[*Ts, 1:2]")
eq("slice[1:2, *Ts, 3:4]")
eq("slice[a, b:c, d:e:f]")
eq("slice[(x for x in a)]")
eq('str or None if sys.version_info[0] > (3,) else str or bytes or None')
@ -403,6 +419,25 @@ class AnnotationsFutureTestCase(unittest.TestCase):
def bar(arg: (yield)): pass
"""))
def test_get_type_hints_on_func_with_variadic_arg(self):
# `typing.get_type_hints` might break on a function with a variadic
# annotation (e.g. `f(*args: *Ts)`) if `from __future__ import
# annotations`, because it could try to evaluate `*Ts` as an expression,
# which on its own isn't value syntax.
namespace = self._exec_future(dedent("""\
class StarredC: pass
class C:
def __iter__(self):
yield StarredC()
c = C()
def f(*args: *c): pass
import typing
hints = typing.get_type_hints(f)
"""))
hints = namespace.pop('hints')
self.assertIsInstance(hints['args'], namespace['StarredC'])
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,326 @@
doctests = """
Setup
>>> class AClass:
... def __init__(self):
... self._setitem_name = None
... self._setitem_val = None
... self._delitem_name = None
... def __setitem__(self, name, val):
... self._delitem_name = None
... self._setitem_name = name
... self._setitem_val = val
... def __repr__(self):
... if self._setitem_name is not None:
... return f"A[{self._setitem_name}]={self._setitem_val}"
... elif self._delitem_name is not None:
... return f"delA[{self._delitem_name}]"
... def __getitem__(self, name):
... return ParameterisedA(name)
... def __delitem__(self, name):
... self._setitem_name = None
... self._delitem_name = name
...
>>> class ParameterisedA:
... def __init__(self, name):
... self._name = name
... def __repr__(self):
... return f"A[{self._name}]"
... def __iter__(self):
... for p in self._name:
... yield p
>>> class B:
... def __iter__(self):
... yield StarredB()
... def __repr__(self):
... return "B"
>>> class StarredB:
... def __repr__(self):
... return "StarredB"
>>> A = AClass()
>>> b = B()
Slices that are supposed to work, starring our custom B class
>>> A[*b]
A[(StarredB,)]
>>> A[*b] = 1; A
A[(StarredB,)]=1
>>> del A[*b]; A
delA[(StarredB,)]
>>> A[*b, *b]
A[(StarredB, StarredB)]
>>> A[*b, *b] = 1; A
A[(StarredB, StarredB)]=1
>>> del A[*b, *b]; A
delA[(StarredB, StarredB)]
>>> A[b, *b]
A[(B, StarredB)]
>>> A[b, *b] = 1; A
A[(B, StarredB)]=1
>>> del A[b, *b]; A
delA[(B, StarredB)]
>>> A[*b, b]
A[(StarredB, B)]
>>> A[*b, b] = 1; A
A[(StarredB, B)]=1
>>> del A[*b, b]; A
delA[(StarredB, B)]
>>> A[b, b, *b]
A[(B, B, StarredB)]
>>> A[b, b, *b] = 1; A
A[(B, B, StarredB)]=1
>>> del A[b, b, *b]; A
delA[(B, B, StarredB)]
>>> A[*b, b, b]
A[(StarredB, B, B)]
>>> A[*b, b, b] = 1; A
A[(StarredB, B, B)]=1
>>> del A[*b, b, b]; A
delA[(StarredB, B, B)]
>>> A[b, *b, b]
A[(B, StarredB, B)]
>>> A[b, *b, b] = 1; A
A[(B, StarredB, B)]=1
>>> del A[b, *b, b]; A
delA[(B, StarredB, B)]
>>> A[b, b, *b, b]
A[(B, B, StarredB, B)]
>>> A[b, b, *b, b] = 1; A
A[(B, B, StarredB, B)]=1
>>> del A[b, b, *b, b]; A
delA[(B, B, StarredB, B)]
>>> A[b, *b, b, b]
A[(B, StarredB, B, B)]
>>> A[b, *b, b, b] = 1; A
A[(B, StarredB, B, B)]=1
>>> del A[b, *b, b, b]; A
delA[(B, StarredB, B, B)]
>>> A[A[b, *b, b]]
A[A[(B, StarredB, B)]]
>>> A[A[b, *b, b]] = 1; A
A[A[(B, StarredB, B)]]=1
>>> del A[A[b, *b, b]]; A
delA[A[(B, StarredB, B)]]
>>> A[*A[b, *b, b]]
A[(B, StarredB, B)]
>>> A[*A[b, *b, b]] = 1; A
A[(B, StarredB, B)]=1
>>> del A[*A[b, *b, b]]; A
delA[(B, StarredB, B)]
>>> A[b, ...]
A[(B, Ellipsis)]
>>> A[b, ...] = 1; A
A[(B, Ellipsis)]=1
>>> del A[b, ...]; A
delA[(B, Ellipsis)]
>>> A[*A[b, ...]]
A[(B, Ellipsis)]
>>> A[*A[b, ...]] = 1; A
A[(B, Ellipsis)]=1
>>> del A[*A[b, ...]]; A
delA[(B, Ellipsis)]
Slices that are supposed to work, starring a list
>>> l = [1, 2, 3]
>>> A[*l]
A[(1, 2, 3)]
>>> A[*l] = 1; A
A[(1, 2, 3)]=1
>>> del A[*l]; A
delA[(1, 2, 3)]
>>> A[*l, 4]
A[(1, 2, 3, 4)]
>>> A[*l, 4] = 1; A
A[(1, 2, 3, 4)]=1
>>> del A[*l, 4]; A
delA[(1, 2, 3, 4)]
>>> A[0, *l]
A[(0, 1, 2, 3)]
>>> A[0, *l] = 1; A
A[(0, 1, 2, 3)]=1
>>> del A[0, *l]; A
delA[(0, 1, 2, 3)]
>>> A[1:2, *l]
A[(slice(1, 2, None), 1, 2, 3)]
>>> A[1:2, *l] = 1; A
A[(slice(1, 2, None), 1, 2, 3)]=1
>>> del A[1:2, *l]; A
delA[(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] = 1; A
A[(1, 2, 3)]=1
>>> del A[*t]; A
delA[(1, 2, 3)]
>>> A[*t, 4]
A[(1, 2, 3, 4)]
>>> A[*t, 4] = 1; A
A[(1, 2, 3, 4)]=1
>>> del A[*t, 4]; A
delA[(1, 2, 3, 4)]
>>> A[0, *t]
A[(0, 1, 2, 3)]
>>> A[0, *t] = 1; A
A[(0, 1, 2, 3)]=1
>>> del A[0, *t]; A
delA[(0, 1, 2, 3)]
>>> A[1:2, *t]
A[(slice(1, 2, None), 1, 2, 3)]
>>> A[1:2, *t] = 1; A
A[(slice(1, 2, None), 1, 2, 3)]=1
>>> del A[1:2, *t]; A
delA[(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()] = 1; A
A[[1, 2, 3]]=1
>>> del A[returns_list()]; A
delA[[1, 2, 3]]
>>> A[returns_list(), 4]
A[([1, 2, 3], 4)]
>>> A[returns_list(), 4] = 1; A
A[([1, 2, 3], 4)]=1
>>> del A[returns_list(), 4]; A
delA[([1, 2, 3], 4)]
>>> A[*returns_list()]
A[(1, 2, 3)]
>>> A[*returns_list()] = 1; A
A[(1, 2, 3)]=1
>>> del A[*returns_list()]; A
delA[(1, 2, 3)]
>>> A[*returns_list(), 4]
A[(1, 2, 3, 4)]
>>> A[*returns_list(), 4] = 1; A
A[(1, 2, 3, 4)]=1
>>> del A[*returns_list(), 4]; A
delA[(1, 2, 3, 4)]
>>> A[0, *returns_list()]
A[(0, 1, 2, 3)]
>>> A[0, *returns_list()] = 1; A
A[(0, 1, 2, 3)]=1
>>> del A[0, *returns_list()]; A
delA[(0, 1, 2, 3)]
>>> A[*returns_list(), *returns_list()]
A[(1, 2, 3, 1, 2, 3)]
>>> A[*returns_list(), *returns_list()] = 1; A
A[(1, 2, 3, 1, 2, 3)]=1
>>> del A[*returns_list(), *returns_list()]; A
delA[(1, 2, 3, 1, 2, 3)]
Using both a starred object and a start:stop in a slice
(See also tests in test_syntax confirming that starring *inside* a start:stop
is *not* valid syntax.)
>>> A[1:2, *b]
A[(slice(1, 2, None), StarredB)]
>>> A[*b, 1:2]
A[(StarredB, slice(1, 2, None))]
>>> A[1:2, *b, 1:2]
A[(slice(1, 2, None), StarredB, slice(1, 2, None))]
>>> A[*b, 1:2, *b]
A[(StarredB, slice(1, 2, None), StarredB)]
>>> A[1:, *b]
A[(slice(1, None, None), StarredB)]
>>> A[*b, 1:]
A[(StarredB, slice(1, None, None))]
>>> A[1:, *b, 1:]
A[(slice(1, None, None), StarredB, slice(1, None, None))]
>>> A[*b, 1:, *b]
A[(StarredB, slice(1, None, None), StarredB)]
>>> A[:1, *b]
A[(slice(None, 1, None), StarredB)]
>>> A[*b, :1]
A[(StarredB, slice(None, 1, None))]
>>> A[:1, *b, :1]
A[(slice(None, 1, None), StarredB, slice(None, 1, None))]
>>> A[*b, :1, *b]
A[(StarredB, slice(None, 1, None), StarredB)]
>>> A[:, *b]
A[(slice(None, None, None), StarredB)]
>>> A[*b, :]
A[(StarredB, slice(None, None, None))]
>>> A[:, *b, :]
A[(slice(None, None, None), StarredB, slice(None, None, None))]
>>> A[*b, :, *b]
A[(StarredB, slice(None, None, None), StarredB)]
*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'>}
>>> def f5(*args: *b = (1,)): pass
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)

View File

@ -1622,6 +1622,149 @@ Corner-cases that used to crash:
... ...
Traceback (most recent call last):
SyntaxError: positional patterns follow keyword patterns
Uses of the star operator which should fail:
A[:*b]
>>> A[:*b]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> A[:(*b)]
Traceback (most recent call last):
...
SyntaxError: cannot use starred expression here
>>> A[:*b] = 1
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> del A[:*b]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
A[*b:]
>>> A[*b:]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> A[(*b):]
Traceback (most recent call last):
...
SyntaxError: cannot use starred expression here
>>> A[*b:] = 1
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> del A[*b:]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
A[*b:*b]
>>> A[*b:*b]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> A[(*b:*b)]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> A[*b:*b] = 1
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> del A[*b:*b]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
A[*(1:2)]
>>> A[*(1:2)]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> A[*(1:2)] = 1
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> del A[*(1:2)]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
A[*:] and A[:*]
>>> A[*:]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> A[:*]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
A[*]
>>> A[*]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
A[**]
>>> A[**]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
A[**b]
>>> A[**b]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> A[**b] = 1
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> del A[**b]
Traceback (most recent call last):
...
SyntaxError: invalid syntax
def f(x: *b)
>>> def f6(x: *b): pass
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> def f7(x: *b = 1): pass
Traceback (most recent call last):
...
SyntaxError: invalid syntax
**kwargs: *a
>>> def f8(**kwargs: *a): pass
Traceback (most recent call last):
...
SyntaxError: invalid syntax
x: *b
>>> x: *b
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> x: *b = 1
Traceback (most recent call last):
...
SyntaxError: invalid syntax
"""
import re

View File

@ -344,7 +344,17 @@ class UnparseTestCase(ASTTestCase):
self.check_ast_roundtrip("a[i]")
self.check_ast_roundtrip("a[i,]")
self.check_ast_roundtrip("a[i, j]")
# The AST for these next two both look like `a[(*a,)]`
self.check_ast_roundtrip("a[(*a,)]")
self.check_ast_roundtrip("a[*a]")
self.check_ast_roundtrip("a[b, *a]")
self.check_ast_roundtrip("a[*a, c]")
self.check_ast_roundtrip("a[b, *a, c]")
self.check_ast_roundtrip("a[*a, *a]")
self.check_ast_roundtrip("a[b, *a, *a]")
self.check_ast_roundtrip("a[*a, b, *a]")
self.check_ast_roundtrip("a[*a, *a, b]")
self.check_ast_roundtrip("a[b, *a, *a, c]")
self.check_ast_roundtrip("a[(a:=b)]")
self.check_ast_roundtrip("a[(a:=b,c)]")
self.check_ast_roundtrip("a[()]")
@ -543,9 +553,23 @@ class CosmeticTestCase(ASTTestCase):
self.check_src_roundtrip(f"{prefix} 1")
def test_slices(self):
self.check_src_roundtrip("a[()]")
self.check_src_roundtrip("a[1]")
self.check_src_roundtrip("a[1, 2]")
self.check_src_roundtrip("a[(1, *a)]")
# Note that `a[*a]`, `a[*a,]`, and `a[(*a,)]` all evaluate to the same
# thing at runtime and have the same AST, but only `a[*a,]` passes
# this test, because that's what `ast.unparse` produces.
self.check_src_roundtrip("a[*a,]")
self.check_src_roundtrip("a[1, *a]")
self.check_src_roundtrip("a[*a, 2]")
self.check_src_roundtrip("a[1, *a, 2]")
self.check_src_roundtrip("a[*a, *a]")
self.check_src_roundtrip("a[1, *a, *a]")
self.check_src_roundtrip("a[*a, 1, *a]")
self.check_src_roundtrip("a[*a, *a, 1]")
self.check_src_roundtrip("a[1, *a, *a, 2]")
self.check_src_roundtrip("a[1:2, *a]")
self.check_src_roundtrip("a[*a, 1:2]")
def test_lambda_parameters(self):
self.check_src_roundtrip("lambda: something")

View File

@ -734,10 +734,19 @@ class ForwardRef(_Final, _root=True):
def __init__(self, arg, is_argument=True, module=None, *, is_class=False):
if not isinstance(arg, str):
raise TypeError(f"Forward reference must be a string -- got {arg!r}")
# If we do `def f(*args: *Ts)`, then we'll have `arg = '*Ts'`.
# Unfortunately, this isn't a valid expression on its own, so we
# do the unpacking manually.
if arg[0] == '*':
arg_to_compile = f'({arg},)[0]' # E.g. (*Ts,)[0]
else:
arg_to_compile = arg
try:
code = compile(arg, '<string>', 'eval')
code = compile(arg_to_compile, '<string>', 'eval')
except SyntaxError:
raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}")
self.__forward_arg__ = arg
self.__forward_code__ = code
self.__forward_evaluated__ = False

View File

@ -0,0 +1 @@
Make grammar changes required for PEP 646.

7102
Parser/parser.c generated

File diff suppressed because it is too large Load Diff

View File

@ -786,19 +786,8 @@ static int
append_ast_subscript(_PyUnicodeWriter *writer, expr_ty e)
{
APPEND_EXPR(e->v.Subscript.value, PR_ATOM);
int level = PR_TUPLE;
expr_ty slice = e->v.Subscript.slice;
if (slice->kind == Tuple_kind) {
for (Py_ssize_t i = 0; i < asdl_seq_LEN(slice->v.Tuple.elts); i++) {
expr_ty element = asdl_seq_GET(slice->v.Tuple.elts, i);
if (element->kind == Starred_kind) {
++level;
break;
}
}
}
APPEND_STR("[");
APPEND_EXPR(e->v.Subscript.slice, level);
APPEND_EXPR(e->v.Subscript.slice, PR_TUPLE);
APPEND_STR_FINISH("]");
}

View File

@ -2335,10 +2335,20 @@ compiler_visit_argannotation(struct compiler *c, identifier id,
Py_DECREF(mangled);
if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) {
VISIT(c, annexpr, annotation)
VISIT(c, annexpr, annotation);
}
else {
VISIT(c, expr, annotation);
if (annotation->kind == Starred_kind) {
// *args: *Ts (where Ts is a TypeVarTuple).
// Do [annotation_value] = [*Ts].
// (Note that in theory we could end up here even for an argument
// other than *args, but in practice the grammar doesn't allow it.)
VISIT(c, expr, annotation->v.Starred.value);
ADDOP_I(c, UNPACK_SEQUENCE, (Py_ssize_t) 1);
}
else {
VISIT(c, expr, annotation);
}
}
*annotations_len += 2;
return 1;