Skip to content

Commit 6746784

Browse files
committed
Add classmethod and staticmethod classes.
1 parent 5ffb20b commit 6746784

File tree

4 files changed

+139
-4
lines changed

4 files changed

+139
-4
lines changed

tests/snippets/class.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,26 @@ def square(self):
1313
assert foo.y == Foo.y
1414
assert foo.x == 5
1515
assert foo.square() == 25
16+
17+
18+
class Bar:
19+
@classmethod
20+
def fubar(cls, x):
21+
assert cls is Bar
22+
assert x == 2
23+
24+
@staticmethod
25+
def kungfu(x):
26+
assert x == 3
27+
28+
29+
bar = Bar()
30+
bar.fubar(2)
31+
32+
# TODO: make below work:
33+
# Bar.fubar(2)
34+
35+
bar.kungfu(3)
36+
# TODO: make below work:
37+
# Bar.kungfu(3)
38+

vm/src/builtins.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,6 @@ fn builtin_setattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
522522

523523
// builtin_slice
524524
// builtin_sorted
525-
// builtin_staticmethod
526525

527526
fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
528527
arg_check!(vm, args, required = [(iterable, None)]);
@@ -557,6 +556,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
557556
dict.insert(String::from("bytearray"), ctx.bytearray_type());
558557
dict.insert(String::from("bytes"), ctx.bytes_type());
559558
dict.insert(String::from("chr"), ctx.new_rustfunc(builtin_chr));
559+
dict.insert(String::from("classmethod"), ctx.classmethod_type());
560560
dict.insert(String::from("compile"), ctx.new_rustfunc(builtin_compile));
561561
dict.insert(String::from("complex"), ctx.complex_type());
562562
dict.insert(String::from("delattr"), ctx.new_rustfunc(builtin_delattr));
@@ -597,6 +597,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
597597
dict.insert(String::from("repr"), ctx.new_rustfunc(builtin_repr));
598598
dict.insert(String::from("set"), ctx.set_type());
599599
dict.insert(String::from("setattr"), ctx.new_rustfunc(builtin_setattr));
600+
dict.insert(String::from("staticmethod"), ctx.staticmethod_type());
600601
dict.insert(String::from("str"), ctx.str_type());
601602
dict.insert(String::from("sum"), ctx.new_rustfunc(builtin_sum));
602603
dict.insert(String::from("tuple"), ctx.tuple_type());
@@ -681,13 +682,13 @@ pub fn builtin_build_class_(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> Py
681682
},
682683
)?;
683684

684-
&vm.invoke(
685+
vm.invoke(
685686
function,
686687
PyFuncArgs {
687688
args: vec![namespace.clone()],
688689
kwargs: vec![],
689690
},
690-
);
691+
)?;
691692

692693
vm.call_method(&metaclass, "__call__", vec![name_arg, bases, namespace])
693694
}

vm/src/obj/objfunction.rs

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1-
use super::super::pyobject::{AttributeProtocol, PyContext, PyFuncArgs, PyResult};
1+
use super::super::pyobject::{
2+
AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult,
3+
TypeProtocol,
4+
};
25
use super::super::vm::VirtualMachine;
6+
use super::objtype;
37

48
pub fn init(context: &PyContext) {
59
let ref function_type = context.function_type;
610
function_type.set_attr("__get__", context.new_rustfunc(bind_method));
711

812
let ref member_descriptor_type = context.member_descriptor_type;
913
member_descriptor_type.set_attr("__get__", context.new_rustfunc(member_get));
14+
15+
let ref classmethod_type = context.classmethod_type;
16+
classmethod_type.set_attr("__get__", context.new_rustfunc(classmethod_get));
17+
classmethod_type.set_attr("__new__", context.new_rustfunc(classmethod_new));
18+
19+
let ref staticmethod_type = context.staticmethod_type;
20+
staticmethod_type.set_attr("__get__", context.new_rustfunc(staticmethod_get));
21+
staticmethod_type.set_attr("__new__", context.new_rustfunc(staticmethod_new));
1022
}
1123

1224
fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -22,3 +34,83 @@ fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
2234
}
2335
}
2436
}
37+
38+
// Classmethod type methods:
39+
fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
40+
trace!("classmethod.__get__ {:?}", args.args);
41+
arg_check!(
42+
vm,
43+
args,
44+
required = [
45+
(cls, Some(vm.ctx.classmethod_type())),
46+
(_inst, None),
47+
(owner, None)
48+
]
49+
);
50+
match cls.get_attr("function") {
51+
Some(function) => {
52+
let py_obj = owner.clone();
53+
let py_method = vm.new_bound_method(function, py_obj);
54+
Ok(py_method)
55+
}
56+
None => {
57+
let attribute_error = vm.context().exceptions.attribute_error.clone();
58+
Err(vm.new_exception(
59+
attribute_error,
60+
String::from("Attribute Error: classmethod must have 'function' attribute"),
61+
))
62+
}
63+
}
64+
}
65+
66+
fn classmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
67+
trace!("classmethod.__new__ {:?}", args.args);
68+
arg_check!(vm, args, required = [(cls, None), (callable, None)]);
69+
70+
let py_obj = PyObject::new(
71+
PyObjectKind::Instance {
72+
dict: vm.ctx.new_dict(),
73+
},
74+
cls.clone(),
75+
);
76+
py_obj.set_attr("function", callable.clone());
77+
Ok(py_obj)
78+
}
79+
80+
// `staticmethod` methods.
81+
fn staticmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
82+
trace!("staticmethod.__get__ {:?}", args.args);
83+
arg_check!(
84+
vm,
85+
args,
86+
required = [
87+
(cls, Some(vm.ctx.staticmethod_type())),
88+
(_inst, None),
89+
(_owner, None)
90+
]
91+
);
92+
match cls.get_attr("function") {
93+
Some(function) => Ok(function),
94+
None => {
95+
let attribute_error = vm.context().exceptions.attribute_error.clone();
96+
Err(vm.new_exception(
97+
attribute_error,
98+
String::from("Attribute Error: staticmethod must have 'function' attribute"),
99+
))
100+
}
101+
}
102+
}
103+
104+
fn staticmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
105+
trace!("staticmethod.__new__ {:?}", args.args);
106+
arg_check!(vm, args, required = [(cls, None), (callable, None)]);
107+
108+
let py_obj = PyObject::new(
109+
PyObjectKind::Instance {
110+
dict: vm.ctx.new_dict(),
111+
},
112+
cls.clone(),
113+
);
114+
py_obj.set_attr("function", callable.clone());
115+
Ok(py_obj)
116+
}

vm/src/pyobject.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ impl fmt::Display for PyObjectRef {
7272
pub struct PyContext {
7373
pub type_type: PyObjectRef,
7474
pub none: PyObjectRef,
75+
pub classmethod_type: PyObjectRef,
76+
pub staticmethod_type: PyObjectRef,
7577
pub dict_type: PyObjectRef,
7678
pub int_type: PyObjectRef,
7779
pub float_type: PyObjectRef,
@@ -140,6 +142,8 @@ impl PyContext {
140142
objdict::create_type(type_type.clone(), object_type.clone(), dict_type.clone());
141143

142144
let module_type = create_type("module", &type_type, &object_type, &dict_type);
145+
let classmethod_type = create_type("classmethod", &type_type, &object_type, &dict_type);
146+
let staticmethod_type = create_type("staticmethod", &type_type, &object_type, &dict_type);
143147
let function_type = create_type("function", &type_type, &object_type, &dict_type);
144148
let generator_type = create_type("generator", &type_type, &object_type, &dict_type);
145149
let bound_method_type = create_type("method", &type_type, &object_type, &dict_type);
@@ -177,6 +181,8 @@ impl PyContext {
177181
int_type: int_type,
178182
float_type: float_type,
179183
complex_type: complex_type,
184+
classmethod_type: classmethod_type,
185+
staticmethod_type: staticmethod_type,
180186
bytes_type: bytes_type,
181187
bytearray_type: bytearray_type,
182188
list_type: list_type,
@@ -250,22 +256,35 @@ impl PyContext {
250256
pub fn bool_type(&self) -> PyObjectRef {
251257
self.bool_type.clone()
252258
}
259+
253260
pub fn tuple_type(&self) -> PyObjectRef {
254261
self.tuple_type.clone()
255262
}
263+
256264
pub fn iter_type(&self) -> PyObjectRef {
257265
self.iter_type.clone()
258266
}
267+
259268
pub fn dict_type(&self) -> PyObjectRef {
260269
self.dict_type.clone()
261270
}
271+
262272
pub fn str_type(&self) -> PyObjectRef {
263273
self.str_type.clone()
264274
}
275+
265276
pub fn function_type(&self) -> PyObjectRef {
266277
self.function_type.clone()
267278
}
268279

280+
pub fn classmethod_type(&self) -> PyObjectRef {
281+
self.classmethod_type.clone()
282+
}
283+
284+
pub fn staticmethod_type(&self) -> PyObjectRef {
285+
self.staticmethod_type.clone()
286+
}
287+
269288
pub fn generator_type(&self) -> PyObjectRef {
270289
self.generator_type.clone()
271290
}

0 commit comments

Comments
 (0)