@@ -125,24 +125,36 @@ impl Initializer for PyBaseObject {
125125 }
126126
127127 let typ = zelf. class ( ) ;
128- let object_type = & vm. ctx . types . object_type ;
128+ let object_type = vm. ctx . types . object_type ;
129129
130130 let typ_init = typ. slots . init . load ( ) . map ( |f| f as usize ) ;
131131 let object_init = object_type. slots . init . load ( ) . map ( |f| f as usize ) ;
132132
133133 // if (type->tp_init != object_init) → first error
134- if typ_init != object_init {
134+ // Note: typ_init can be None due to SLOTDEFINED check in copyslot_if_none,
135+ // which means the slot was not explicitly defined. Treat None as equivalent
136+ // to object's init (i.e., inherited but not overridden).
137+ if typ_init. is_some ( ) && typ_init != object_init {
135138 return Err ( vm. new_type_error (
136139 "object.__init__() takes exactly one argument (the instance to initialize)"
137140 . to_owned ( ) ,
138141 ) ) ;
139142 }
140143
141- let typ_new = typ. slots . new . load ( ) . map ( |f| f as usize ) ;
142- let object_new = object_type. slots . new . load ( ) . map ( |f| f as usize ) ;
143-
144144 // if (type->tp_new == object_new) → second error
145- if typ_new == object_new {
145+ // Can't compare slot pointers for __new__ because update_one_slot
146+ // unconditionally sets new_wrapper for all heap types. Instead, check
147+ // if any non-object class in the MRO defines __new__ in its own dict.
148+ // This is safe because add_operators skips __new__ — only types that
149+ // explicitly define __new__ have it in their dict.
150+ let has_custom_new = typ. mro . read ( ) . iter ( ) . any ( |cls| {
151+ !cls. is ( object_type)
152+ && cls
153+ . attributes
154+ . read ( )
155+ . contains_key ( identifier ! ( vm, __new__) )
156+ } ) ;
157+ if !has_custom_new {
146158 return Err ( vm. new_type_error ( format ! (
147159 "{}.__init__() takes exactly one argument (the instance to initialize)" ,
148160 typ. name( )
0 commit comments