py/objboundmeth: Implement equality comparisons

Currently different bound method instances will compare as unequal even
when both instances bind the same method and object. This is different
to the CPython implementation and, for example, will cause problems
when working with lists that contain callbacks since things like
list.remove() will not work correctly.

Fix this the obvious way by introducing a binary op and implementing
MP_BINARY_OP_EQUAL. A simple test case is provided.

Currently this is unconditionally compiled and on a Thumb2 system
(ports/nrf) it resulted in a 68 byte increase in code size.

Signed-off-by: Daniel Thompson <daniel@redfelineninja.org.uk>
bound-method-equality
Daniel Thompson 2020-11-14 12:03:30 +00:00
parent 61d1e4b01b
commit 6b1aabac14
2 changed files with 31 additions and 0 deletions

View File

@ -95,6 +95,28 @@ STATIC void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
}
#endif
STATIC mp_obj_t bound_meth_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
// We only implement equality tests
if (op != MP_BINARY_OP_EQUAL) {
return MP_OBJ_NULL; // op not supported
}
// Check types. It is tricky to sub-class a bound method so we can keep this very simple)
const mp_obj_type_t *lhs_type = mp_obj_get_type(lhs_in);
const mp_obj_type_t *rhs_type = mp_obj_get_type(rhs_in);
if (lhs_type->make_new || lhs_type->binary_op != bound_meth_binary_op || lhs_type != rhs_type) {
return MP_OBJ_NULL;
}
mp_obj_bound_meth_t *lhs = MP_OBJ_TO_PTR(lhs_in);
mp_obj_bound_meth_t *rhs = MP_OBJ_TO_PTR(rhs_in);
if (mp_obj_equal(lhs->meth, rhs->meth)) {
return mp_obj_new_bool(mp_obj_equal(lhs->self, rhs->self));
}
return mp_const_false;
}
STATIC const mp_obj_type_t mp_type_bound_meth = {
{ &mp_type_type },
.name = MP_QSTR_bound_method,
@ -102,6 +124,7 @@ STATIC const mp_obj_type_t mp_type_bound_meth = {
.print = bound_meth_print,
#endif
.call = bound_meth_call,
.binary_op = bound_meth_binary_op,
#if MICROPY_PY_FUNCTION_ATTRS
.attr = bound_meth_attr,
#endif

View File

@ -28,3 +28,11 @@ try:
A().f.x = 1
except AttributeError:
print('AttributeError')
# equality tests
i = A() # need methods to be bound from the same instance
m = i.f
print(m == i.f)
print(m != i.f)
print(m == i.g)
print(m != i.g)