diff --git a/py/obj.c b/py/obj.c index e9fed19c6c..e581495888 100644 --- a/py/obj.c +++ b/py/obj.c @@ -172,18 +172,26 @@ mp_int_t mp_obj_hash(mp_obj_t o_in) { return mp_obj_tuple_hash(o_in); } else if (MP_OBJ_IS_TYPE(o_in, &mp_type_type)) { return (mp_int_t)o_in; - - // TODO hash class and instances - // TODO delegate to __hash__ method if it exists - - } else { - if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "unhashable type")); - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, - "unhashable type: '%s'", mp_obj_get_type_str(o_in))); + } else if (MP_OBJ_IS_OBJ(o_in)) { + // if a valid __hash__ method exists, use it + mp_obj_t hash_method[2]; + mp_load_method_maybe(o_in, MP_QSTR___hash__, hash_method); + if (hash_method[0] != MP_OBJ_NULL) { + mp_obj_t hash_val = mp_call_method_n_kw(0, 0, hash_method); + if (MP_OBJ_IS_INT(hash_val)) { + return mp_obj_int_get(hash_val); + } } } + + // TODO hash class and instances - in CPython by default user created classes' __hash__ resolves to their id + + if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "unhashable type")); + } else { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, + "unhashable type: '%s'", mp_obj_get_type_str(o_in))); + } } // this function implements the '==' operator (and so the inverse of '!=') diff --git a/py/qstrdefs.h b/py/qstrdefs.h index ecb1b96236..1506d0a261 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -39,6 +39,7 @@ Q(__locals__) Q(__main__) Q(__module__) Q(__name__) +Q(__hash__) Q(__next__) Q(__qualname__) Q(__path__)