mirror of https://github.com/python/cpython
bpo-43224: Implement PEP 646 grammar changes (GH-31018)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
26cca8067b
commit
e8e737bcf6
|
@ -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) }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))], [])], []),
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Make grammar changes required for PEP 646.
|
File diff suppressed because it is too large
Load Diff
|
@ -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("]");
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue