Skip to content

Commit 338df1a

Browse files
stinosdpgeorge
authored andcommitted
py/objtype: Allow passing keyword arguments to native base __init__.
Allowing passing keyword arguments to a native base's __init__, i.e. `make_new` in the C code. Previously only positional arguments were allowed. The main trade-off in this commit is that every call to the native base's `make_new` is now going to be preceded by a call to `mp_map_init_fixed_table` even though most of what that does is unused and instead it merely serves as a way to pass the number of keyword arguments. Fixes issue micropython#15465. Signed-off-by: stijn <[email protected]>
1 parent 548f88d commit 338df1a

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

Diff for: py/obj.h

+3
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,9 @@ typedef mp_obj_t (*mp_fun_3_t)(mp_obj_t, mp_obj_t, mp_obj_t);
516516
typedef mp_obj_t (*mp_fun_var_t)(size_t n, const mp_obj_t *);
517517
// mp_fun_kw_t takes mp_map_t* (and not const mp_map_t*) to ease passing
518518
// this arg to mp_map_lookup().
519+
// Note that the mp_obj_t* array will contain all arguments, positional and keyword, with the keyword
520+
// ones starting at offset n, like: arg0 arg1 ... arg<n> key0 value0 key1 value1 ..., and the mp_map_t*
521+
// gets those same keyword arguments but as a map for convenience; see fun_builtin_var_call.
519522
typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *);
520523

521524
// Flags for type behaviour (mp_obj_type_t.flags)

Diff for: py/objfun.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ static mp_obj_t fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_k
107107
if (self->sig & 1) {
108108
// function allows keywords
109109

110-
// we create a map directly from the given args array
110+
// we create a map directly from the given args array; self->fun.kw does still
111+
// expect args to have both positional and keyword arguments, ordered as:
112+
// arg0 arg1 ... arg<n_args> key0 value0 key1 value1 ... key<n_kw> value<n_kw>
111113
mp_map_t kw_args;
112114
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
113115

Diff for: py/objtype.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,14 @@ static int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t
8585

8686
// This wrapper function allows a subclass of a native type to call the
8787
// __init__() method (corresponding to type->make_new) of the native type.
88-
static mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) {
88+
static mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
8989
mp_obj_instance_t *self = MP_OBJ_TO_PTR(args[0]);
9090
const mp_obj_type_t *native_base = NULL;
9191
instance_count_native_bases(self->base.type, &native_base);
92-
self->subobj[0] = MP_OBJ_TYPE_GET_SLOT(native_base, make_new)(native_base, n_args - 1, 0, args + 1);
92+
self->subobj[0] = MP_OBJ_TYPE_GET_SLOT(native_base, make_new)(native_base, n_args - 1, kw_args->used, args + 1);
9393
return mp_const_none;
9494
}
95-
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper);
95+
static MP_DEFINE_CONST_FUN_OBJ_KW(native_base_init_wrapper_obj, 1, native_base_init_wrapper);
9696

9797
#if !MICROPY_CPYTHON_COMPAT
9898
static

Diff for: tests/basics/subclass_native_init.py

+29
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,35 @@ def __init__(self, a, b):
66
super().__init__([a, b])
77
print(L(2, 3))
88

9+
# with keyword arguments, with star arguments and without because those use different C calls
10+
class D(dict):
11+
def __init__(self, *args, **kwargs):
12+
super().__init__(*args, **kwargs)
13+
print(D())
14+
print(D([('a', 1)]))
15+
print(D([('a', 1)], a=2, b=3))
16+
print(D(a=2, b=3))
17+
18+
class D(dict):
19+
def __init__(self):
20+
super().__init__()
21+
print(D())
22+
23+
class D(dict):
24+
def __init__(self):
25+
super().__init__([])
26+
print(D())
27+
28+
class D(dict):
29+
def __init__(self):
30+
super().__init__(a=1)
31+
print(D())
32+
33+
class D(dict):
34+
def __init__(self):
35+
super().__init__([], a=1)
36+
print(D())
37+
938
# inherits implicitly from object
1039
class A:
1140
def __init__(self):

0 commit comments

Comments
 (0)