Skip to content

Commit

Permalink
py/objobject: Add object.__setattr__ function.
Browse files Browse the repository at this point in the history
Allows assigning attributes on class instances that implement their own
__setattr__.  Both object.__setattr__ and super(A, b).__setattr__ will work
with this commit.
  • Loading branch information
Jongy authored and dpgeorge committed Dec 20, 2019
1 parent 90f2864 commit 07ccb55
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 2 deletions.
22 changes: 21 additions & 1 deletion py/objobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___init___obj, object___init__);

STATIC mp_obj_t object___new__(mp_obj_t cls) {
if (!mp_obj_is_type(cls, &mp_type_type) || !mp_obj_is_instance_type((mp_obj_type_t*)MP_OBJ_TO_PTR(cls))) {
mp_raise_TypeError("__new__ arg must be a user-type");
mp_raise_TypeError("arg must be user-type");
}
// This executes only "__new__" part of instance creation.
// TODO: This won't work well for classes with native bases.
Expand All @@ -62,13 +62,33 @@ STATIC mp_obj_t object___new__(mp_obj_t cls) {
STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___new___fun_obj, object___new__);
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(object___new___obj, MP_ROM_PTR(&object___new___fun_obj));

#if MICROPY_PY_DELATTR_SETATTR
STATIC mp_obj_t object___setattr__(mp_obj_t self_in, mp_obj_t attr, mp_obj_t value) {
if (!mp_obj_is_instance_type(mp_obj_get_type(MP_OBJ_TO_PTR(self_in)))) {
mp_raise_TypeError("arg must be user-type");
}

if (!mp_obj_is_str(attr)) {
mp_raise_TypeError(NULL);
}

mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in);
mp_map_lookup(&self->members, attr, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(object___setattr___obj, object___setattr__);
#endif

STATIC const mp_rom_map_elem_t object_locals_dict_table[] = {
#if MICROPY_CPYTHON_COMPAT
{ MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&object___init___obj) },
#endif
#if MICROPY_CPYTHON_COMPAT
{ MP_ROM_QSTR(MP_QSTR___new__), MP_ROM_PTR(&object___new___obj) },
#endif
#if MICROPY_PY_DELATTR_SETATTR
{ MP_ROM_QSTR(MP_QSTR___setattr__), MP_ROM_PTR(&object___setattr___obj) },
#endif
};

STATIC MP_DEFINE_CONST_DICT(object_locals_dict, object_locals_dict_table);
Expand Down
4 changes: 3 additions & 1 deletion py/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1038,9 +1038,11 @@ void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t
|| m_type == &mp_type_fun_builtin_1
|| m_type == &mp_type_fun_builtin_2
|| m_type == &mp_type_fun_builtin_3
|| m_type == &mp_type_fun_builtin_var)) {
|| m_type == &mp_type_fun_builtin_var)
&& type != &mp_type_object) {
// we extracted a builtin method without a first argument, so we must
// wrap this function in a type checker
// Note that object will do its own checking so shouldn't be wrapped.
dest[0] = mp_obj_new_checked_fun(type, member);
} else
#endif
Expand Down
26 changes: 26 additions & 0 deletions tests/basics/class_delattr_setattr.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,29 @@ def __delattr__(self, attr):
print(a.a)
except AttributeError:
print("AttributeError")

# test object.__setattr__
class C:
def __init__(self):
pass

def __setattr__(self, attr, value):
print(attr, "=", value)

c = C()
c.a = 5
try:
print(c.a)
except AttributeError:
print("AttributeError")

object.__setattr__(c, "a", 5)
super(C, c).__setattr__("b", 6)
print(c.a)
print(c.b)

try:
# attribute name must be string
object.__setattr__(c, 5, 5)
except TypeError:
print("TypeError")

0 comments on commit 07ccb55

Please sign in to comment.