From 9511f60f016547ab00f634d451c230351bd8b225 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 11 May 2014 03:12:36 +0300 Subject: [PATCH] py: Don't try to "bind" types store as attributes of objects. This was hit when trying to make urlparse.py from stdlib run. Took quite some time to debug. TODO: Reconsile bound method creation process better, maybe callable is to generic type to bind at all? --- py/objtype.c | 3 +++ py/runtime.c | 3 +++ tests/basics/class_store_class.py | 44 +++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 tests/basics/class_store_class.py diff --git a/py/objtype.c b/py/objtype.c index 345eee7140..ef5f6b9d9c 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -333,6 +333,9 @@ STATIC void instance_convert_return_attr(mp_obj_t self, mp_obj_t member, mp_obj_ // return a bound method, with self being the type of this object dest[0] = ((mp_obj_static_class_method_t*)member)->fun; dest[1] = mp_obj_get_type(self); + } else if (MP_OBJ_IS_TYPE(member, &mp_type_type)) { + // Don't try to bind types + dest[0] = member; } else if (mp_obj_is_callable(member)) { // return a bound method, with self being this object dest[0] = member; diff --git a/py/runtime.c b/py/runtime.c index 1479d8cba3..a2de2d75b1 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -840,6 +840,9 @@ void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest) { // return a bound method, with self being the type of this object dest[0] = ((mp_obj_static_class_method_t*)elem->value)->fun; dest[1] = mp_obj_get_type(base); + } else if (MP_OBJ_IS_TYPE(elem->value, &mp_type_type)) { + // Don't try to bind types + dest[0] = elem->value; } else if (mp_obj_is_callable(elem->value)) { // return a bound method, with self being this object dest[0] = elem->value; diff --git a/tests/basics/class_store_class.py b/tests/basics/class_store_class.py new file mode 100644 index 0000000000..c765fceaa8 --- /dev/null +++ b/tests/basics/class_store_class.py @@ -0,0 +1,44 @@ +# Inspired by urlparse.py from CPython 3.3 stdlib +# There was a bug in MicroPython that under some conditions class stored +# in instance attribute later was returned "bound" as if it was a method, +# which caused class constructor to receive extra argument. +from collections import namedtuple + +_DefragResultBase = namedtuple('DefragResult', 'foo bar') + +class _ResultMixinStr(object): + def encode(self): + return self._encoded_counterpart(*(x.encode() for x in self)) + +class _ResultMixinBytes(object): + def decode(self): + return self._decoded_counterpart(*(x.decode() for x in self)) + +class DefragResult(_DefragResultBase, _ResultMixinStr): + pass + +class DefragResultBytes(_DefragResultBase, _ResultMixinBytes): + pass + + +DefragResult._encoded_counterpart = DefragResultBytes +DefragResultBytes._decoded_counterpart = DefragResult + +# Due to differences in type and native subclass printing, +# the best thing we can do here is to just test that no exceptions +# happen + +#print(DefragResult, DefragResult._encoded_counterpart) +#print(DefragResultBytes, DefragResultBytes._decoded_counterpart) + +o1 = DefragResult("a", "b") +#print(o1, type(o1)) +o2 = DefragResultBytes("a", "b") +#print(o2, type(o2)) + +#print(o1._encoded_counterpart) +_o1 = o1.encode() +print(_o1[0], _o1[1]) +#print(_o1, type(_o1)) + +print("All's ok")