Skip to content

Commit 025e9eb

Browse files
committed
PEP 448: additional unpacking generalizations (closes #2292)
Patch by Neil Girdhar.
1 parent 4ccc151 commit 025e9eb

26 files changed

+2662
-2116
lines changed

Grammar/Grammar

+18-6
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,29 @@ subscript: test | [test] ':' [test] [sliceop]
111111
sliceop: ':' [test]
112112
exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
113113
testlist: test (',' test)* [',']
114-
dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
115-
(test (comp_for | (',' test)* [','])) )
114+
dictorsetmaker: ( ((test ':' test | '**' expr)
115+
(comp_for | (',' (test ':' test | '**' expr))* [','])) |
116+
((test | star_expr)
117+
(comp_for | (',' (test | star_expr))* [','])) )
116118

117119
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
118120

119-
arglist: (argument ',')* (argument [',']
120-
|'*' test (',' argument)* [',' '**' test]
121-
|'**' test)
121+
arglist: argument (',' argument)* [',']
122+
122123
# The reason that keywords are test nodes instead of NAME is that using NAME
123124
# results in an ambiguity. ast.c makes sure it's a NAME.
124-
argument: test [comp_for] | test '=' test # Really [keyword '='] test
125+
# "test '=' test" is really "keyword '=' test", but we have no such token.
126+
# These need to be in a single rule to avoid grammar that is ambiguous
127+
# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr,
128+
# we explicitly match '*' here, too, to give it proper precedence.
129+
# Illegal combinations and orderings are blocked in ast.c:
130+
# multiple (test comp_for) arguements are blocked; keyword unpackings
131+
# that precede iterable unpackings are blocked; etc.
132+
argument: ( test [comp_for] |
133+
test '=' test |
134+
'**' expr |
135+
star_expr )
136+
125137
comp_iter: comp_for | comp_if
126138
comp_for: 'for' exprlist 'in' or_test [comp_iter]
127139
comp_if: 'if' test_nocond [comp_iter]

Include/Python-ast.h

+6-12
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,6 @@ struct _stmt {
8484
identifier name;
8585
asdl_seq *bases;
8686
asdl_seq *keywords;
87-
expr_ty starargs;
88-
expr_ty kwargs;
8987
asdl_seq *body;
9088
asdl_seq *decorator_list;
9189
} ClassDef;
@@ -263,8 +261,6 @@ struct _expr {
263261
expr_ty func;
264262
asdl_seq *args;
265263
asdl_seq *keywords;
266-
expr_ty starargs;
267-
expr_ty kwargs;
268264
} Call;
269265

270266
struct {
@@ -406,11 +402,10 @@ mod_ty _Py_Suite(asdl_seq * body, PyArena *arena);
406402
stmt_ty _Py_FunctionDef(identifier name, arguments_ty args, asdl_seq * body,
407403
asdl_seq * decorator_list, expr_ty returns, int lineno,
408404
int col_offset, PyArena *arena);
409-
#define ClassDef(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) _Py_ClassDef(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
405+
#define ClassDef(a0, a1, a2, a3, a4, a5, a6, a7) _Py_ClassDef(a0, a1, a2, a3, a4, a5, a6, a7)
410406
stmt_ty _Py_ClassDef(identifier name, asdl_seq * bases, asdl_seq * keywords,
411-
expr_ty starargs, expr_ty kwargs, asdl_seq * body,
412-
asdl_seq * decorator_list, int lineno, int col_offset,
413-
PyArena *arena);
407+
asdl_seq * body, asdl_seq * decorator_list, int lineno,
408+
int col_offset, PyArena *arena);
414409
#define Return(a0, a1, a2, a3) _Py_Return(a0, a1, a2, a3)
415410
stmt_ty _Py_Return(expr_ty value, int lineno, int col_offset, PyArena *arena);
416411
#define Delete(a0, a1, a2, a3) _Py_Delete(a0, a1, a2, a3)
@@ -504,10 +499,9 @@ expr_ty _Py_YieldFrom(expr_ty value, int lineno, int col_offset, PyArena
504499
#define Compare(a0, a1, a2, a3, a4, a5) _Py_Compare(a0, a1, a2, a3, a4, a5)
505500
expr_ty _Py_Compare(expr_ty left, asdl_int_seq * ops, asdl_seq * comparators,
506501
int lineno, int col_offset, PyArena *arena);
507-
#define Call(a0, a1, a2, a3, a4, a5, a6, a7) _Py_Call(a0, a1, a2, a3, a4, a5, a6, a7)
508-
expr_ty _Py_Call(expr_ty func, asdl_seq * args, asdl_seq * keywords, expr_ty
509-
starargs, expr_ty kwargs, int lineno, int col_offset, PyArena
510-
*arena);
502+
#define Call(a0, a1, a2, a3, a4, a5) _Py_Call(a0, a1, a2, a3, a4, a5)
503+
expr_ty _Py_Call(expr_ty func, asdl_seq * args, asdl_seq * keywords, int
504+
lineno, int col_offset, PyArena *arena);
511505
#define Num(a0, a1, a2, a3) _Py_Num(a0, a1, a2, a3)
512506
expr_ty _Py_Num(object n, int lineno, int col_offset, PyArena *arena);
513507
#define Str(a0, a1, a2, a3) _Py_Str(a0, a1, a2, a3)

Include/dictobject.h

+4
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ PyAPI_FUNC(int) PyDict_Merge(PyObject *mp,
105105
PyObject *other,
106106
int override);
107107

108+
#ifndef Py_LIMITED_API
109+
PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other);
110+
#endif
111+
108112
/* PyDict_MergeFromSeq2 updates/merges from an iterable object producing
109113
iterable objects of length 2. If override is true, the last occurrence
110114
of a key wins, else the first. The Python dict constructor dict(seq2)

Include/opcode.h

+5
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ extern "C" {
111111
#define SET_ADD 146
112112
#define MAP_ADD 147
113113
#define LOAD_CLASSDEREF 148
114+
#define BUILD_LIST_UNPACK 149
115+
#define BUILD_MAP_UNPACK 150
116+
#define BUILD_MAP_UNPACK_WITH_CALL 151
117+
#define BUILD_TUPLE_UNPACK 152
118+
#define BUILD_SET_UNPACK 153
114119

115120
/* EXCEPT_HANDLER is a special, implicit block type which is created when
116121
entering an except handler. It is not an opcode but we define it here

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,13 @@ def _write_atomic(path, data, mode=0o666):
220220
# Python 3.4a4 3300 (more changes to __qualname__ computation)
221221
# Python 3.4rc2 3310 (alter __qualname__ computation)
222222
# Python 3.5a0 3320 (matrix multiplication operator)
223+
# Python 3.5b1 3330 (PEP 448: Additional Unpacking Generalizations)
223224
#
224225
# MAGIC must change whenever the bytecode emitted by the compiler may no
225226
# longer be understood by older implementations of the eval loop (usually
226227
# due to the addition of new opcodes).
227228

228-
MAGIC_NUMBER = (3320).to_bytes(2, 'little') + b'\r\n'
229+
MAGIC_NUMBER = (3330).to_bytes(2, 'little') + b'\r\n'
229230
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
230231

231232
_PYCACHE = '__pycache__'

Lib/opcode.py

+6
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,10 @@ def jabs_op(name, op):
200200
def_op('EXTENDED_ARG', 144)
201201
EXTENDED_ARG = 144
202202

203+
def_op('BUILD_LIST_UNPACK', 149)
204+
def_op('BUILD_MAP_UNPACK', 150)
205+
def_op('BUILD_MAP_UNPACK_WITH_CALL', 151)
206+
def_op('BUILD_TUPLE_UNPACK', 152)
207+
def_op('BUILD_SET_UNPACK', 153)
208+
203209
del def_op, name_op, jrel_op, jabs_op

Lib/test/test_ast.py

+21-35
Original file line numberDiff line numberDiff line change
@@ -427,17 +427,17 @@ def test_dump(self):
427427
self.assertEqual(ast.dump(node),
428428
"Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), "
429429
"args=[Name(id='eggs', ctx=Load()), Str(s='and cheese')], "
430-
"keywords=[], starargs=None, kwargs=None))])"
430+
"keywords=[]))])"
431431
)
432432
self.assertEqual(ast.dump(node, annotate_fields=False),
433433
"Module([Expr(Call(Name('spam', Load()), [Name('eggs', Load()), "
434-
"Str('and cheese')], [], None, None))])"
434+
"Str('and cheese')], []))])"
435435
)
436436
self.assertEqual(ast.dump(node, include_attributes=True),
437437
"Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), "
438438
"lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), "
439439
"lineno=1, col_offset=5), Str(s='and cheese', lineno=1, "
440-
"col_offset=11)], keywords=[], starargs=None, kwargs=None, "
440+
"col_offset=11)], keywords=[], "
441441
"lineno=1, col_offset=0), lineno=1, col_offset=0)])"
442442
)
443443

@@ -453,16 +453,16 @@ def test_copy_location(self):
453453
def test_fix_missing_locations(self):
454454
src = ast.parse('write("spam")')
455455
src.body.append(ast.Expr(ast.Call(ast.Name('spam', ast.Load()),
456-
[ast.Str('eggs')], [], None, None)))
456+
[ast.Str('eggs')], [])))
457457
self.assertEqual(src, ast.fix_missing_locations(src))
458458
self.assertEqual(ast.dump(src, include_attributes=True),
459459
"Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), "
460460
"lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, "
461-
"col_offset=6)], keywords=[], starargs=None, kwargs=None, "
461+
"col_offset=6)], keywords=[], "
462462
"lineno=1, col_offset=0), lineno=1, col_offset=0), "
463463
"Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, "
464464
"col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], "
465-
"keywords=[], starargs=None, kwargs=None, lineno=1, "
465+
"keywords=[], lineno=1, "
466466
"col_offset=0), lineno=1, col_offset=0)])"
467467
)
468468

@@ -487,8 +487,7 @@ def test_iter_fields(self):
487487
node = ast.parse('foo()', mode='eval')
488488
d = dict(ast.iter_fields(node.body))
489489
self.assertEqual(d.pop('func').id, 'foo')
490-
self.assertEqual(d, {'keywords': [], 'kwargs': None,
491-
'args': [], 'starargs': None})
490+
self.assertEqual(d, {'keywords': [], 'args': []})
492491

493492
def test_iter_child_nodes(self):
494493
node = ast.parse("spam(23, 42, eggs='leek')", mode='eval')
@@ -604,8 +603,7 @@ def fac(args):
604603
self._check_arguments(fac, self.stmt)
605604

606605
def test_classdef(self):
607-
def cls(bases=None, keywords=None, starargs=None, kwargs=None,
608-
body=None, decorator_list=None):
606+
def cls(bases=None, keywords=None, body=None, decorator_list=None):
609607
if bases is None:
610608
bases = []
611609
if keywords is None:
@@ -614,16 +612,12 @@ def cls(bases=None, keywords=None, starargs=None, kwargs=None,
614612
body = [ast.Pass()]
615613
if decorator_list is None:
616614
decorator_list = []
617-
return ast.ClassDef("myclass", bases, keywords, starargs,
618-
kwargs, body, decorator_list)
615+
return ast.ClassDef("myclass", bases, keywords,
616+
body, decorator_list)
619617
self.stmt(cls(bases=[ast.Name("x", ast.Store())]),
620618
"must have Load context")
621619
self.stmt(cls(keywords=[ast.keyword("x", ast.Name("x", ast.Store()))]),
622620
"must have Load context")
623-
self.stmt(cls(starargs=ast.Name("x", ast.Store())),
624-
"must have Load context")
625-
self.stmt(cls(kwargs=ast.Name("x", ast.Store())),
626-
"must have Load context")
627621
self.stmt(cls(body=[]), "empty body on ClassDef")
628622
self.stmt(cls(body=[None]), "None disallowed")
629623
self.stmt(cls(decorator_list=[ast.Name("x", ast.Store())]),
@@ -854,20 +848,12 @@ def test_call(self):
854848
func = ast.Name("x", ast.Load())
855849
args = [ast.Name("y", ast.Load())]
856850
keywords = [ast.keyword("w", ast.Name("z", ast.Load()))]
857-
stararg = ast.Name("p", ast.Load())
858-
kwarg = ast.Name("q", ast.Load())
859-
call = ast.Call(ast.Name("x", ast.Store()), args, keywords, stararg,
860-
kwarg)
851+
call = ast.Call(ast.Name("x", ast.Store()), args, keywords)
861852
self.expr(call, "must have Load context")
862-
call = ast.Call(func, [None], keywords, stararg, kwarg)
853+
call = ast.Call(func, [None], keywords)
863854
self.expr(call, "None disallowed")
864855
bad_keywords = [ast.keyword("w", ast.Name("z", ast.Store()))]
865-
call = ast.Call(func, args, bad_keywords, stararg, kwarg)
866-
self.expr(call, "must have Load context")
867-
call = ast.Call(func, args, keywords, ast.Name("z", ast.Store()), kwarg)
868-
self.expr(call, "must have Load context")
869-
call = ast.Call(func, args, keywords, stararg,
870-
ast.Name("w", ast.Store()))
856+
call = ast.Call(func, args, bad_keywords)
871857
self.expr(call, "must have Load context")
872858

873859
def test_num(self):
@@ -957,8 +943,8 @@ def main():
957943
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], ('arg', (1, 7), 'args', None), [], [], None, []), [('Pass', (1, 14))], [], None)]),
958944
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], ('arg', (1, 8), 'kwargs', None), []), [('Pass', (1, 17))], [], None)]),
959945
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None), ('arg', (1, 9), 'b', None), ('arg', (1, 14), 'c', None), ('arg', (1, 22), 'd', None), ('arg', (1, 28), 'e', None)], ('arg', (1, 35), 'args', None), [('arg', (1, 41), 'f', None)], [('Num', (1, 43), 42)], ('arg', (1, 49), 'kwargs', None), [('Num', (1, 11), 1), ('NameConstant', (1, 16), None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [('Pass', (1, 58))], [], None)]),
960-
('Module', [('ClassDef', (1, 0), 'C', [], [], None, None, [('Pass', (1, 8))], [])]),
961-
('Module', [('ClassDef', (1, 0), 'C', [('Name', (1, 8), 'object', ('Load',))], [], None, None, [('Pass', (1, 17))], [])]),
946+
('Module', [('ClassDef', (1, 0), 'C', [], [], [('Pass', (1, 8))], [])]),
947+
('Module', [('ClassDef', (1, 0), 'C', [('Name', (1, 8), 'object', ('Load',))], [], [('Pass', (1, 17))], [])]),
962948
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Return', (1, 8), ('Num', (1, 15), 1))], [], None)]),
963949
('Module', [('Delete', (1, 0), [('Name', (1, 4), 'v', ('Del',))])]),
964950
('Module', [('Assign', (1, 0), [('Name', (1, 0), 'v', ('Store',))], ('Num', (1, 4), 1))]),
@@ -968,7 +954,7 @@ def main():
968954
('Module', [('If', (1, 0), ('Name', (1, 3), 'v', ('Load',)), [('Pass', (1, 5))], [])]),
969955
('Module', [('With', (1, 0), [('withitem', ('Name', (1, 5), 'x', ('Load',)), ('Name', (1, 10), 'y', ('Store',)))], [('Pass', (1, 13))])]),
970956
('Module', [('With', (1, 0), [('withitem', ('Name', (1, 5), 'x', ('Load',)), ('Name', (1, 10), 'y', ('Store',))), ('withitem', ('Name', (1, 13), 'z', ('Load',)), ('Name', (1, 18), 'q', ('Store',)))], [('Pass', (1, 21))])]),
971-
('Module', [('Raise', (1, 0), ('Call', (1, 6), ('Name', (1, 6), 'Exception', ('Load',)), [('Str', (1, 16), 'string')], [], None, None), None)]),
957+
('Module', [('Raise', (1, 0), ('Call', (1, 6), ('Name', (1, 6), 'Exception', ('Load',)), [('Str', (1, 16), 'string')], []), None)]),
972958
('Module', [('Try', (1, 0), [('Pass', (2, 2))], [('ExceptHandler', (3, 0), ('Name', (3, 7), 'Exception', ('Load',)), None, [('Pass', (4, 2))])], [], [])]),
973959
('Module', [('Try', (1, 0), [('Pass', (2, 2))], [], [], [('Pass', (4, 2))])]),
974960
('Module', [('Assert', (1, 0), ('Name', (1, 7), 'v', ('Load',)), None)]),
@@ -998,14 +984,14 @@ def main():
998984
('Expression', ('BinOp', (1, 0), ('Name', (1, 0), 'a', ('Load',)), ('Add',), ('Name', (1, 4), 'b', ('Load',)))),
999985
('Expression', ('UnaryOp', (1, 0), ('Not',), ('Name', (1, 4), 'v', ('Load',)))),
1000986
('Expression', ('Lambda', (1, 0), ('arguments', [], None, [], [], None, []), ('NameConstant', (1, 7), None))),
1001-
('Expression', ('Dict', (1, 0), [('Num', (1, 2), 1)], [('Num', (1, 4), 2)])),
987+
('Expression', ('Dict', (1, 2), [('Num', (1, 2), 1)], [('Num', (1, 4), 2)])),
1002988
('Expression', ('Dict', (1, 0), [], [])),
1003-
('Expression', ('Set', (1, 0), [('NameConstant', (1, 1), None)])),
1004-
('Expression', ('Dict', (1, 0), [('Num', (2, 6), 1)], [('Num', (4, 10), 2)])),
989+
('Expression', ('Set', (1, 1), [('NameConstant', (1, 1), None)])),
990+
('Expression', ('Dict', (2, 6), [('Num', (2, 6), 1)], [('Num', (4, 10), 2)])),
1005991
('Expression', ('ListComp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))])])),
1006992
('Expression', ('GeneratorExp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))])])),
1007993
('Expression', ('Compare', (1, 0), ('Num', (1, 0), 1), [('Lt',), ('Lt',)], [('Num', (1, 4), 2), ('Num', (1, 8), 3)])),
1008-
('Expression', ('Call', (1, 0), ('Name', (1, 0), 'f', ('Load',)), [('Num', (1, 2), 1), ('Num', (1, 4), 2)], [('keyword', 'c', ('Num', (1, 8), 3))], ('Name', (1, 11), 'd', ('Load',)), ('Name', (1, 15), 'e', ('Load',)))),
994+
('Expression', ('Call', (1, 0), ('Name', (1, 0), 'f', ('Load',)), [('Num', (1, 2), 1), ('Num', (1, 4), 2), ('Starred', (1, 10), ('Name', (1, 11), 'd', ('Load',)), ('Load',))], [('keyword', 'c', ('Num', (1, 8), 3)), ('keyword', None, ('Name', (1, 15), 'e', ('Load',)))])),
1009995
('Expression', ('Num', (1, 0), 10)),
1010996
('Expression', ('Str', (1, 0), 'string')),
1011997
('Expression', ('Attribute', (1, 0), ('Name', (1, 0), 'a', ('Load',)), 'b', ('Load',))),
@@ -1016,6 +1002,6 @@ def main():
10161002
('Expression', ('Tuple', (1, 0), [('Num', (1, 0), 1), ('Num', (1, 2), 2), ('Num', (1, 4), 3)], ('Load',))),
10171003
('Expression', ('Tuple', (1, 1), [('Num', (1, 1), 1), ('Num', (1, 3), 2), ('Num', (1, 5), 3)], ('Load',))),
10181004
('Expression', ('Tuple', (1, 0), [], ('Load',))),
1019-
('Expression', ('Call', (1, 0), ('Attribute', (1, 0), ('Attribute', (1, 0), ('Attribute', (1, 0), ('Name', (1, 0), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',)), 'd', ('Load',)), [('Subscript', (1, 8), ('Attribute', (1, 8), ('Name', (1, 8), 'a', ('Load',)), 'b', ('Load',)), ('Slice', ('Num', (1, 12), 1), ('Num', (1, 14), 2), None), ('Load',))], [], None, None)),
1005+
('Expression', ('Call', (1, 0), ('Attribute', (1, 0), ('Attribute', (1, 0), ('Attribute', (1, 0), ('Name', (1, 0), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',)), 'd', ('Load',)), [('Subscript', (1, 8), ('Attribute', (1, 8), ('Name', (1, 8), 'a', ('Load',)), 'b', ('Load',)), ('Slice', ('Num', (1, 12), 1), ('Num', (1, 14), 2), None), ('Load',))], [])),
10201006
]
10211007
main()

Lib/test/test_extcall.py

+22
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,46 @@
3434
(1, 2, 3, 4, 5) {}
3535
>>> f(1, 2, 3, *[4, 5])
3636
(1, 2, 3, 4, 5) {}
37+
>>> f(*[1, 2, 3], 4, 5)
38+
(1, 2, 3, 4, 5) {}
3739
>>> f(1, 2, 3, *UserList([4, 5]))
3840
(1, 2, 3, 4, 5) {}
41+
>>> f(1, 2, 3, *[4, 5], *[6, 7])
42+
(1, 2, 3, 4, 5, 6, 7) {}
43+
>>> f(1, *[2, 3], 4, *[5, 6], 7)
44+
(1, 2, 3, 4, 5, 6, 7) {}
45+
>>> f(*UserList([1, 2]), *UserList([3, 4]), 5, *UserList([6, 7]))
46+
(1, 2, 3, 4, 5, 6, 7) {}
3947
4048
Here we add keyword arguments
4149
4250
>>> f(1, 2, 3, **{'a':4, 'b':5})
4351
(1, 2, 3) {'a': 4, 'b': 5}
52+
>>> f(1, 2, **{'a': -1, 'b': 5}, **{'a': 4, 'c': 6})
53+
Traceback (most recent call last):
54+
...
55+
TypeError: f() got multiple values for keyword argument 'a'
56+
>>> f(1, 2, **{'a': -1, 'b': 5}, a=4, c=6)
57+
Traceback (most recent call last):
58+
...
59+
TypeError: f() got multiple values for keyword argument 'a'
4460
>>> f(1, 2, 3, *[4, 5], **{'a':6, 'b':7})
4561
(1, 2, 3, 4, 5) {'a': 6, 'b': 7}
4662
>>> f(1, 2, 3, x=4, y=5, *(6, 7), **{'a':8, 'b': 9})
4763
(1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
64+
>>> f(1, 2, 3, *[4, 5], **{'c': 8}, **{'a':6, 'b':7})
65+
(1, 2, 3, 4, 5) {'a': 6, 'b': 7, 'c': 8}
66+
>>> f(1, 2, 3, *(4, 5), x=6, y=7, **{'a':8, 'b': 9})
67+
(1, 2, 3, 4, 5) {'a': 8, 'b': 9, 'x': 6, 'y': 7}
4868
4969
>>> f(1, 2, 3, **UserDict(a=4, b=5))
5070
(1, 2, 3) {'a': 4, 'b': 5}
5171
>>> f(1, 2, 3, *(4, 5), **UserDict(a=6, b=7))
5272
(1, 2, 3, 4, 5) {'a': 6, 'b': 7}
5373
>>> f(1, 2, 3, x=4, y=5, *(6, 7), **UserDict(a=8, b=9))
5474
(1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
75+
>>> f(1, 2, 3, *(4, 5), x=6, y=7, **UserDict(a=8, b=9))
76+
(1, 2, 3, 4, 5) {'a': 8, 'b': 9, 'x': 6, 'y': 7}
5577
5678
Examples with invalid arguments (TypeErrors). We're also testing the function
5779
names in the exception messages.

Lib/test/test_grammar.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,12 @@ def f(*args, **kwargs):
296296
return args, kwargs
297297
self.assertEqual(f(1, x=2, *[3, 4], y=5), ((1, 3, 4),
298298
{'x':2, 'y':5}))
299-
self.assertRaises(SyntaxError, eval, "f(1, *(2,3), 4)")
299+
self.assertEqual(f(1, *(2,3), 4), ((1, 2, 3, 4), {}))
300300
self.assertRaises(SyntaxError, eval, "f(1, x=2, *(3,4), x=5)")
301+
self.assertEqual(f(**{'eggs':'scrambled', 'spam':'fried'}),
302+
((), {'eggs':'scrambled', 'spam':'fried'}))
303+
self.assertEqual(f(spam='fried', **{'eggs':'scrambled'}),
304+
((), {'eggs':'scrambled', 'spam':'fried'}))
301305

302306
# argument annotation tests
303307
def f(x) -> list: pass

0 commit comments

Comments
 (0)