Skip to content

Commit

Permalink
py/objtype: Allow passing keyword arguments to native base __init__.
Browse files Browse the repository at this point in the history
Fixes issue micropython#15465.

Signed-off-by: stijn <[email protected]>
  • Loading branch information
stinos committed Oct 3, 2024
1 parent ca6723b commit ea49b46
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 4 deletions.
3 changes: 3 additions & 0 deletions py/obj.h
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,9 @@ typedef mp_obj_t (*mp_fun_3_t)(mp_obj_t, mp_obj_t, mp_obj_t);
typedef mp_obj_t (*mp_fun_var_t)(size_t n, const mp_obj_t *);
// mp_fun_kw_t takes mp_map_t* (and not const mp_map_t*) to ease passing
// this arg to mp_map_lookup().
// Note that the mp_obj_t* array will contain all arguments, positional and keyword, with the keyword ones
// starting at offset n, like: arg0 arg1 ... arg<n> key0 value0 key1 value1 ..., and the mp_map_t* gets those
// same keyword arguments but as a map for convenience; see fun_builtin_var_call.
typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *);

// Flags for type behaviour (mp_obj_type_t.flags)
Expand Down
4 changes: 3 additions & 1 deletion py/objfun.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
if (self->sig & 1) {
// function allows keywords

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

Expand Down
6 changes: 3 additions & 3 deletions py/objtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,14 @@ static int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t

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

#if !MICROPY_CPYTHON_COMPAT
static
Expand Down
29 changes: 29 additions & 0 deletions tests/basics/subclass_native_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,35 @@ def __init__(self, a, b):
super().__init__([a, b])
print(L(2, 3))

# with keyword arguments, with star arguments and without because those use different C calls
class D(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
print(D())
print(D([('a', 1)]))
print(D([('a', 1)], a=2, b=3))
print(D(a=2, b=3))

class D(dict):
def __init__(self):
super().__init__()
print(D())

class D(dict):
def __init__(self):
super().__init__([])
print(D())

class D(dict):
def __init__(self):
super().__init__(a=1)
print(D())

class D(dict):
def __init__(self):
super().__init__([], a=1)
print(D())

# inherits implicitly from object
class A:
def __init__(self):
Expand Down

0 comments on commit ea49b46

Please sign in to comment.