Index: Python/ceval.c =================================================================== --- Python/ceval.c (revision 61386) +++ Python/ceval.c (working copy) @@ -1769,6 +1769,38 @@ } break; + case BUILD_LIST_UNPACK: + case BUILD_TUPLE_UNPACK: + x = PyList_New(0); + if (x != NULL) { + int i; + for ( i = oparg; i > 0; i--) { + w = stack_pointer[-i]; + v = _PyList_Extend((PyListObject *)x, + w); + if (!v) { + Py_DECREF(x); + x = NULL; + break; + } + Py_DECREF(v); + } + if (x == NULL) + break; + STACKADJ(-oparg); + if (opcode == BUILD_TUPLE_UNPACK) { + v = PyObject_CallFunctionObjArgs( + (PyObject *)&PyTuple_Type, + x, NULL); + Py_DECREF(x); + if (v) + PUSH(v); + x = v; + } else + PUSH(x); + } + break; + case BUILD_SET: x = PySet_New(NULL); if (x != NULL) { @@ -1787,6 +1819,25 @@ } break; + case BUILD_SET_UNPACK: + x = PySet_New(NULL); + if (x != NULL) { + int i; + for ( i = oparg; i > 0; i--) { + w = stack_pointer[-i]; + if (_PySet_Update(x, w) < 0) { + Py_DECREF(x); + x = NULL; + break; + } + } + if (x == NULL) + break; + STACKADJ(-oparg); + PUSH(x); + } + break; + case BUILD_MAP: x = _PyDict_NewPresized((Py_ssize_t)oparg); PUSH(x); Index: Python/compile.c =================================================================== --- Python/compile.c (revision 61386) +++ Python/compile.c (working copy) @@ -791,6 +791,9 @@ case BUILD_TUPLE: case BUILD_LIST: case BUILD_SET: + case BUILD_LIST_UNPACK: + case BUILD_TUPLE_UNPACK: + case BUILD_SET_UNPACK: return 1-oparg; case BUILD_MAP: return 1; @@ -2609,8 +2612,9 @@ compiler_list(struct compiler *c, expr_ty e) { int n = asdl_seq_LEN(e->v.List.elts); + int i, nsublists = 0, nseen = 0; if (e->v.List.ctx == Store) { - int i, seen_star = 0; + int seen_star = 0; for (i = 0; i < n; i++) { expr_ty elt = asdl_seq_GET(e->v.List.elts, i); if (elt->kind == Starred_kind && !seen_star) { @@ -2631,10 +2635,35 @@ ADDOP_I(c, UNPACK_SEQUENCE, n); } } - VISIT_SEQ(c, expr, e->v.List.elts); if (e->v.List.ctx == Load) { - ADDOP_I(c, BUILD_LIST, n); + for (i = 0; i < n; i++) { + expr_ty elt = asdl_seq_GET(e->v.List.elts, i); + if (elt->kind == Starred_kind) { + if (nseen) { + ADDOP_I(c, BUILD_TUPLE, nseen); + nseen = 0; + nsublists++; + } + VISIT(c, expr, elt->v.Starred.value); + nsublists++; + } + else { + VISIT(c, expr, elt); + nseen++; + } + } + if (nsublists) { + if (nseen) { + ADDOP_I(c, BUILD_TUPLE, nseen); + nsublists++; + } + ADDOP_I(c, BUILD_LIST_UNPACK, nsublists); + } + else + ADDOP_I(c, BUILD_LIST, nseen); } + else + VISIT_SEQ(c, expr, e->v.List.elts); return 1; } @@ -2642,8 +2671,9 @@ compiler_tuple(struct compiler *c, expr_ty e) { int n = asdl_seq_LEN(e->v.Tuple.elts); + int i, nsubtuples = 0, nseen = 0; if (e->v.Tuple.ctx == Store) { - int i, seen_star = 0; + int seen_star = 0; for (i = 0; i < n; i++) { expr_ty elt = asdl_seq_GET(e->v.Tuple.elts, i); if (elt->kind == Starred_kind && !seen_star) { @@ -2664,14 +2694,72 @@ ADDOP_I(c, UNPACK_SEQUENCE, n); } } - VISIT_SEQ(c, expr, e->v.Tuple.elts); if (e->v.Tuple.ctx == Load) { - ADDOP_I(c, BUILD_TUPLE, n); + for (i = 0; i < n; i++) { + expr_ty elt = asdl_seq_GET(e->v.Tuple.elts, i); + if (elt->kind == Starred_kind) { + if (nseen) { + ADDOP_I(c, BUILD_TUPLE, nseen); + nseen = 0; + nsubtuples++; + } + VISIT(c, expr, elt->v.Starred.value); + nsubtuples++; + } + else { + VISIT(c, expr, elt); + nseen++; + } + } + if (nsubtuples) { + if (nseen) { + ADDOP_I(c, BUILD_TUPLE, nseen); + nsubtuples++; + } + ADDOP_I(c, BUILD_TUPLE_UNPACK, nsubtuples); + } + else + ADDOP_I(c, BUILD_TUPLE, nseen); } + else + VISIT_SEQ(c, expr, e->v.Tuple.elts); return 1; } static int +compiler_set(struct compiler *c, expr_ty e) +{ + int n = asdl_seq_LEN(e->v.List.elts); + int i, nsubsets = 0, nseen = 0; + for (i = 0; i < n; i++) { + expr_ty elt = asdl_seq_GET(e->v.Set.elts, i); + if (elt->kind == Starred_kind) { + if (nseen) { + ADDOP_I(c, BUILD_SET, nseen); + nseen = 0; + nsubsets++; + } + VISIT(c, expr, elt->v.Starred.value); + nsubsets++; + } + else { + VISIT(c, expr, elt); + nseen++; + } + } + if (nsubsets) { + if (nseen) { + ADDOP_I(c, BUILD_SET, nseen); + nsubsets++; + } + ADDOP_I(c, BUILD_SET_UNPACK, nsubsets); + } + else + ADDOP_I(c, BUILD_SET, nseen); + return 1; +} + +static int compiler_compare(struct compiler *c, expr_ty e) { int i, n; @@ -2796,8 +2884,9 @@ comprehension_ty gen; basicblock *start, *anchor, *skip, *if_cleanup; - int i, n; - + basicblock *innerstart, *inneranchor; + int i, n, is_star = 0; + start = compiler_new_block(c); skip = compiler_new_block(c); if_cleanup = compiler_new_block(c); @@ -2840,25 +2929,44 @@ elt, val, type)) return 0; + if (elt->kind == Starred_kind && + type != COMP_DICTCOMP) { + elt = elt->v.Starred.value; + is_star = 1; + innerstart = compiler_new_block(c); + inneranchor = compiler_new_block(c); + if (innerstart == NULL || inneranchor == NULL) + return 0; + } + /* only append after the last for generator */ if (gen_index >= asdl_seq_LEN(generators)) { + /* Dictcomps can't be using unpacking-*, and need + elements visited in a different order. */ + if (type != COMP_DICTCOMP) + VISIT(c, expr, elt); + if (is_star) { + ADDOP(c, GET_ITER); + compiler_use_next_block(c, innerstart); + ADDOP_JREL(c, FOR_ITER, inneranchor); + NEXT_BLOCK(c); + } /* comprehension specific code */ switch (type) { case COMP_GENEXP: - VISIT(c, expr, elt); ADDOP(c, YIELD_VALUE); ADDOP(c, POP_TOP); break; case COMP_LISTCOMP: if (!compiler_nameop(c, tmpname, Load)) return 0; - VISIT(c, expr, elt); + ADDOP(c, ROT_TWO); ADDOP(c, LIST_APPEND); break; case COMP_SETCOMP: if (!compiler_nameop(c, tmpname, Load)) return 0; - VISIT(c, expr, elt); + ADDOP(c, ROT_TWO); ADDOP(c, SET_ADD); break; case COMP_DICTCOMP: @@ -2875,7 +2983,10 @@ default: return 0; } - + if (is_star) { + ADDOP_JABS(c, JUMP_ABSOLUTE, innerstart); + compiler_use_next_block(c, inneranchor); + } compiler_use_next_block(c, skip); } for (i = 0; i < n; i++) { @@ -3025,6 +3136,47 @@ static int +compiler_yield(struct compiler *c, expr_ty e) +{ + int is_star = 0; + expr_ty elt = e->v.Yield.value; + + if (c->u->u_ste->ste_type != FunctionBlock) + return compiler_error(c, "'yield' outside function"); + if (elt && elt->kind == Starred_kind) { + is_star = 1; + elt = elt->v.Starred.value; + } + if (elt) { + VISIT(c, expr, elt); + } + else { + ADDOP_O(c, LOAD_CONST, Py_None, consts); + } + if (is_star) { + basicblock *start, *anchor; + + start = compiler_new_block(c); + anchor = compiler_new_block(c); + if (start == NULL || anchor == NULL) + return 0; + ADDOP(c, GET_ITER); + compiler_use_next_block(c, start); + ADDOP_JREL(c, FOR_ITER, anchor); + NEXT_BLOCK(c); + ADDOP(c, YIELD_VALUE); + ADDOP(c, POP_TOP); + ADDOP_JABS(c, JUMP_ABSOLUTE, start); + compiler_use_next_block(c, anchor); + ADDOP_O(c, LOAD_CONST, Py_None, consts); + } + else + ADDOP(c, YIELD_VALUE); + return 1; +} + + +static int compiler_visit_keyword(struct compiler *c, keyword_ty k) { ADDOP_O(c, LOAD_CONST, k->arg, consts); @@ -3237,10 +3389,7 @@ } break; case Set_kind: - n = asdl_seq_LEN(e->v.Set.elts); - VISIT_SEQ(c, expr, e->v.Set.elts); - ADDOP_I(c, BUILD_SET, n); - break; + return compiler_set(c, e); case GeneratorExp_kind: return compiler_genexp(c, e); case ListComp_kind: @@ -3250,16 +3399,7 @@ case DictComp_kind: return compiler_dictcomp(c, e); case Yield_kind: - if (c->u->u_ste->ste_type != FunctionBlock) - return compiler_error(c, "'yield' outside function"); - if (e->v.Yield.value) { - VISIT(c, expr, e->v.Yield.value); - } - else { - ADDOP_O(c, LOAD_CONST, Py_None, consts); - } - ADDOP(c, YIELD_VALUE); - break; + return compiler_yield(c, e); case Compare_kind: return compiler_compare(c, e); case Call_kind: Index: Include/opcode.h =================================================================== --- Include/opcode.h (revision 61386) +++ Include/opcode.h (working copy) @@ -95,13 +95,14 @@ #define COMPARE_OP 107 /* Comparison operator */ #define IMPORT_NAME 108 /* Index in name list */ #define IMPORT_FROM 109 /* Index in name list */ - #define JUMP_FORWARD 110 /* Number of bytes to skip */ #define JUMP_IF_FALSE 111 /* "" */ #define JUMP_IF_TRUE 112 /* "" */ #define JUMP_ABSOLUTE 113 /* Target byte offset from beginning of code */ - +#define BUILD_LIST_UNPACK 114 /* number of iterables to unpack */ +#define BUILD_TUPLE_UNPACK 115 /* number of iterables to unpack */ #define LOAD_GLOBAL 116 /* Index in name list */ +#define BUILD_SET_UNPACK 117 #define CONTINUE_LOOP 119 /* Start of loop (absolute) */ #define SETUP_LOOP 120 /* Target address (relative) */ Index: Lib/opcode.py =================================================================== --- Lib/opcode.py (revision 61386) +++ Lib/opcode.py (working copy) @@ -135,8 +135,10 @@ jrel_op('JUMP_IF_FALSE', 111) # "" jrel_op('JUMP_IF_TRUE', 112) # "" jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code - +def_op('BUILD_LIST_UNPACK', 114) +def_op('BUILD_TUPLE_UNPACK', 115) name_op('LOAD_GLOBAL', 116) # Index in name list +def_op('BUILD_SET_UNPACK', 117) jabs_op('CONTINUE_LOOP', 119) # Target address jrel_op('SETUP_LOOP', 120) # Distance to target address Index: Lib/test/test_unpack_ex.py =================================================================== --- Lib/test/test_unpack_ex.py (revision 61386) +++ Lib/test/test_unpack_ex.py (working copy) @@ -59,7 +59,7 @@ 1 [2] 3 4 [5, 6] 7 -Unpack in list +Unpack into list assignment >>> [a, *b, c] = range(5) >>> a == 0 and b == [1, 2, 3] and c == 4 @@ -71,6 +71,58 @@ >>> a == 0 and b == [1, 2, 3] and c == 4 and d == [0, 1, 2, 3] and e == 4 True +Unpacking inside a list literal + >>> l = [3, 4] + >>> [1, 2, *l, 5, *reversed(l), 2, 1] + [1, 2, 3, 4, 5, 4, 3, 2, 1] + +Or a tuple literal + >>> l = [3, 4] + >>> 1, 2, *l, 5, *reversed(l), 2, 1 + (1, 2, 3, 4, 5, 4, 3, 2, 1) + >>> (1, 2, *l, 5, *reversed(l), 2, 1) + (1, 2, 3, 4, 5, 4, 3, 2, 1) + +Or a set literal + >>> l = [3, 4] + >>> s = {1, 2, *l, 5, *reversed(l), 2, 1} + >>> sorted(s) + [1, 2, 3, 4, 5] + +Stars on both sides of an assignment + >>> a, b, *c = range(5) + >>> a, b, c + (0, 1, [2, 3, 4]) + >>> *a, b, c = a, b, *c + >>> a, b, c + ([0, 1, 2], 3, 4) + +Unpacking an iterator multiple times: + >>> it = (item for item in range(2)) + >>> a, b, c = *it, 2, *it, *it + >>> a, b, c + (0, 1, 2) + +Stars inside a list- or gencomp, unpacking arbitrary iterables: + >>> l = [[1], (2, 3), {4}, {5: None}, (i for i in (6, 7)), "89"] + >>> [ *item for item in l ] + [1, 2, 3, 4, 5, 6, 7, '8', '9'] + + >>> l = [[1], (2, 3), {4}, {5: None}, (i for i in (6, 7)), "89"] + >>> g = (*item for item in l) + >>> list(g) + [1, 2, 3, 4, 5, 6, 7, '8', '9'] + +Unpacking an iterator in a generator: + >>> def yield_nested(it): + ... for item in it: + ... yield *item + ... + >>> l = [[1], (2, 3), {4}, {5: None}, (i for i in (6, 7)), "89"] + >>> g = yield_nested(l) + >>> list(g) + [1, 2, 3, 4, 5, 6, 7, '8', '9'] + Now for some failures Unpacking non-sequence @@ -143,6 +195,21 @@ ... SyntaxError: can use starred expression only as assignment target + >>> { x: *y for x, y in zip(range(5), range(5)) } # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + SyntaxError: can use starred expression only as assignment target + + >>> { *x: y for x, y in zip(range(5), range(5)) } # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + SyntaxError: can use starred expression only as assignment target + + >>> { *x: *y for x, y in zip(range(5), range(5)) } # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + SyntaxError: can use starred expression only as assignment target + Some size constraints (all fail.) >>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range(1<<8 + 1)"