Skip to content

Commit 008c364

Browse files
committed
Add support for __prepare__.
1 parent 4be2f24 commit 008c364

File tree

3 files changed

+25
-4
lines changed

3 files changed

+25
-4
lines changed

tests/snippets/metaclasses.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ class MC(type):
22
classes = []
33
count = 0
44

5+
def __prepare__(name, bases):
6+
return {'prepared': True}
7+
58
def __new__(cls, name, bases, namespace):
69
MC.classes.append(name)
710
return type.__new__(cls, name, bases, namespace)
@@ -16,10 +19,13 @@ def __new__(cls, count):
1619
self.count = count
1720
return self
1821

19-
class D(object, metaclass=MC):
22+
class D(metaclass=MC):
2023
pass
2124

2225
assert MC == type(C)
2326
assert C == type(C())
2427
assert MC.classes == ['C', 'D']
2528
assert C().count == 2
29+
30+
assert C.prepared
31+
assert D.prepared

vm/src/builtins.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,19 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
640640
let metaclass = args.get_kwarg("metaclass", vm.get_type());
641641

642642
bases.push(vm.context().object());
643-
let namespace = vm.new_dict();
643+
let bases = vm.context().new_tuple(bases);
644+
645+
// Prepare uses full __getattribute__ resolution chain.
646+
let prepare_name = vm.new_str("__prepare__".to_string());
647+
let prepare = vm.get_attribute(metaclass.clone(), prepare_name)?;
648+
let namespace = vm.invoke(
649+
prepare,
650+
PyFuncArgs {
651+
args: vec![name_arg.clone(), bases.clone()],
652+
kwargs: vec![],
653+
},
654+
)?;
655+
644656
&vm.invoke(
645657
function,
646658
PyFuncArgs {
@@ -649,8 +661,6 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
649661
},
650662
);
651663

652-
let bases = vm.context().new_tuple(bases);
653-
654664
// Special case: __new__ must be looked up on the metaclass, not the meta-metaclass as
655665
// per vm.call(metaclass, "__new__", ...)
656666
let new = metaclass.get_attr("__new__").unwrap();

vm/src/obj/objtype.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub fn init(context: &PyContext) {
2828
type_type.set_attr("__mro__", context.new_member_descriptor(type_mro));
2929
type_type.set_attr("__class__", context.new_member_descriptor(type_new));
3030
type_type.set_attr("__repr__", context.new_rustfunc(type_repr));
31+
type_type.set_attr("__prepare__", context.new_rustfunc(type_prepare));
3132
}
3233

3334
fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -300,6 +301,10 @@ fn type_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
300301
Ok(vm.new_str(format!("<class '{}'>", type_name)))
301302
}
302303

304+
fn type_prepare(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
305+
Ok(vm.new_dict())
306+
}
307+
303308
#[cfg(test)]
304309
mod tests {
305310
use super::{linearise_mro, new};

0 commit comments

Comments
 (0)