Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,8 +479,6 @@ def __getitem__(self, i):
self.assertEqual(a[2], 102)
self.assertEqual(a[100:200], (100,200))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_metaclass(self):
# Testing metaclasses...
class C(metaclass=type):
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_super.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,6 @@ def f():

self.assertIs(test_class, A)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test___class___delayed(self):
# See issue #23722
test_namespace = None
Expand Down Expand Up @@ -236,8 +234,6 @@ def f(self):
with self.assertRaises(AttributeError):
WithClassRef.__classcell__

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test___classcell___missing(self):
# See issue #23722
# Some metaclasses may not pass the original namespace to type.__new__
Expand Down
54 changes: 35 additions & 19 deletions crates/vm/src/stdlib/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -892,13 +892,12 @@ mod builtins {
#[pyfunction]
pub fn __build_class__(
function: PyRef<PyFunction>,
qualified_name: PyStrRef,
name: PyStrRef,
bases: PosArgs,
mut kwargs: KwArgs,
vm: &VirtualMachine,
) -> PyResult {
let name = qualified_name.as_str().split('.').next_back().unwrap();
let name_obj = vm.ctx.new_str(name);
let name_obj: PyObjectRef = name.clone().into();

// Update bases.
let mut new_bases: Option<Vec<PyObjectRef>> = None;
Expand Down Expand Up @@ -942,20 +941,33 @@ mod builtins {
.downcast_exact::<PyType>(vm)
.map(|m| m.into_pyref())
})
.unwrap_or_else(|| Ok(vm.ctx.types.type_type.to_owned()));
.unwrap_or_else(|| {
// if there are no bases, use type; else get the type of the first base
Ok(if bases.is_empty() {
vm.ctx.types.type_type.to_owned()
} else {
bases.first().unwrap().class().to_owned()
})
});

let (metaclass, meta_name) = match metaclass {
Ok(mut metaclass) => {
for base in bases.iter() {
let base_class = base.class();
// if winner is subtype of tmptype, continue (winner is more derived)
if metaclass.fast_issubclass(base_class) {
continue;
}
// if tmptype is subtype of winner, update (tmptype is more derived)
if base_class.fast_issubclass(&metaclass) {
metaclass = base.class().to_owned();
} else if !metaclass.fast_issubclass(base_class) {
return Err(vm.new_type_error(
"metaclass conflict: the metaclass of a derived class must be a (non-strict) \
subclass of the metaclasses of all its bases",
));
metaclass = base_class.to_owned();
continue;
}
// Metaclass conflict
return Err(vm.new_type_error(
"metaclass conflict: the metaclass of a derived class must be a (non-strict) \
subclass of the metaclasses of all its bases",
));
}
let meta_name = metaclass.slot_name();
(metaclass.to_owned().into(), meta_name.to_owned())
Expand All @@ -969,8 +981,7 @@ mod builtins {
let namespace = vm
.get_attribute_opt(metaclass.clone(), identifier!(vm, __prepare__))?
.map_or(Ok(vm.ctx.new_dict().into()), |prepare| {
let args =
FuncArgs::new(vec![name_obj.clone().into(), bases.clone()], kwargs.clone());
let args = FuncArgs::new(vec![name_obj.clone(), bases.clone()], kwargs.clone());
prepare.call(args, vm)
})?;

Expand Down Expand Up @@ -1013,7 +1024,7 @@ mod builtins {
.del_item(vm.ctx.intern_str(".type_params"), vm)
.ok();

let args = FuncArgs::new(vec![name_obj.into(), bases, namespace.into()], kwargs);
let args = FuncArgs::new(vec![name_obj, bases, namespace.into()], kwargs);
let class = metaclass.call(args, vm)?;

// For PEP 695 classes, set __type_params__ on the class from the function
Expand All @@ -1028,16 +1039,21 @@ mod builtins {
class.set_attr(identifier!(vm, __parameters__), type_params, vm)?;
}

if let Some(ref classcell) = classcell {
let classcell = classcell.get().ok_or_else(|| {
vm.new_type_error(format!(
"__class__ not set defining {meta_name:?} as {class:?}. Was __classcell__ propagated to type.__new__?"
// only check cell if cls is a type and cell is a cell object
if let Some(ref classcell) = classcell
&& class.fast_isinstance(vm.ctx.types.type_type)
{
let cell_value = classcell.get().ok_or_else(|| {
vm.new_runtime_error(format!(
"__class__ not set defining {:?} as {:?}. Was __classcell__ propagated to type.__new__?",
name, class
))
})?;

if !classcell.is(&class) {
if !cell_value.is(&class) {
return Err(vm.new_type_error(format!(
"__class__ set to {classcell:?} defining {meta_name:?} as {class:?}"
"__class__ set to {:?} defining {:?} as {:?}",
cell_value, name, class
)));
}
}
Expand Down
Loading