diff --git a/docs/differences/python_35.rst b/docs/differences/python_35.rst index e88df25f95e07..06cfbfc03cfa1 100644 --- a/docs/differences/python_35.rst +++ b/docs/differences/python_35.rst @@ -8,7 +8,7 @@ Below is a list of finalised/accepted PEPs for Python 3.5 grouped into their imp +----------------------------------------------------------------------------------------------------------+---------------+ | **Extensions to the syntax:** | **Status** | +--------------------------------------------------------+-------------------------------------------------+---------------+ - | `PEP 448 `_ | additional unpacking generalizations | | + | `PEP 448 `_ | additional unpacking generalizations | Partial | +--------------------------------------------------------+-------------------------------------------------+---------------+ | `PEP 465 `_ | a new matrix multiplication operator | Completed | +--------------------------------------------------------+-------------------------------------------------+---------------+ diff --git a/py/compile.c b/py/compile.c index d61dabb9a5ac0..a79b11ff22ed6 100644 --- a/py/compile.c +++ b/py/compile.c @@ -37,6 +37,7 @@ #include "py/asmbase.h" #include "py/nativeglue.h" #include "py/persistentcode.h" +#include "py/smallint.h" #if MICROPY_ENABLE_COMPILER @@ -2397,24 +2398,36 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar int n_positional = n_positional_extra; uint n_keyword = 0; uint star_flags = 0; - mp_parse_node_struct_t *star_args_node = NULL, *dblstar_args_node = NULL; + mp_uint_t star_args = 0; for (size_t i = 0; i < n_args; i++) { if (MP_PARSE_NODE_IS_STRUCT(args[i])) { mp_parse_node_struct_t *pns_arg = (mp_parse_node_struct_t *)args[i]; if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_star) { - if (star_flags & MP_EMIT_STAR_FLAG_SINGLE) { - compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("can't have multiple *x")); + if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) { + compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("* arg after **")); return; } - star_flags |= MP_EMIT_STAR_FLAG_SINGLE; - star_args_node = pns_arg; - } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) { - if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) { - compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("can't have multiple **x")); + #if MICROPY_DYNAMIC_COMPILER + if (i > mp_dynamic_compiler.small_int_bits) + #else + if (i > MP_SMALL_INT_BITS) + #endif + { + // If there are not enough bits in a small int to fit the flag, then we consider + // it a syntax error. It should be unlikely to have this many args in practice. + compile_syntax_error(comp, (mp_parse_node_t)pns_arg, MP_ERROR_TEXT("too many args")); return; } + star_flags |= MP_EMIT_STAR_FLAG_SINGLE; + star_args |= 1 << i; + compile_node(comp, pns_arg->nodes[0]); + n_positional++; + } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_arglist_dbl_star) { star_flags |= MP_EMIT_STAR_FLAG_DOUBLE; - dblstar_args_node = pns_arg; + // double-star args are stored as kw arg with key of None + EMIT(load_null); + compile_node(comp, pns_arg->nodes[0]); + n_keyword++; } else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_argument) { #if MICROPY_PY_ASSIGN_EXPR if (MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_argument_3)) { @@ -2429,7 +2442,7 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar } EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pns_arg->nodes[0])); compile_node(comp, pns_arg->nodes[1]); - n_keyword += 1; + n_keyword++; } else { compile_comprehension(comp, pns_arg, SCOPE_GEN_EXPR); n_positional++; @@ -2439,12 +2452,12 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar } } else { normal_argument: - if (star_flags) { - compile_syntax_error(comp, args[i], MP_ERROR_TEXT("non-keyword arg after */**")); + if (star_flags & MP_EMIT_STAR_FLAG_DOUBLE) { + compile_syntax_error(comp, args[i], MP_ERROR_TEXT("positional arg after **")); return; } if (n_keyword > 0) { - compile_syntax_error(comp, args[i], MP_ERROR_TEXT("non-keyword arg after keyword arg")); + compile_syntax_error(comp, args[i], MP_ERROR_TEXT("positional arg after keyword arg")); return; } compile_node(comp, args[i]); @@ -2452,19 +2465,9 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar } } - // compile the star/double-star arguments if we had them - // if we had one but not the other then we load "null" as a place holder if (star_flags != 0) { - if (star_args_node == NULL) { - EMIT(load_null); - } else { - compile_node(comp, star_args_node->nodes[0]); - } - if (dblstar_args_node == NULL) { - EMIT(load_null); - } else { - compile_node(comp, dblstar_args_node->nodes[0]); - } + // one extra object that contains the star_args map + EMIT_ARG(load_const_small_int, star_args); } // emit the function/method call diff --git a/py/emitbc.c b/py/emitbc.c index 1f5cd9d322d47..21ec121911e69 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -752,7 +752,9 @@ void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_ov STATIC void emit_bc_call_function_method_helper(emit_t *emit, int stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { if (star_flags) { - stack_adj -= (int)n_positional + 2 * (int)n_keyword + 2; + // each positional arg is one object, each kwarg is two objects, the key + // and the value and one extra object for the star args bitmap. + stack_adj -= (int)n_positional + 2 * (int)n_keyword + 1; emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? } else { stack_adj -= (int)n_positional + 2 * (int)n_keyword; diff --git a/py/emitnative.c b/py/emitnative.c index bddd661428471..056a1a8a6175c 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -2746,7 +2746,7 @@ STATIC void emit_native_call_function(emit_t *emit, mp_uint_t n_positional, mp_u } else { assert(vtype_fun == VTYPE_PYOBJ); if (star_flags) { - emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 3); // pointer to args + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 2); // pointer to args emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 0, REG_ARG_1, n_positional | (n_keyword << 8), REG_ARG_2); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } else { @@ -2762,7 +2762,7 @@ STATIC void emit_native_call_function(emit_t *emit, mp_uint_t n_positional, mp_u STATIC void emit_native_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { if (star_flags) { - emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 4); // pointer to args + emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 3); // pointer to args emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 1, REG_ARG_1, n_positional | (n_keyword << 8), REG_ARG_2); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } else { diff --git a/py/runtime.c b/py/runtime.c index ba3fbe7fa5cc8..594e63082be4e 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -25,10 +25,11 @@ * THE SOFTWARE. */ +#include #include #include #include -#include +#include #include "py/parsenum.h" #include "py/compile.h" @@ -699,12 +700,11 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ if (have_self) { self = *args++; // may be MP_OBJ_NULL } - uint n_args = n_args_n_kw & 0xff; - uint n_kw = (n_args_n_kw >> 8) & 0xff; - mp_obj_t pos_seq = args[n_args + 2 * n_kw]; // may be MP_OBJ_NULL - mp_obj_t kw_dict = args[n_args + 2 * n_kw + 1]; // may be MP_OBJ_NULL + size_t n_args = n_args_n_kw & 0xff; + size_t n_kw = (n_args_n_kw >> 8) & 0xff; + mp_uint_t star_args = mp_obj_get_int_truncated(args[n_args + 2 * n_kw]); - DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, seq=%p, dict=%p)\n", fun, self, n_args, n_kw, args, pos_seq, kw_dict); + DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, map=%u)\n", fun, self, n_args, n_kw, args, star_args); // We need to create the following array of objects: // args[0 .. n_args] unpacked(pos_seq) args[n_args .. n_args + 2 * n_kw] unpacked(kw_dict) @@ -712,19 +712,40 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // The new args array mp_obj_t *args2; - uint args2_alloc; - uint args2_len = 0; + size_t args2_alloc; + size_t args2_len = 0; + + // Try to get a hint for unpacked * args length + ssize_t list_len = 0; + + if (star_args != 0) { + for (size_t i = 0; i < n_args; i++) { + if (star_args & (1 << i)) { + mp_obj_t len = mp_obj_len_maybe(args[i]); + if (len != MP_OBJ_NULL) { + // -1 accounts for 1 of n_args occupied by this arg + list_len += mp_obj_get_int(len) - 1; + } + } + } + } // Try to get a hint for the size of the kw_dict - uint kw_dict_len = 0; - if (kw_dict != MP_OBJ_NULL && mp_obj_is_type(kw_dict, &mp_type_dict)) { - kw_dict_len = mp_obj_dict_len(kw_dict); + ssize_t kw_dict_len = 0; + + for (size_t i = 0; i < n_kw; i++) { + mp_obj_t key = args[n_args + i * 2]; + mp_obj_t value = args[n_args + i * 2 + 1]; + if (key == MP_OBJ_NULL && value != MP_OBJ_NULL && mp_obj_is_type(value, &mp_type_dict)) { + // -1 accounts for 1 of n_kw occupied by this arg + kw_dict_len += mp_obj_dict_len(value) - 1; + } } // Extract the pos_seq sequence to the new args array. // Note that it can be arbitrary iterator. - if (pos_seq == MP_OBJ_NULL) { - // no sequence + if (star_args == 0) { + // no star args to unpack // allocate memory for the new array of args args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len); @@ -738,33 +759,11 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // copy the fixed pos args mp_seq_copy(args2 + args2_len, args, n_args, mp_obj_t); args2_len += n_args; - - } else if (mp_obj_is_type(pos_seq, &mp_type_tuple) || mp_obj_is_type(pos_seq, &mp_type_list)) { - // optimise the case of a tuple and list - - // get the items - size_t len; - mp_obj_t *items; - mp_obj_get_array(pos_seq, &len, &items); - - // allocate memory for the new array of args - args2_alloc = 1 + n_args + len + 2 * (n_kw + kw_dict_len); - args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t)); - - // copy the self - if (self != MP_OBJ_NULL) { - args2[args2_len++] = self; - } - - // copy the fixed and variable position args - mp_seq_cat(args2 + args2_len, args, n_args, items, len, mp_obj_t); - args2_len += n_args + len; - } else { - // generic iterator + // at least one star arg to unpack // allocate memory for the new array of args - args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len) + 3; + args2_alloc = 1 + n_args + list_len + 2 * (n_kw + kw_dict_len); args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t)); // copy the self @@ -772,84 +771,120 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ args2[args2_len++] = self; } - // copy the fixed position args - mp_seq_copy(args2 + args2_len, args, n_args, mp_obj_t); - args2_len += n_args; - - // extract the variable position args from the iterator - mp_obj_iter_buf_t iter_buf; - mp_obj_t iterable = mp_getiter(pos_seq, &iter_buf); - mp_obj_t item; - while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - if (args2_len >= args2_alloc) { - args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), args2_alloc * 2 * sizeof(mp_obj_t)); - args2_alloc *= 2; + for (size_t i = 0; i < n_args; i++) { + mp_obj_t arg = args[i]; + if (star_args & (1 << i)) { + // star arg + if (mp_obj_is_type(arg, &mp_type_tuple) || mp_obj_is_type(arg, &mp_type_list)) { + // optimise the case of a tuple and list + + // get the items + size_t len; + mp_obj_t *items; + mp_obj_get_array(arg, &len, &items); + + // copy the items + assert(args2_len + len <= args2_alloc); + mp_seq_copy(args2 + args2_len, items, len, mp_obj_t); + args2_len += len; + } else { + // generic iterator + + // extract the variable position args from the iterator + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(arg, &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (args2_len >= args2_alloc) { + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), + args2_alloc * 2 * sizeof(mp_obj_t)); + args2_alloc *= 2; + } + args2[args2_len++] = item; + } + } + } else { + // normal argument + assert(args2_len < args2_alloc); + args2[args2_len++] = arg; } - args2[args2_len++] = item; } } // The size of the args2 array now is the number of positional args. - uint pos_args_len = args2_len; - - // Copy the fixed kw args. - mp_seq_copy(args2 + args2_len, args + n_args, 2 * n_kw, mp_obj_t); - args2_len += 2 * n_kw; + size_t pos_args_len = args2_len; + + // ensure there is still enough room for kw args + if (args2_len + 2 * (n_kw + kw_dict_len) > args2_alloc) { + size_t new_alloc = args2_len + 2 * (n_kw + kw_dict_len); + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), + new_alloc * sizeof(mp_obj_t)); + args2_alloc = new_alloc; + } - // Extract (key,value) pairs from kw_dict dictionary and append to args2. - // Note that it can be arbitrary iterator. - if (kw_dict == MP_OBJ_NULL) { - // pass - } else if (mp_obj_is_type(kw_dict, &mp_type_dict)) { - // dictionary - mp_map_t *map = mp_obj_dict_get_map(kw_dict); - assert(args2_len + 2 * map->used <= args2_alloc); // should have enough, since kw_dict_len is in this case hinted correctly above - for (size_t i = 0; i < map->alloc; i++) { - if (mp_map_slot_is_filled(map, i)) { - // the key must be a qstr, so intern it if it's a string - mp_obj_t key = map->table[i].key; - if (!mp_obj_is_qstr(key)) { - key = mp_obj_str_intern_checked(key); - } - args2[args2_len++] = key; - args2[args2_len++] = map->table[i].value; - } - } - } else { - // generic mapping: - // - call keys() to get an iterable of all keys in the mapping - // - call __getitem__ for each key to get the corresponding value - - // get the keys iterable - mp_obj_t dest[3]; - mp_load_method(kw_dict, MP_QSTR_keys, dest); - mp_obj_t iterable = mp_getiter(mp_call_method_n_kw(0, 0, dest), NULL); - - mp_obj_t key; - while ((key = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - // expand size of args array if needed - if (args2_len + 1 >= args2_alloc) { - uint new_alloc = args2_alloc * 2; - if (new_alloc < 4) { - new_alloc = 4; + // Copy the kw args. + for (size_t i = 0; i < n_kw; i++) { + mp_obj_t kw_key = args[n_args + i * 2]; + mp_obj_t kw_value = args[n_args + i * 2 + 1]; + if (kw_key == MP_OBJ_NULL) { + // double-star args + if (kw_value == MP_OBJ_NULL) { + // pass + } else if (mp_obj_is_type(kw_value, &mp_type_dict)) { + // dictionary + mp_map_t *map = mp_obj_dict_get_map(kw_value); + // should have enough, since kw_dict_len is in this case hinted correctly above + assert(args2_len + 2 * map->used <= args2_alloc); + for (size_t j = 0; j < map->alloc; j++) { + if (mp_map_slot_is_filled(map, j)) { + // the key must be a qstr, so intern it if it's a string + mp_obj_t key = map->table[j].key; + if (!mp_obj_is_qstr(key)) { + key = mp_obj_str_intern_checked(key); + } + args2[args2_len++] = key; + args2[args2_len++] = map->table[j].value; + } } - args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t)); - args2_alloc = new_alloc; - } + } else { + // generic mapping: + // - call keys() to get an iterable of all keys in the mapping + // - call __getitem__ for each key to get the corresponding value + + // get the keys iterable + mp_obj_t dest[3]; + mp_load_method(kw_value, MP_QSTR_keys, dest); + mp_obj_t iterable = mp_getiter(mp_call_method_n_kw(0, 0, dest), NULL); + + mp_obj_t key; + while ((key = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + // expand size of args array if needed + if (args2_len + 1 >= args2_alloc) { + size_t new_alloc = args2_alloc * 2; + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t)); + args2_alloc = new_alloc; + } - // the key must be a qstr, so intern it if it's a string - if (!mp_obj_is_qstr(key)) { - key = mp_obj_str_intern_checked(key); - } + // the key must be a qstr, so intern it if it's a string + if (!mp_obj_is_qstr(key)) { + key = mp_obj_str_intern_checked(key); + } - // get the value corresponding to the key - mp_load_method(kw_dict, MP_QSTR___getitem__, dest); - dest[2] = key; - mp_obj_t value = mp_call_method_n_kw(1, 0, dest); + // get the value corresponding to the key + mp_load_method(kw_value, MP_QSTR___getitem__, dest); + dest[2] = key; + mp_obj_t value = mp_call_method_n_kw(1, 0, dest); - // store the key/value pair in the argument array - args2[args2_len++] = key; - args2[args2_len++] = value; + // store the key/value pair in the argument array + args2[args2_len++] = key; + args2[args2_len++] = value; + } + } + } else { + // normal kwarg + assert(args2_len + 2 <= args2_alloc); + args2[args2_len++] = kw_key; + args2[args2_len++] = kw_value; } } diff --git a/py/vm.c b/py/vm.c index 50da90e7d2500..e6265814aad25 100644 --- a/py/vm.c +++ b/py/vm.c @@ -949,8 +949,8 @@ unwind_jump:; // unum & 0xff == n_positional // (unum >> 8) & 0xff == n_keyword // We have following stack layout here: - // fun arg0 arg1 ... kw0 val0 kw1 val1 ... seq dict <- TOS - sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 2; + // fun arg0 arg1 ... kw0 val0 kw1 val1 ... seq <- TOS + sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 1; #if MICROPY_STACKLESS if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { code_state->ip = ip; @@ -1034,8 +1034,8 @@ unwind_jump:; // unum & 0xff == n_positional // (unum >> 8) & 0xff == n_keyword // We have following stack layout here: - // fun self arg0 arg1 ... kw0 val0 kw1 val1 ... seq dict <- TOS - sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 3; + // fun self arg0 arg1 ... kw0 val0 kw1 val1 ... seq <- TOS + sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 2; #if MICROPY_STACKLESS if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { code_state->ip = ip; diff --git a/tests/basics/fun_calldblstar4.py b/tests/basics/fun_calldblstar4.py new file mode 100644 index 0000000000000..acb332a8c23bb --- /dev/null +++ b/tests/basics/fun_calldblstar4.py @@ -0,0 +1,33 @@ +# test calling a function with multiple **args + + +def f(a, b=None, c=None): + print(a, b, c) + + +f(**{"a": 1}, **{"b": 2}) +f(**{"a": 1}, **{"b": 2}, c=3) +f(**{"a": 1}, b=2, **{"c": 3}) + +try: + f(1, **{"b": 2}, **{"b": 3}) +except TypeError: + print("TypeError") + +# test calling a method with multiple **args + + +class A: + def f(self, a, b=None, c=None): + print(a, b, c) + + +a = A() +a.f(**{"a": 1}, **{"b": 2}) +a.f(**{"a": 1}, **{"b": 2}, c=3) +a.f(**{"a": 1}, b=2, **{"c": 3}) + +try: + a.f(1, **{"b": 2}, **{"b": 3}) +except TypeError: + print("TypeError") diff --git a/tests/basics/fun_callstar.py b/tests/basics/fun_callstar.py index a27a288a3c2be..53d2ece3e138e 100644 --- a/tests/basics/fun_callstar.py +++ b/tests/basics/fun_callstar.py @@ -3,10 +3,16 @@ def foo(a, b, c): print(a, b, c) +foo(*(), 1, 2, 3) +foo(*(1,), 2, 3) +foo(*(1, 2), 3) foo(*(1, 2, 3)) foo(1, *(2, 3)) foo(1, 2, *(3,)) foo(1, 2, 3, *()) +foo(*(1,), 2, *(3,)) +foo(*(1, 2), *(3,)) +foo(*(1,), *(2, 3)) # Another sequence type foo(1, 2, *[100]) @@ -29,10 +35,16 @@ def foo(self, a, b, c): print(a, b, c) a = A() +a.foo(*(), 1, 2, 3) +a.foo(*(1,), 2, 3) +a.foo(*(1, 2), 3) a.foo(*(1, 2, 3)) a.foo(1, *(2, 3)) a.foo(1, 2, *(3,)) a.foo(1, 2, 3, *()) +a.foo(*(1,), 2, *(3,)) +a.foo(*(1, 2), *(3,)) +a.foo(*(1,), *(2, 3)) # Another sequence type a.foo(1, 2, *[100]) diff --git a/tests/basics/fun_callstardblstar.py b/tests/basics/fun_callstardblstar.py index f2fd29107e270..7db458b561b5f 100644 --- a/tests/basics/fun_callstardblstar.py +++ b/tests/basics/fun_callstardblstar.py @@ -6,6 +6,11 @@ def f(a, b, c, d): f(*(1, 2), **{'c':3, 'd':4}) f(*(1, 2), **{['c', 'd'][i]:(3 + i) for i in range(2)}) +try: + eval("f(**{'a': 1}, *(2, 3, 4))") +except SyntaxError: + print("SyntaxError") + # test calling a method with *tuple and **dict class A: @@ -15,3 +20,17 @@ def f(self, a, b, c, d): a = A() a.f(*(1, 2), **{'c':3, 'd':4}) a.f(*(1, 2), **{['c', 'd'][i]:(3 + i) for i in range(2)}) + +try: + eval("a.f(**{'a': 1}, *(2, 3, 4))") +except SyntaxError: + print("SyntaxError") + + +# coverage test for arg allocation corner case + +def f2(*args, **kwargs): + print(len(args), len(kwargs)) + + +f2(*iter(range(4)), **{'a': 1}) diff --git a/tests/basics/fun_kwvarargs.py b/tests/basics/fun_kwvarargs.py index bdc10fcf14d8c..e9fd0720e94ff 100644 --- a/tests/basics/fun_kwvarargs.py +++ b/tests/basics/fun_kwvarargs.py @@ -23,3 +23,16 @@ def f4(*vargs, **kwargs): f4(*(1, 2)) f4(kw_arg=3) f4(*(1, 2), kw_arg=3) + + +# test evaluation order of arguments +def f5(*vargs, **kwargs): + print(vargs, kwargs) + + +def print_ret(x): + print(x) + return x + + +f5(*print_ret(["a", "b"]), kw_arg=print_ret(None)) diff --git a/tests/basics/python34.py b/tests/basics/python34.py index 0f6e4bafd0b9e..36e25e20dd5f9 100644 --- a/tests/basics/python34.py +++ b/tests/basics/python34.py @@ -6,27 +6,23 @@ print("SKIP") raise SystemExit -# from basics/fun_kwvarargs.py -# test evaluation order of arguments (in 3.4 it's backwards, 3.5 it's fixed) -def f4(*vargs, **kwargs): - print(vargs, kwargs) + def print_ret(x): print(x) return x -f4(*print_ret(['a', 'b']), kw_arg=print_ret(None)) # test evaluation order of dictionary key/value pair (in 3.4 it's backwards) {print_ret(1):print_ret(2)} + # from basics/syntaxerror.py def test_syntax(code): try: exec(code) except SyntaxError: print("SyntaxError") -test_syntax("f(*a, *b)") # can't have multiple * (in 3.5 we can) -test_syntax("f(**a, **b)") # can't have multiple ** (in 3.5 we can) -test_syntax("f(*a, b)") # can't have positional after * + + test_syntax("f(**a, b)") # can't have positional after ** test_syntax("() = []") # can't assign to empty tuple (in 3.6 we can) test_syntax("del ()") # can't delete empty tuple (in 3.6 we can) diff --git a/tests/basics/python34.py.exp b/tests/basics/python34.py.exp index 8480171307d14..a56c1a50b6996 100644 --- a/tests/basics/python34.py.exp +++ b/tests/basics/python34.py.exp @@ -1,14 +1,8 @@ -None -['a', 'b'] -('a', 'b') {'kw_arg': None} 2 1 SyntaxError SyntaxError SyntaxError -SyntaxError -SyntaxError -SyntaxError 3.4 3 4 IndexError('foo',) diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 92501e1248015..663bacda0159d 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -297,8 +297,8 @@ arg names: 208 POP_TOP 209 LOAD_FAST 0 210 LOAD_DEREF 14 -212 LOAD_NULL -213 CALL_FUNCTION_VAR_KW n=0 nkw=0 +212 LOAD_CONST_SMALL_INT 1 +213 CALL_FUNCTION_VAR_KW n=1 nkw=0 215 POP_TOP 216 LOAD_FAST 0 217 LOAD_METHOD b @@ -318,8 +318,8 @@ arg names: 239 LOAD_FAST 0 240 LOAD_METHOD b 242 LOAD_FAST 1 -243 LOAD_NULL -244 CALL_METHOD_VAR_KW n=0 nkw=0 +243 LOAD_CONST_SMALL_INT 1 +244 CALL_METHOD_VAR_KW n=1 nkw=0 246 POP_TOP 247 LOAD_FAST 0 248 POP_JUMP_IF_FALSE 255 diff --git a/tests/cpydiff/syntax_arg_unpacking.py b/tests/cpydiff/syntax_arg_unpacking.py new file mode 100644 index 0000000000000..e54832ddb9165 --- /dev/null +++ b/tests/cpydiff/syntax_arg_unpacking.py @@ -0,0 +1,23 @@ +""" +categories: Syntax +description: Argument unpacking does not work if the argument being unpacked is the nth or greater argument where n is the number of bits in an MP_SMALL_INT. +cause: The implementation uses an MP_SMALL_INT to flag args that need to be unpacked. +workaround: Use fewer arguments. +""" + + +def example(*args): + print(len(args)) + + +MORE = ["a", "b", "c"] + +# fmt: off +example( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + *MORE, +) +# fmt: on