8000 py: Implement default keyword only args. · micropython/micropython@f0778a7 · GitHub
[go: up one dir, main page]

Skip to content

Commit f0778a7

Browse files
committed
py: Implement default keyword only args.
Should finish addressing issue #524.
1 parent aabd83e commit f0778a7

File tree

6 files changed

+56
-27
lines changed

6 files changed

+56
-27
lines changed

py/compile.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,10 @@ void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) {
10351035

10361036
if (comp->have_star) {
10371037
comp->num_dict_params += 1;
1038-
#if !MICROPY_EMIT_CPYTHON
1038+
#if MICROPY_EMIT_CPYTHON
1039+
EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pn_id), false);
1040+
compile_node(comp, pn_equal);
1041+
#else
10391042
// in Micro Python we put the default dict parameters into a dictionary using the bytecode
10401043
if (comp->num_dict_params == 1) {
10411044
// in Micro Python we put the default positional parameters into a tuple using the bytecode
@@ -1048,11 +1051,10 @@ void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) {
10481051
// first default dict param, so make the map
10491052
EMIT_ARG(build_map, 0);
10501053
}
1051-
#endif
1052-
EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pn_id), false);
1054+
1055+
// compile value then key, then store it to the dict
10531056
compile_node(comp, pn_equal);
1054-
#if !MICROPY_EMIT_CPYTHON
1055-
// in Micro Python we put the default dict parameters into a dictionary using the bytecode
1057+
EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pn_id), false);
10561058
EMIT(store_map);
10571059
#endif
10581060
} else {

py/emitglue.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,14 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp
119119
// def_args must be MP_OBJ_NULL or a tuple
120120
assert(def_args == MP_OBJ_NULL || MP_OBJ_IS_TYPE(def_args, &mp_type_tuple));
121121

122-
// TODO implement default kw args
123-
assert(def_kw_args == MP_OBJ_NULL);
122+
// def_kw_args must be MP_OBJ_NULL or a dict
123+
assert(def_kw_args == MP_OBJ_NULL || MP_OBJ_IS_TYPE(def_kw_args, &mp_type_dict));
124124

125125
// make the function, depending on the raw code kind
126126
mp_obj_t fun;
127127
switch (rc->kind) {
128128
case MP_CODE_BYTECODE:
129-
fun = mp_obj_new_fun_bc(rc->scope_flags, rc->arg_names, rc->n_pos_args, rc->n_kwonly_args, def_args, rc->u_byte.code);
129+
fun = mp_obj_new_fun_bc(rc->scope_flags, rc->arg_names, rc->n_pos_args, rc->n_kwonly_args, def_args, def_kw_args, rc->u_byte.code);
130130
break;
131131
case MP_CODE_NATIVE_PY:
132132
fun = mp_make_function_n(rc->n_pos_args, rc->u_native.fun);

py/obj.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg);
380380
mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, uint n_args, const mp_obj_t *args);
381381
mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg);
382382
mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!)
383-
mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n_kwonly_args, mp_obj_t def_args, const byte *code);
383+
mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n_kwonly_args, mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code);
384384
mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun);
385385
mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun);
386386
mp_obj_t mp_obj_new_closure(mp_obj_t fun, uint n_closed, const mp_obj_t *closed);

py/objfun.c

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o
249249
// This function is pretty complicated. It's main aim is to be efficient in speed and RAM
250250
// usage for the common case of positional only args.
251251
//
252+
// TODO Now that we allocate the state for the bytecode execution, we probably don't
253+
// need extra_args anymore, and definitely don't need flat_args.
254+
//
255+
// TODO The following comment is obsolete and probably wrong:
252256
// extra_args layout: def_args, var_arg tuple, kwonly args, var_kw dict
253257

254258
DEBUG_printf("Input n_args: %d, n_kw: %d\n", n_args, n_kw);
@@ -260,7 +264,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o
260264
DEBUG_printf("Func n_def_args: %d\n", self->n_def_args);
261265

262266
const mp_obj_t *kwargs = args + n_args;
263-
mp_obj_t *extra_args = self->extra_args + self->n_def_args;
267+
mp_obj_t *extra_args = self->extra_args + self->n_def_args + self->has_def_kw_args;
264268
uint n_extra_args = 0;
265269

266270
// check positional arguments
@@ -295,7 +299,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o
295299

296300
// check keyword arguments
297301

298-
if (n_kw != 0) {
302+
if (n_kw != 0 || self->has_def_kw_args) {
299303
// We cannot use dynamically-sized array here, because GCC indeed
300304
// deallocates it on leaving defining scope (unlike most static stack allocs).
301305
// So, we have 2 choices: allocate it unconditionally at the top of function
@@ -355,10 +359,19 @@ continue2:;
355359
}
356360

357361
// Check that all mandatory keyword args are specified
362+
// Fill in default kw args if we have them
358363
for (int i = 0; i < self->n_kwonly_args; i++) {
359364
if (flat_args[self->n_pos_args + i] == MP_OBJ_NULL) {
360-
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
361-
"function missing required keyword argument '%s'", qstr_str(self->args[self->n_pos_args + i])));
365+
mp_map_elem_t *elem = NULL;
366+
if (self->has_def_kw_args) {
367+
elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[self->n_def_args])->map, MP_OBJ_NEW_QSTR(self->args[self->n_pos_args + i]), MP_MAP_LOOKUP);
368+
}
369+
if (elem != NULL) {
370+
flat_args[self->n_pos_args + i] = elem->value;
371+
} else {
372+
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
373+
"function missing required keyword argument '%s'", qstr_str(self->args[self->n_pos_args + i])));
374+
}
362375
}
363376
}
364377

@@ -513,7 +526,7 @@ const mp_obj_type_t mp_type_fun_bc = {
513526
.binary_op = fun_binary_op,
514527
};
515528

516-
mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n_kwonly_args, mp_obj_t def_args_in, const byte *code) {
529+
mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code) {
517530
uint n_def_args = 0;
518531
uint n_extra_args = 0;
519532
mp_obj_tuple_t *def_args = def_args_in;
@@ -522,6 +535,9 @@ mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n
522535
n_def_args = def_args->len;
523536
n_extra_args = def_args->len;
524537
}
538+
if (def_kw_args != MP_OBJ_NULL) {
539+
n_extra_args += 1;
540+
}
525541
if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
526542
n_extra_args += 1;
527543
}
@@ -535,18 +551,24 @@ mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n
535551
o->n_pos_args = n_pos_args;
536552
o->n_kwonly_args = n_kwonly_args;
537553
o->n_def_args = n_def_args;
554+
o->has_def_kw_args = def_kw_args != MP_OBJ_NULL;
538555
o->takes_var_args = (scope_flags & MP_SCOPE_FLAG_VARARGS) != 0;
539556
o->takes_kw_args = (scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0;
540557
o->bytecode = code;
541-
memset(o->extra_args, 0, n_extra_args * sizeof(mp_obj_t));
558+
mp_obj_t *extra_args = o->extra_args;
559+
memset(extra_args, 0, n_extra_args * sizeof(mp_obj_t));
542560
if (def_args != MP_OBJ_NULL) {
543-
memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
561+
memcpy(extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
562+
extra_args += n_def_args;
563+
}
564+
if (def_kw_args != MP_OBJ_NULL) {
565+
*extra_args++ = def_kw_args;
544566
}
545567
if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
546-
o->extra_args[n_def_args] = MP_OBJ_NULL;
568+
*extra_args++ = MP_OBJ_NULL;
547569
}
548570
if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
549-
o->extra_args[n_extra_args - 1] = MP_OBJ_NULL;
571+
*extra_args = MP_OBJ_NULL;
550572
}
551573
return o;
552574
}

py/objfun.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ typedef struct _mp_obj_fun_bc_t {
3030
machine_uint_t n_pos_args : 16; // number of arguments this function takes
3131
machine_uint_t n_kwonly_args : 16; // number of arguments this function takes
3232
machine_uint_t n_def_args : 16; // number of default arguments
33+
machine_uint_t has_def_kw_args : 1; // set if this function has default keyword args
3334
machine_uint_t takes_var_args : 1; // set if this function takes variable args
3435
machine_uint_t takes_kw_args : 1; // set if this function takes keyword args
3536
const byte *bytecode; // bytecode for the function
3637
qstr *args; // argument names (needed to resolve positional args passed as keywords)
37-
// values of default args (if any), plus a slot at the end for var args and/or kw args (if it takes them)
38+
// the following extra_args array is allocated space to take (in order):
39+
// - values of positional default args (if any)
40+
// - a single slot for default kw args dict (if it has them)
41+
// - a single slot for var args tuple (if it takes them)
42+
// - a single slot for kw args dict (if it takes them)
3843
mp_obj_t extra_args[];
3944
} mp_obj_fun_bc_t;

tests/basics/fun-kwonly.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ def f(a, *, b, **kw):
4343
f(1, b=2)
4444
f(1, b=2, c=3)
4545

46-
## with a default value; not currently working
47-
#def g(a, *, b=2, c):
48-
# print(a, b, c)
49-
#
50-
#g(1, c=3)
51-
#g(1, b=3, c=4)
52-
#g(1, **{'c':3})
53-
#g(1, **{'b':'3', 'c':4})
46+
# with a default value
47+
def g(a, *, b=2, c):
48+
print(a, b, c)
49+
50+
g(1, c=3)
51+
g(1, b=3, c=4)
52+
g(1, **{'c':3})
53+
g(1, **{'b':'3', 'c':4})
5454

5555
# with named star
5656
def f(*a, b, c):

0 commit comments

Comments
 (0)
0