Skip to content

Commit 4be2f24

Browse files
Merge pull request RustPython#184 from RustPython/metaclasses
WIP - Specify metaclass and create class with correct type.
2 parents 89cfdbe + aa10e9b commit 4be2f24

File tree

10 files changed

+266
-71
lines changed

10 files changed

+266
-71
lines changed

src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool
127127
}
128128
Err(err) => {
129129
// Enum rather than special string here.
130-
let msg = match vm.get_attribute(err.clone(), "msg") {
130+
let name = vm.new_str("msg".to_string());
131+
let msg = match vm.get_attribute(err.clone(), name) {
131132
Ok(value) => objstr::get_value(&value),
132133
Err(_) => panic!("Expected msg attribute on exception object!"),
133134
};

tests/snippets/metaclasses.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class MC(type):
2+
classes = []
3+
count = 0
4+
5+
def __new__(cls, name, bases, namespace):
6+
MC.classes.append(name)
7+
return type.__new__(cls, name, bases, namespace)
8+
9+
def __call__(cls):
10+
MC.count += 1
11+
return type.__call__(cls, MC.count)
12+
13+
class C(object, metaclass=MC):
14+
def __new__(cls, count):
15+
self = object.__new__(cls)
16+
self.count = count
17+
return self
18+
19+
class D(object, metaclass=MC):
20+
pass
21+
22+
assert MC == type(C)
23+
assert C == type(C())
24+
assert MC.classes == ['C', 'D']
25+
assert C().count == 2

vm/src/builtins.rs

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn dir_object(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyObjectRef {
5050

5151
fn builtin_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
5252
arg_check!(vm, args, required = [(x, None)]);
53-
match vm.get_attribute(x.clone(), &"__abs__") {
53+
match vm.get_method(x.clone(), "__abs__") {
5454
Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![], vec![])),
5555
Err(..) => Err(vm.new_type_error("bad operand for abs".to_string())),
5656
}
@@ -134,7 +134,7 @@ fn builtin_dir(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
134134

135135
fn builtin_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
136136
arg_check!(vm, args, required = [(x, None), (y, None)]);
137-
match vm.get_attribute(x.clone(), &"__divmod__") {
137+
match vm.get_method(x.clone(), "__divmod__") {
138138
Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![y.clone()], vec![])),
139139
Err(..) => Err(vm.new_type_error("unsupported operand type(s) for divmod".to_string())),
140140
}
@@ -218,11 +218,7 @@ fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
218218
args,
219219
required = [(obj, None), (attr, Some(vm.ctx.str_type()))]
220220
);
221-
if let PyObjectKind::String { ref value } = attr.borrow().kind {
222-
vm.get_attribute(obj.clone(), value)
223-
} else {
224-
panic!("argument checking failure: attr not string")
225-
}
221+
vm.get_attribute(obj.clone(), attr.clone())
226222
}
227223

228224
// builtin_globals
@@ -233,15 +229,11 @@ fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
233229
args,
234230
required = [(obj, None), (attr, Some(vm.ctx.str_type()))]
235231
);
236-
if let PyObjectKind::String { ref value } = attr.borrow().kind {
237-
let has_attr = match vm.get_attribute(obj.clone(), value) {
238-
Ok(..) => true,
239-
Err(..) => false,
240-
};
241-
Ok(vm.context().new_bool(has_attr))
242-
} else {
243-
panic!("argument checking failure: attr not string")
244-
}
232+
let has_attr = match vm.get_attribute(obj.clone(), attr.clone()) {
233+
Ok(..) => true,
234+
Err(..) => false,
235+
};
236+
Ok(vm.context().new_bool(has_attr))
245237
}
246238

247239
fn builtin_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -308,7 +300,7 @@ fn builtin_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
308300
}
309301
_ => {
310302
let len_method_name = "__len__".to_string();
311-
match vm.get_attribute(obj.clone(), &len_method_name) {
303+
match vm.get_method(obj.clone(), &len_method_name) {
312304
Ok(value) => vm.invoke(value, PyFuncArgs::default()),
313305
Err(..) => Err(vm.context().new_str(
314306
format!(
@@ -444,7 +436,7 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
444436
optional = [(mod_value, Some(vm.ctx.int_type()))]
445437
);
446438
let pow_method_name = "__pow__".to_string();
447-
let result = match vm.get_attribute(x.clone(), &pow_method_name) {
439+
let result = match vm.get_method(x.clone(), &pow_method_name) {
448440
Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![y.clone()], vec![])),
449441
Err(..) => Err(vm.new_type_error("unsupported operand type(s) for pow".to_string())),
450442
};
@@ -454,7 +446,7 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
454446
match mod_value {
455447
Some(mod_value) => {
456448
let mod_method_name = "__mod__".to_string();
457-
match vm.get_attribute(
449+
match vm.get_method(
458450
result.expect("result not defined").clone(),
459451
&mod_method_name,
460452
) {
@@ -644,14 +636,10 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
644636
pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
645637
let function = args.shift();
646638
let name_arg = args.shift();
647-
let name_arg_ref = name_arg.borrow();
648-
let name = match name_arg_ref.kind {
649-
PyObjectKind::String { ref value } => value,
650-
_ => panic!("Class name must by a string!"),
651-
};
652639
let mut bases = args.args.clone();
640+
let metaclass = args.get_kwarg("metaclass", vm.get_type());
641+
653642
bases.push(vm.context().object());
654-
let metaclass = vm.get_type();
655643
let namespace = vm.new_dict();
656644
&vm.invoke(
657645
function,
@@ -660,5 +648,18 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
660648
kwargs: vec![],
661649
},
662650
);
663-
objtype::new(metaclass, name, bases, namespace)
651+
652+
let bases = vm.context().new_tuple(bases);
653+
654+
// Special case: __new__ must be looked up on the metaclass, not the meta-metaclass as
655+
// per vm.call(metaclass, "__new__", ...)
656+
let new = metaclass.get_attr("__new__").unwrap();
657+
let wrapped = vm.call_get_descriptor(new, metaclass)?;
658+
vm.invoke(
659+
wrapped,
660+
PyFuncArgs {
661+
args: vec![name_arg, bases, namespace],
662+
kwargs: vec![],
663+
},
664+
)
664665
}

vm/src/compile.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ impl Compiler {
436436
name,
437437
body,
438438
bases,
439-
keywords: _,
439+
keywords,
440440
decorator_list,
441441
} => {
442442
self.prepare_decorators(decorator_list)?;
@@ -483,9 +483,34 @@ impl Compiler {
483483
for base in bases {
484484
self.compile_expression(base)?;
485485
}
486-
self.emit(Instruction::CallFunction {
487-
typ: CallType::Positional(2 + bases.len()),
488-
});
486+
487+
if keywords.len() > 0 {
488+
let mut kwarg_names = vec![];
489+
for keyword in keywords {
490+
if let Some(name) = &keyword.name {
491+
kwarg_names.push(bytecode::Constant::String {
492+
value: name.to_string(),
493+
});
494+
} else {
495+
// This means **kwargs!
496+
panic!("name must be set");
497+
}
498+
self.compile_expression(&keyword.value)?;
499+
}
500+
501+
self.emit(Instruction::LoadConst {
502+
value: bytecode::Constant::Tuple {
503+
elements: kwarg_names,
504+
},
505+
});
506+
self.emit(Instruction::CallFunction {
507+
typ: CallType::Keyword(2 + keywords.len() + bases.len()),
508+
});
509+
} else {
510+
self.emit(Instruction::CallFunction {
511+
typ: CallType::Positional(2 + bases.len()),
512+
});
513+
}
489514

490515
self.apply_decorators(decorator_list);
491516

vm/src/frame.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,8 @@ impl Frame {
123123
&exception,
124124
&vm.ctx.exceptions.base_exception_type
125125
));
126-
let traceback = vm
127-
.get_attribute(exception.clone(), &"__traceback__".to_string())
128-
.unwrap();
126+
let traceback_name = vm.new_str("__traceback__".to_string());
127+
let traceback = vm.get_attribute(exception.clone(), traceback_name).unwrap();
129128
trace!("Adding to traceback: {:?} {:?}", traceback, lineno);
130129
let pos = vm.ctx.new_tuple(vec![
131130
vm.ctx.new_str(filename.clone()),
@@ -1007,6 +1006,7 @@ impl Frame {
10071006

10081007
fn load_attr(&mut self, vm: &mut VirtualMachine, attr_name: &str) -> FrameResult {
10091008
let parent = self.pop_value();
1009+
let attr_name = vm.new_str(attr_name.to_string());
10101010
let obj = vm.get_attribute(parent, attr_name)?;
10111011
self.push_value(obj);
10121012
Ok(None)

vm/src/obj/objbool.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result<bool, PyObje
1515
PyObjectKind::String { ref value } => !value.is_empty(),
1616
PyObjectKind::None { .. } => false,
1717
_ => {
18-
if let Ok(f) = objtype::get_attribute(vm, obj.clone(), &String::from("__bool__")) {
18+
if let Ok(f) = vm.get_method(obj.clone(), "__bool__") {
1919
let bool_res = vm.invoke(f, PyFuncArgs::default())?;
2020
let v = match bool_res.borrow().kind {
2121
PyObjectKind::Integer { ref value } => !value.is_zero(),

vm/src/obj/objobject.rs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use super::super::pyobject::{
55
use super::super::vm::VirtualMachine;
66
use super::objbool;
77
use super::objdict;
8+
use super::objstr;
89
use super::objtype;
910

1011
pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
@@ -15,12 +16,6 @@ pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
1516
Ok(obj)
1617
}
1718

18-
pub fn call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
19-
let instance = args.shift();
20-
let function = objtype::get_attribute(vm, instance, &String::from("__call__"))?;
21-
vm.invoke(function, args)
22-
}
23-
2419
pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) {
2520
(*object_type.borrow_mut()).kind = PyObjectKind::Class {
2621
name: String::from("object"),
@@ -101,6 +96,10 @@ pub fn init(context: &PyContext) {
10196
object.set_attr("__hash__", context.new_rustfunc(object_hash));
10297
object.set_attr("__str__", context.new_rustfunc(object_str));
10398
object.set_attr("__repr__", context.new_rustfunc(object_repr));
99+
object.set_attr(
100+
"__getattribute__",
101+
context.new_rustfunc(object_getattribute),
102+
);
104103
}
105104

106105
fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
@@ -114,3 +113,54 @@ fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
114113
_ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())),
115114
}
116115
}
116+
117+
fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
118+
arg_check!(
119+
vm,
120+
args,
121+
required = [
122+
(obj, Some(vm.ctx.object())),
123+
(name_str, Some(vm.ctx.str_type()))
124+
]
125+
);
126+
let name = objstr::get_value(&name_str);
127+
trace!("object.__getattribute__({:?}, {:?})", obj, name);
128+
let cls = obj.typ();
129+
130+
if let Some(attr) = cls.get_attr(&name) {
131+
let attr_class = attr.typ();
132+
if attr_class.has_attr("__set__") {
133+
if let Some(descriptor) = attr_class.get_attr("__get__") {
134+
return vm.invoke(
135+
descriptor,
136+
PyFuncArgs {
137+
args: vec![attr, obj.clone(), cls],
138+
kwargs: vec![],
139+
},
140+
);
141+
}
142+
}
143+
}
144+
145+
if let Some(obj_attr) = obj.get_attr(&name) {
146+
Ok(obj_attr)
147+
} else if let Some(attr) = cls.get_attr(&name) {
148+
vm.call_get_descriptor(attr, obj.clone())
149+
} else {
150+
if let Some(getter) = cls.get_attr("__getattr__") {
151+
vm.invoke(
152+
getter,
153+
PyFuncArgs {
154+
args: vec![cls, name_str.clone()],
155+
kwargs: vec![],
156+
},
157+
)
158+
} else {
159+
let attribute_error = vm.context().exceptions.attribute_error.clone();
160+
Err(vm.new_exception(
161+
attribute_error,
162+
format!("{:?} object has no attribute {}", cls, name),
163+
))
164+
}
165+
}
166+
}

0 commit comments

Comments
 (0)