Skip to content

Commit 081a33f

Browse files
Merge pull request RustPython#185 from RustPython/prepare
Add support for __prepare__.
2 parents 039b5bb + 008c364 commit 081a33f

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
@@ -635,7 +635,19 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
635635
let metaclass = args.get_kwarg("metaclass", vm.get_type());
636636

637637
bases.push(vm.context().object());
638-
let namespace = vm.new_dict();
638+
let bases = vm.context().new_tuple(bases);
639+
640+
// Prepare uses full __getattribute__ resolution chain.
641+
let prepare_name = vm.new_str("__prepare__".to_string());
642+
let prepare = vm.get_attribute(metaclass.clone(), prepare_name)?;
643+
let namespace = vm.invoke(
644+
prepare,
645+
PyFuncArgs {
646+
args: vec![name_arg.clone(), bases.clone()],
647+
kwargs: vec![],
648+
},
649+
)?;
650+
639651
&vm.invoke(
640652
function,
641653
PyFuncArgs {
@@ -644,8 +656,6 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
644656
},
645657
);
646658

647-
let bases = vm.context().new_tuple(bases);
648-
649659
// Special case: __new__ must be looked up on the metaclass, not the meta-metaclass as
650660
// per vm.call(metaclass, "__new__", ...)
651661
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 {
@@ -291,6 +292,10 @@ fn type_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
291292
Ok(vm.new_str(format!("<class '{}'>", type_name)))
292293
}
293294

295+
fn type_prepare(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
296+
Ok(vm.new_dict())
297+
}
298+
294299
#[cfg(test)]
295300
mod tests {
296301
use super::{linearise_mro, new};

0 commit comments

Comments
 (0)