← index #1685PR #9997
Off-topic · high · value 1.230
QUERY · ISSUE

Exception.__init__ raises TypeError if overridden and called by subclass

openby mbueschopened 2015-12-05updated 2024-09-29
bugpy-core
>>> class A(Exception):
...     def __init__(self):
...         Exception.__init__(self)
... 
>>> a = A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: argument should be a 'Exception' not a 'A'

Why is Exception being enforced here, even if A is a subclass of Exception? That makes it hard to subclass from Exception and override __init__.

CANDIDATE · PULL REQUEST

py/runtime: Avoid crash on calling members of uninitialized native type.

closedby laurensvalkopened 2022-11-17updated 2024-07-25
py-core

When subclassing a native type, calling native members in __init__ before super().__init__() has been called could cause a crash. In this situation, self in mp_convert_member_lookup is the native_base_init_wrapper_obj. This check ensures that a TypeError is raised when this happens.

Also fix a typo in a related comment.


This is an attempt at fixing the following crash in Pybricks MicroPython

from pybricks.parameters import Port
from pybricks.pupdevices import Motor  # Builtin type

class NewMotor(Motor):

    def __init__(self, port):

        # Would crash. After this PR it raises a TypeError.
        print(self.angle())

        super().__init__(port)

        # Would work.
        print(self.angle())

motor = NewMotor(Port.A)

EDIT: This isn't quite the right solution as seen in the tests, so I'll have to have another look.

This variant appears to work without breaking the other tests, but still needs some cleaning up:

diff --git a/py/objtype.c b/py/objtype.c
index e0753aace..101a82beb 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -91,7 +91,7 @@ STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) {
     self->subobj[0] = native_base->make_new(native_base, n_args - 1, 0, args + 1);
     return mp_const_none;
 }
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper);
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper);
 
 #if !MICROPY_CPYTHON_COMPAT
 STATIC
diff --git a/py/runtime.c b/py/runtime.c
index 5a7474fba..62254af0d 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -1111,13 +1111,13 @@ void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t
                 #endif
                 else {
                     // Return a (built-in) bound method, with self being this object.
-                    #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG
-                    // Ensures that self is not uninitialized object.
-                    dest[0] = mp_obj_new_checked_fun(type, member);
-                    #else 
                     dest[0] = member;
-                    #endif
                     dest[1] = self;
+                    extern const mp_obj_fun_builtin_var_t native_base_init_wrapper_obj;
+                    if (self == MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj)) {
+                        // object not initialized yet
+                        mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("call super().__init__() first"));
+                    }
                 }
             } else {
                 // Return a bound method, with self being this object.

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied