diff --git a/ports/unix/native_base_class.c b/ports/unix/native_base_class.c index 88165ef645..42cea88e18 100644 --- a/ports/unix/native_base_class.c +++ b/ports/unix/native_base_class.c @@ -55,8 +55,24 @@ MP_PROPERTY_GETSET(native_base_class_test_obj, (mp_obj_t)&native_base_class_get_test_obj, (mp_obj_t)&native_base_class_set_test_obj); +STATIC mp_obj_t native_base_class_obj_print_subclass_attr(mp_obj_t self_in, mp_obj_t attr_name_obj) { + if (!mp_obj_is_str(attr_name_obj)) { + mp_raise_TypeError(NULL); + } + qstr attr_name = mp_obj_str_get_qstr(attr_name_obj); + mp_obj_t value = mp_load_attr(self_in, attr_name); + mp_printf(&mp_plat_print, "native base class .%q set to: ", attr_name); + mp_obj_print_helper(&mp_plat_print, value, PRINT_REPR); + mp_printf(&mp_plat_print, "\n"); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(native_base_class_print_subclass_attr_obj, native_base_class_obj_print_subclass_attr); + STATIC const mp_rom_map_elem_t native_base_class_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_test), MP_ROM_PTR(&native_base_class_test_obj) }, + + { MP_ROM_QSTR(MP_QSTR_print_subclass_attr), MP_ROM_PTR(&native_base_class_print_subclass_attr_obj) }, + }; STATIC MP_DEFINE_CONST_DICT(native_base_class_locals_dict, native_base_class_locals_dict_table); diff --git a/py/objstringio.c b/py/objstringio.c index a3c66ed010..a5167768d7 100644 --- a/py/objstringio.c +++ b/py/objstringio.c @@ -45,15 +45,26 @@ STATIC void check_stringio_is_open(const mp_obj_stringio_t *o) { #define check_stringio_is_open(o) #endif +STATIC mp_obj_stringio_t *native_obj(mp_obj_t o_in) { + mp_obj_stringio_t *native = mp_obj_cast_to_native_base(o_in, &mp_type_stringio); + + #if MICROPY_PY_IO_BYTESIO + if (native == MP_OBJ_NULL) { + native = mp_obj_cast_to_native_base(o_in, &mp_type_bytesio); + } + #endif + return native; +} + STATIC void stringio_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; - mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_stringio_t *self = native_obj(self_in); mp_printf(print, self->base.type == &mp_type_stringio ? "" : "", self); } STATIC mp_uint_t stringio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { (void)errcode; - mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in); + mp_obj_stringio_t *o = native_obj(o_in); check_stringio_is_open(o); if (o->vstr->len <= o->pos) { // read to EOF, or seeked to EOF or beyond return 0; @@ -77,7 +88,7 @@ STATIC void stringio_copy_on_write(mp_obj_stringio_t *o) { STATIC mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { (void)errcode; - mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in); + mp_obj_stringio_t *o = native_obj(o_in); check_stringio_is_open(o); if (o->vstr->fixed_buf) { @@ -111,7 +122,7 @@ STATIC mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, STATIC mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { (void)errcode; - mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in); + mp_obj_stringio_t *o = native_obj(o_in); switch (request) { case MP_STREAM_SEEK: { struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)arg; @@ -163,7 +174,7 @@ STATIC mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, #define STREAM_TO_CONTENT_TYPE(o) (((o)->base.type == &mp_type_stringio) ? &mp_type_str : &mp_type_bytes) STATIC mp_obj_t stringio_getvalue(mp_obj_t self_in) { - mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_stringio_t *self = native_obj(self_in); check_stringio_is_open(self); // TODO: Try to avoid copying string return mp_obj_new_str_of_type(STREAM_TO_CONTENT_TYPE(self), (byte *)self->vstr->buf, self->vstr->len); diff --git a/py/objtype.c b/py/objtype.c index 213c5e3838..d15d36a10e 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -199,14 +199,9 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_t return; } else { mp_obj_instance_t *obj = lookup->obj; - mp_obj_t obj_obj; - if (obj != NULL && mp_obj_is_native_type(type) && type != &mp_type_object /* object is not a real type */) { - // If we're dealing with native base class, then it applies to native sub-object - obj_obj = obj->subobj[0]; - } else { - obj_obj = MP_OBJ_FROM_PTR(obj); - } - mp_convert_member_lookup(obj_obj, type, elem->value, lookup->dest); + // CIRCUITPY-CHANGE: Pass object directly. MP passes the native object. + // This allows native code to lookup and call functions on Python subclasses. + mp_convert_member_lookup(obj, type, elem->value, lookup->dest); } #if DEBUG_PRINT DEBUG_printf("mp_obj_class_lookup: Returning: "); diff --git a/tests/unix/subclass_native.py b/tests/unix/subclass_native.py index fa80326db6..8b3a9dbb73 100644 --- a/tests/unix/subclass_native.py +++ b/tests/unix/subclass_native.py @@ -29,6 +29,8 @@ print(".test:", a.test) a._new_attribute = True print("._new_attribute", a._new_attribute) +a.print_subclass_attr("_new_attribute") + class B(NativeBaseClass): def __init__(self, suffix): @@ -43,3 +45,5 @@ print(".test:", b.test) b._new_attribute = True print("._new_attribute", b._new_attribute) + +b.print_subclass_attr("_new_attribute") diff --git a/tests/unix/subclass_native.py.exp b/tests/unix/subclass_native.py.exp index a264d3b93a..7f872c4008 100644 --- a/tests/unix/subclass_native.py.exp +++ b/tests/unix/subclass_native.py.exp @@ -5,7 +5,9 @@ native base class .test set to: 'test set directly' native base class .test set to: 'test set indirectly' .test: test set indirectly ._new_attribute True +native base class ._new_attribute set to: True .test: super init suffix native base class .test set to: 'test set indirectly through b' .test: test set indirectly through b ._new_attribute True +native base class ._new_attribute set to: True