copyslot for init (#6515)

This commit is contained in:
Jeong, YunWon authored and GitHub committed 2025-12-26 09:46:33 +09:00
1 parent ae39b132e4
commit c9bf8df19c
13 files changed
+216 -129

No files matched your search

-1
View File
@@ -3189,7 +3189,6 @@ class TransformCodecTest(unittest.TestCase):
sout = reader.readline()
self.assertEqual(sout, b"\x80")
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_buffer_api_usage(self):
# We check all the transform codecs accept memoryview input
# for encoding and decoding
-8
View File
@@ -724,8 +724,6 @@ class CBytesIOTest(PyBytesIOTest):
ioclass = io.BytesIO
UnsupportedOperation = io.UnsupportedOperation
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytes_array(self):
super().test_bytes_array()
@@ -739,8 +737,6 @@ class CBytesIOTest(PyBytesIOTest):
def test_getbuffer(self):
super().test_getbuffer()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_init(self):
super().test_init()
@@ -770,8 +766,6 @@ class CBytesIOTest(PyBytesIOTest):
def test_seek(self):
super().test_seek()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_subclassing(self):
super().test_subclassing()
@@ -884,8 +878,6 @@ class CStringIOTest(PyStringIOTest):
def test_flags(self):
super().test_flags()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_init(self):
super().test_init()
-15
View File
@@ -180,11 +180,6 @@ class PyPicklerTests(AbstractPickleTests, unittest.TestCase):
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers_writable_to_readonly()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optional_frames(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_optional_frames()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_buffers_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
@@ -220,11 +215,6 @@ class PyPicklerTests(AbstractPickleTests, unittest.TestCase):
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers_writable_to_readonly()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optional_frames(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_optional_frames()
class InMemoryPickleTests(AbstractPickleTests, AbstractUnpickleTests,
BigmemPickleTests, unittest.TestCase):
@@ -309,11 +299,6 @@ class InMemoryPickleTests(AbstractPickleTests, AbstractUnpickleTests,
def test_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optional_frames(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_optional_frames()
class PersistentPicklerUnpicklerMixin(object):
def dumps(self, arg, proto=None):
-5
View File
@@ -97,11 +97,6 @@ class OptimizedPickleTests(AbstractPickleTests, unittest.TestCase):
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers_writable_to_readonly()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optional_frames(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_optional_frames()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_py_methods(self): # TODO(RUSTPYTHON): Remove this test when it passes
-28
View File
@@ -641,8 +641,6 @@ class OpenerDirectorTests(unittest.TestCase):
self.assertRaises(urllib.error.URLError, o.open, req)
self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_http_error(self):
# XXX http_error_default
# http errors are a special case
@@ -666,8 +664,6 @@ class OpenerDirectorTests(unittest.TestCase):
self.assertEqual((handler, method_name), got[:2])
self.assertEqual(args, got[2])
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_processors(self):
# *_request / *_response methods get called appropriately
o = OpenerDirector()
@@ -874,8 +870,6 @@ class HandlerTests(unittest.TestCase):
self.assertEqual(req.type, "ftp")
self.assertEqual(req.type == "ftp", ftp)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_http(self):
h = urllib.request.AbstractHTTPHandler()
@@ -1136,8 +1130,6 @@ class HandlerTests(unittest.TestCase):
self.assertEqual(newreq.host, 'www.python.org')
self.assertEqual(newreq.selector, '')
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_errors(self):
h = urllib.request.HTTPErrorProcessor()
o = h.parent = MockOpener()
@@ -1163,8 +1155,6 @@ class HandlerTests(unittest.TestCase):
self.assertEqual(o.proto, "http") # o.error called
self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_cookies(self):
cj = MockCookieJar()
h = urllib.request.HTTPCookieProcessor(cj)
@@ -1291,8 +1281,6 @@ class HandlerTests(unittest.TestCase):
MockHeaders({"location": valid_url}))
self.assertEqual(o.req.get_full_url(), valid_url)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_cookie_redirect(self):
# cookies shouldn't leak into redirected requests
from http.cookiejar import CookieJar
@@ -1308,8 +1296,6 @@ class HandlerTests(unittest.TestCase):
o.open("http://www.example.com/")
self.assertFalse(hh.req.has_header("Cookie"))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_redirect_fragment(self):
redirected_url = 'http://www.example.com/index.html#OK\r\n\r\n'
hh = MockHTTPHandler(302, 'Location: ' + redirected_url)
@@ -1374,8 +1360,6 @@ class HandlerTests(unittest.TestCase):
request = handler.last_buf
self.assertTrue(request.startswith(expected), repr(request))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_proxy(self):
u = "proxy.example.com:3128"
for d in dict(http=u), dict(HTTP=u):
@@ -1420,8 +1404,6 @@ class HandlerTests(unittest.TestCase):
self.assertEqual(req.host, "www.python.org")
del os.environ['no_proxy']
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_proxy_https(self):
o = OpenerDirector()
ph = urllib.request.ProxyHandler(dict(https="proxy.example.com:3128"))
@@ -1509,8 +1491,6 @@ class HandlerTests(unittest.TestCase):
"http://acme.example.com/protected",
"http://acme.example.com/protected")
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basic_auth(self):
realm = "realm2@example.com"
realm2 = "realm2@example.com"
@@ -1556,8 +1536,6 @@ class HandlerTests(unittest.TestCase):
for challenge in challenges]
self.check_basic_auth(headers, realm)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_proxy_basic_auth(self):
opener = OpenerDirector()
ph = urllib.request.ProxyHandler(dict(http="proxy.example.com:3128"))
@@ -1575,8 +1553,6 @@ class HandlerTests(unittest.TestCase):
"proxy.example.com:3128",
)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basic_and_digest_auth_handlers(self):
# HTTPDigestAuthHandler raised an exception if it couldn't handle a 40*
# response (http://python.org/sf/1479302), where it should instead
@@ -1684,8 +1660,6 @@ class HandlerTests(unittest.TestCase):
self.assertEqual(len(http_handler.requests), 1)
self.assertFalse(http_handler.requests[0].has_header(auth_header))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basic_prior_auth_auto_send(self):
# Assume already authenticated if is_authenticated=True
# for APIs like Github that don't return 401
@@ -1713,8 +1687,6 @@ class HandlerTests(unittest.TestCase):
# expect request to be sent with auth header
self.assertTrue(http_handler.has_auth_header)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basic_prior_auth_send_after_first_success(self):
# Auto send auth header after authentication is successful once
-2
View File
@@ -1746,8 +1746,6 @@ class OtherTests(unittest.TestCase):
fp.write("short file")
self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_negative_central_directory_offset_raises_BadZipFile(self):
# Zip file containing an empty EOCD record
buffer = bytearray(b'PK\x05\x06' + b'\0'*18)
+19 -4
View File
@@ -223,8 +223,8 @@ pub(crate) fn impl_pyclass_impl(attr: PunctuatedNestedMeta, item: Item) -> Resul
const METHOD_DEFS: &'static [::rustpython_vm::function::PyMethodDef] = &#method_defs;
fn extend_slots(slots: &mut ::rustpython_vm::types::PyTypeSlots) {
#impl_ty::__extend_slots(slots);
#with_slots
#impl_ty::__extend_slots(slots);
}
}
}
@@ -1672,9 +1672,24 @@ fn extract_impl_attrs(attr: PunctuatedNestedMeta, item: &Ident) -> Result<Extrac
#extend_class(ctx, class);
});
with_method_defs.push(method_defs);
with_slots.push(quote_spanned! { item_span =>
#extend_slots(slots);
});
// For Initializer and Constructor traits, directly set the slot
// instead of calling __extend_slots. This ensures that the trait
// impl's override (e.g., slot_init in impl Initializer) is used,
// not the trait's default implementation.
let slot_code = if path.is_ident("Initializer") {
quote_spanned! { item_span =>
slots.init.store(Some(<Self as ::rustpython_vm::types::Initializer>::slot_init as _));
}
} else if path.is_ident("Constructor") {
quote_spanned! { item_span =>
slots.new.store(Some(<Self as ::rustpython_vm::types::Constructor>::slot_new as _));
}
} else {
quote_spanned! { item_span =>
#extend_slots(slots);
}
};
with_slots.push(slot_code);
}
} else if path.is_ident("flags") {
for meta in nested {
+24 -25
View File
@@ -120,44 +120,36 @@ impl Initializer for PyBaseObject {
// object_init: excess_args validation
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
if args.is_empty() {
return Ok(());
}
let typ = zelf.class();
let object_type = &vm.ctx.types.object_type;
let typ_init = typ.slots.init.load().map(|f| f as usize);
let object_init = object_type.slots.init.load().map(|f| f as usize);
// if (type->tp_init != object_init) → first error
if typ_init != object_init {
return Err(vm.new_type_error(
"object.__init__() takes exactly one argument (the instance to initialize)"
.to_owned(),
));
}
let typ_new = typ.slots.new.load().map(|f| f as usize);
let object_new = object_type.slots.new.load().map(|f| f as usize);
// For heap types (Python classes), check if __new__ is defined anywhere in MRO
// (before object) because heap types always have slots.new = new_wrapper via MRO
let is_heap_type = typ
.slots
.flags
.contains(crate::types::PyTypeFlags::HEAPTYPE);
let new_overridden = if is_heap_type {
// Check if __new__ is defined in any base class (excluding object)
let new_id = identifier!(vm, __new__);
typ.mro_collect()
.into_iter()
.take_while(|t| !std::ptr::eq(t.as_ref(), *object_type))
.any(|t| t.attributes.read().contains_key(new_id))
} else {
// For built-in types, use slot comparison
typ_new != object_new
};
// If both __init__ and __new__ are overridden, allow excess args
if typ_init != object_init && new_overridden {
return Ok(());
}
// Otherwise, reject excess args
if !args.is_empty() {
// if (type->tp_new == object_new) → second error
if typ_new == object_new {
return Err(vm.new_type_error(format!(
"{}.__init__() takes exactly one argument (the instance to initialize)",
typ.name()
)));
}
// Both conditions false → OK (e.g., tuple, dict with custom __new__)
Ok(())
}
@@ -591,6 +583,13 @@ pub fn object_set_dict(obj: PyObjectRef, dict: PyDictRef, vm: &VirtualMachine) -
}
pub fn init(ctx: &Context) {
// Manually set init slot - derive macro doesn't generate extend_slots
// for trait impl that overrides #[pyslot] method
ctx.types
.object_type
.slots
.init
.store(Some(<PyBaseObject as Initializer>::slot_init));
PyBaseObject::extend_class(ctx, ctx.types.object_type);
}
+61 -9
View File
@@ -25,8 +25,8 @@ use crate::{
object::{Traverse, TraverseFn},
protocol::{PyIterReturn, PyNumberMethods},
types::{
AsNumber, Callable, Constructor, GetAttr, PyTypeFlags, PyTypeSlots, Representable, SetAttr,
TypeDataRef, TypeDataRefMut, TypeDataSlot,
AsNumber, Callable, Constructor, GetAttr, Initializer, PyTypeFlags, PyTypeSlots,
Representable, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot,
},
};
use indexmap::{IndexMap, map::Entry};
@@ -412,8 +412,10 @@ impl PyType {
}
pub(crate) fn init_slots(&self, ctx: &Context) {
// Inherit slots from direct bases (not MRO)
for base in self.bases.read().iter() {
// Inherit slots from MRO
// Note: self.mro does NOT include self, so we iterate all elements
let mro: Vec<_> = self.mro.read().iter().cloned().collect();
for base in mro.iter() {
self.inherit_slots(base);
}
@@ -460,7 +462,7 @@ impl PyType {
}
}
/// Inherit slots from base type. typeobject.c: inherit_slots
/// Inherit slots from base type. inherit_slots
pub(crate) fn inherit_slots(&self, base: &Self) {
macro_rules! copyslot {
($slot:ident) => {
@@ -472,6 +474,28 @@ impl PyType {
};
}
// Copy init slot only if base actually defines it (not just inherited)
// This is needed for multiple inheritance where a later base might
// have a more specific init slot
macro_rules! copyslot_defined {
($slot:ident) => {
if self.slots.$slot.load().is_none() {
if let Some(base_val) = base.slots.$slot.load() {
// SLOTDEFINED: base->SLOT && (basebase == NULL || base->SLOT != basebase->SLOT)
let basebase = base.base.as_ref();
let slot_defined = match basebase {
None => true,
Some(bb) => bb.slots.$slot.load().map(|v| v as usize)
!= Some(base_val as usize),
};
if slot_defined {
self.slots.$slot.store(Some(base_val));
}
}
}
};
}
// Core slots
copyslot!(hash);
copyslot!(call);
@@ -484,9 +508,8 @@ impl PyType {
copyslot!(iternext);
copyslot!(descr_get);
copyslot!(descr_set);
// Note: init is NOT inherited here because object_init has special
// handling in CPython (checks if type->tp_init != object_init).
// TODO: implement proper init inheritance with object_init check
// init uses SLOTDEFINED check for multiple inheritance support
copyslot_defined!(init);
copyslot!(del);
// new is handled by set_new()
// as_buffer is inherited at type creation time (not AtomicCell)
@@ -832,7 +855,16 @@ impl Py<PyType> {
}
#[pyclass(
with(Py, Constructor, GetAttr, SetAttr, Callable, AsNumber, Representable),
with(
Py,
Constructor,
Initializer,
GetAttr,
SetAttr,
Callable,
AsNumber,
Representable
),
flags(BASETYPE)
)]
impl PyType {
@@ -1532,6 +1564,26 @@ fn get_doc_from_internal_doc<'a>(name: &str, internal_doc: &'a str) -> &'a str {
internal_doc
}
impl Initializer for PyType {
type Args = FuncArgs;
// type_init
fn slot_init(_zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
// type.__init__() takes 1 or 3 arguments
if args.args.len() == 1 && !args.kwargs.is_empty() {
return Err(vm.new_type_error("type.__init__() takes no keyword arguments".to_owned()));
}
if args.args.len() != 1 && args.args.len() != 3 {
return Err(vm.new_type_error("type.__init__() takes 1 or 3 arguments".to_owned()));
}
Ok(())
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
impl GetAttr for PyType {
fn getattro(zelf: &Py<Self>, name_str: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
#[cold]
+3 -11
View File
@@ -353,21 +353,13 @@ pub(super) mod types {
impl Initializer for PyBaseExceptionGroup {
type Args = FuncArgs;
fn slot_init(
_zelf: PyObjectRef,
_args: ::rustpython_vm::function::FuncArgs,
_vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult<()> {
// CPython's BaseExceptionGroup.__init__ just calls BaseException.__init__
// which stores args as-is. Since __new__ already set up the correct args
// (message, exceptions_tuple), we don't need to do anything here.
// This also allows subclasses to pass extra arguments to __new__ without
// __init__ complaining about argument count.
fn slot_init(_zelf: PyObjectRef, _args: FuncArgs, _vm: &VirtualMachine) -> PyResult<()> {
// No-op: __new__ already set up the correct args (message, exceptions_tuple)
Ok(())
}
fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
unreachable!("slot_init is overridden")
}
}
+39 -18
View File
@@ -3679,23 +3679,31 @@ mod _io {
}
impl Constructor for StringIO {
type Args = FuncArgs;
fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
Ok(Self {
_base: Default::default(),
buffer: PyRwLock::new(BufferedIO::new(Cursor::new(Vec::new()))),
closed: AtomicCell::new(false),
})
}
}
impl Initializer for StringIO {
type Args = StringIONewArgs;
#[allow(unused_variables)]
fn py_new(
_cls: &Py<PyType>,
fn init(
zelf: PyRef<Self>,
Self::Args { object, newline }: Self::Args,
_vm: &VirtualMachine,
) -> PyResult<Self> {
) -> PyResult<()> {
let raw_bytes = object
.flatten()
.map_or_else(Vec::new, |v| v.as_bytes().to_vec());
Ok(Self {
_base: Default::default(),
buffer: PyRwLock::new(BufferedIO::new(Cursor::new(raw_bytes))),
closed: AtomicCell::new(false),
})
*zelf.buffer.write() = BufferedIO::new(Cursor::new(raw_bytes));
Ok(())
}
}
@@ -3709,7 +3717,7 @@ mod _io {
}
}
#[pyclass(flags(BASETYPE, HAS_DICT), with(Constructor))]
#[pyclass(flags(BASETYPE, HAS_DICT), with(Constructor, Initializer))]
impl StringIO {
#[pymethod]
const fn readable(&self) -> bool {
@@ -3814,22 +3822,35 @@ mod _io {
}
impl Constructor for BytesIO {
type Args = OptionalArg<Option<PyBytesRef>>;
fn py_new(_cls: &Py<PyType>, object: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
let raw_bytes = object
.flatten()
.map_or_else(Vec::new, |input| input.as_bytes().to_vec());
type Args = FuncArgs;
fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
Ok(Self {
_base: Default::default(),
buffer: PyRwLock::new(BufferedIO::new(Cursor::new(raw_bytes))),
buffer: PyRwLock::new(BufferedIO::new(Cursor::new(Vec::new()))),
closed: AtomicCell::new(false),
exports: AtomicCell::new(0),
})
}
}
impl Initializer for BytesIO {
type Args = OptionalArg<Option<ArgBytesLike>>;
fn init(zelf: PyRef<Self>, object: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
if zelf.exports.load() > 0 {
return Err(vm.new_buffer_error(
"Existing exports of data: object cannot be re-sized".to_owned(),
));
}
let raw_bytes = object
.flatten()
.map_or_else(Vec::new, |input| input.borrow_buf().to_vec());
*zelf.buffer.write() = BufferedIO::new(Cursor::new(raw_bytes));
Ok(())
}
}
impl BytesIO {
fn buffer(&self, vm: &VirtualMachine) -> PyResult<PyRwLockWriteGuard<'_, BufferedIO>> {
if !self.closed.load() {
@@ -3840,7 +3861,7 @@ mod _io {
}
}
#[pyclass(flags(BASETYPE, HAS_DICT), with(PyRef, Constructor))]
#[pyclass(flags(BASETYPE, HAS_DICT), with(PyRef, Constructor, Initializer))]
impl BytesIO {
#[pymethod]
const fn readable(&self) -> bool {
+67 -2
View File
@@ -632,13 +632,48 @@ impl PyType {
update_slot!(descr_set, descr_set_wrapper);
}
_ if name == identifier!(ctx, __init__) => {
toggle_slot!(init, init_wrapper);
// Special handling: check if this type or any base has a real __init__ method
// If only slot wrappers exist, use the inherited slot from copyslot!
if ADD {
// First check if this type has __init__ in its own dict
let has_own_init = self.attributes.read().contains_key(name);
if has_own_init {
// This type defines __init__ - use wrapper
self.slots.init.store(Some(init_wrapper));
} else if self.has_real_method_in_mro(name, ctx) {
// A base class defines a real __init__ method - use wrapper
self.slots.init.store(Some(init_wrapper));
}
// else: keep inherited slot from copyslot!
} else {
let inherited = self
.mro
.read()
.iter()
.skip(1)
.find_map(|cls| cls.slots.init.load());
self.slots.init.store(inherited);
}
}
_ if name == identifier!(ctx, __new__) => {
toggle_slot!(new, new_wrapper);
}
_ if name == identifier!(ctx, __del__) => {
toggle_slot!(del, del_wrapper);
// Same special handling as __init__
if ADD {
let has_own_del = self.attributes.read().contains_key(name);
if has_own_del || self.has_real_method_in_mro(name, ctx) {
self.slots.del.store(Some(del_wrapper));
}
} else {
let inherited = self
.mro
.read()
.iter()
.skip(1)
.find_map(|cls| cls.slots.del.load());
self.slots.del.store(inherited);
}
}
_ if name == identifier!(ctx, __bool__) => {
toggle_sub_slot!(as_number, boolean, bool_wrapper);
@@ -890,6 +925,36 @@ impl PyType {
_ => {}
}
}
/// Check if there's a real method (not a slot wrapper) for `name` anywhere in the MRO.
/// If a real method exists, we should use a wrapper function. Otherwise, use the inherited slot.
fn has_real_method_in_mro(&self, name: &'static PyStrInterned, ctx: &Context) -> bool {
use crate::builtins::descriptor::PySlotWrapper;
// Check the entire MRO (including self) for the method
for cls in self.mro.read().iter() {
if let Some(attr) = cls.attributes.read().get(name).cloned() {
// Found the method - check if it's a slot wrapper
if attr.class().is(ctx.types.wrapper_descriptor_type) {
if let Some(wrapper) = attr.downcast_ref::<PySlotWrapper>() {
// It's a slot wrapper - check if it belongs to this class
let wrapper_typ: *const _ = wrapper.typ;
let cls_ptr: *const _ = cls.as_ref();
if wrapper_typ == cls_ptr {
// Slot wrapper defined on this exact class - use inherited slot
return false;
}
}
// Inherited slot wrapper - continue checking MRO
} else {
// Real method found - use wrapper function
return true;
}
}
}
// No real method found in MRO - use inherited slot
false
}
}
/// Trait for types that can be constructed via Python's `__new__` method.
+3 -1
View File
@@ -199,8 +199,10 @@ impl TypeZoo {
/// Fill attributes of builtin types.
#[cold]
pub(crate) fn extend(context: &Context) {
type_::init(context);
// object must be initialized before type to set object.slots.init,
// which type will inherit via inherit_slots()
object::init(context);
type_::init(context);
list::init(context);
set::init(context);
tuple::init(context);