From d198d2907a0837cb82f4d38984bb2ba068cacf77 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Sat, 14 Nov 2020 12:03:30 +0000 Subject: [PATCH] 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 --- py/objboundmeth.c | 23 +++++++++++++++++++++++ tests/basics/boundmeth1.py | 8 ++++++++ 2 files changed, 31 insertions(+) diff --git a/py/objboundmeth.c b/py/objboundmeth.c index a3e1d302d..1199e3eec 100644 --- a/py/objboundmeth.c +++ b/py/objboundmeth.c @@ -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 diff --git a/tests/basics/boundmeth1.py b/tests/basics/boundmeth1.py index f483ba406..aa900c669 100644 --- a/tests/basics/boundmeth1.py +++ b/tests/basics/boundmeth1.py @@ -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)