diff --git a/tests/snippets/types_snippet.py b/tests/snippets/types_snippet.py index 3fc27b71c0..ac715751ef 100644 --- a/tests/snippets/types_snippet.py +++ b/tests/snippets/types_snippet.py @@ -15,5 +15,14 @@ assert type(cls) is metaclass assert type(metaclass) is type +assert issubclass(metaclass, type) +assert isinstance(cls, type) + +assert inst.__class__ is cls +assert cls.__class__ is metaclass +assert metaclass.__class__ is type +assert type.__class__ is type +assert None.__class__ is type(None) + assert isinstance(type, type) assert issubclass(type, type) diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index a2092c308f..26fee403fa 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -27,6 +27,18 @@ pub fn init(context: &PyContext) { context.new_rustfunc(member_get), ); + let data_descriptor_type = &context.data_descriptor_type; + context.set_attr( + &data_descriptor_type, + "__get__", + context.new_rustfunc(data_get), + ); + context.set_attr( + &data_descriptor_type, + "__set__", + context.new_rustfunc(data_set), + ); + let classmethod_type = &context.classmethod_type; context.set_attr( &classmethod_type, @@ -83,6 +95,26 @@ fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { } } +fn data_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { + match args.shift().get_attr("fget") { + Some(function) => vm.invoke(function, args), + None => { + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) + } + } +} + +fn data_set(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult { + match args.shift().get_attr("fset") { + Some(function) => vm.invoke(function, args), + None => { + let attribute_error = vm.context().exceptions.attribute_error.clone(); + Err(vm.new_exception(attribute_error, String::from("Attribute Error"))) + } + } +} + // Classmethod type methods: fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("classmethod.__get__ {:?}", args.args); diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index a387028cbb..bf659b2c72 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -161,6 +161,11 @@ pub fn init(context: &PyContext) { context.set_attr(&object, "__new__", context.new_rustfunc(new_instance)); context.set_attr(&object, "__init__", context.new_rustfunc(object_init)); + context.set_attr( + &object, + "__class__", + context.new_data_descriptor(object_class, object_class_setter), + ); context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq)); context.set_attr(&object, "__ne__", context.new_rustfunc(object_ne)); context.set_attr(&object, "__lt__", context.new_rustfunc(object_lt)); @@ -190,6 +195,20 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { Ok(vm.ctx.none()) } +// TODO Use PyClassRef for owner to enforce type +fn object_class(_obj: PyObjectRef, owner: PyObjectRef, _vm: &mut VirtualMachine) -> PyObjectRef { + owner +} + +fn object_class_setter( + instance: PyObjectRef, + _value: PyObjectRef, + vm: &mut VirtualMachine, +) -> PyResult { + let type_repr = vm.to_pystr(&instance.typ())?; + Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) +} + fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { match args.args[0].payload { PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => { diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index b30c77226d..dfe11510bc 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -39,11 +39,6 @@ pub fn init(context: &PyContext) { "__mro__", context.new_member_descriptor(type_mro), ); - context.set_attr( - &type_type, - "__class__", - context.new_member_descriptor(type_new), - ); context.set_attr(&type_type, "__repr__", context.new_rustfunc(type_repr)); context.set_attr( &type_type, diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 6a618cd7b6..accab331a1 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -150,6 +150,7 @@ pub struct PyContext { pub module_type: PyObjectRef, pub bound_method_type: PyObjectRef, pub member_descriptor_type: PyObjectRef, + pub data_descriptor_type: PyObjectRef, pub object: PyObjectRef, pub exceptions: exceptions::ExceptionZoo, } @@ -199,6 +200,8 @@ impl PyContext { let bound_method_type = create_type("method", &type_type, &object_type, &dict_type); let member_descriptor_type = create_type("member_descriptor", &type_type, &object_type, &dict_type); + let data_descriptor_type = + create_type("data_descriptor", &type_type, &object_type, &dict_type); let str_type = create_type("str", &type_type, &object_type, &dict_type); let list_type = create_type("list", &type_type, &object_type, &dict_type); let set_type = create_type("set", &type_type, &object_type, &dict_type); @@ -285,6 +288,7 @@ impl PyContext { module_type, bound_method_type, member_descriptor_type, + data_descriptor_type, type_type, exceptions, }; @@ -449,6 +453,10 @@ impl PyContext { pub fn member_descriptor_type(&self) -> PyObjectRef { self.member_descriptor_type.clone() } + pub fn data_descriptor_type(&self) -> PyObjectRef { + self.data_descriptor_type.clone() + } + pub fn type_type(&self) -> PyObjectRef { self.type_type.clone() } @@ -658,6 +666,22 @@ impl PyContext { self.new_instance(self.member_descriptor_type(), Some(dict)) } + pub fn new_data_descriptor< + G: IntoPyNativeFunc<(I, PyObjectRef), T>, + S: IntoPyNativeFunc<(I, T), PyResult>, + T, + I, + >( + &self, + getter: G, + setter: S, + ) -> PyObjectRef { + let mut dict = PyAttributes::new(); + dict.insert("fget".to_string(), self.new_rustfunc(getter)); + dict.insert("fset".to_string(), self.new_rustfunc(setter)); + self.new_instance(self.data_descriptor_type(), Some(dict)) + } + pub fn new_instance(&self, class: PyObjectRef, dict: Option) -> PyObjectRef { let dict = if let Some(dict) = dict { dict