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