Merge pull request #1447 from RustPython/coolreader18/type-attrs

Fix properties on `type`
This commit is contained in:
Windel Bouwman
2019-10-03 20:10:25 +02:00
committed by GitHub
5 changed files with 43 additions and 24 deletions

View File

@@ -43,6 +43,11 @@ assert B.__subclasses__() == [D]
assert C.__subclasses__() == [D]
assert D.__subclasses__() == []
assert D.__bases__ == (B, C)
assert A.__bases__ == (object,)
assert B.__bases__ == (A,)
del D
try: # gc sweep is needed here for CPython...

View File

@@ -1,4 +1,4 @@
use crate::function::{Args, KwArgs};
use crate::function::PyFuncArgs;
use crate::obj::objcode::PyCodeRef;
use crate::obj::objdict::PyDictRef;
use crate::obj::objtuple::PyTupleRef;
@@ -44,8 +44,8 @@ impl PyValue for PyFunction {
}
impl PyFunctionRef {
fn call(self, args: Args, kwargs: KwArgs, vm: &VirtualMachine) -> PyResult {
vm.invoke(&self.into_object(), (&args, &kwargs))
fn call(func: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
vm.invoke(&func, args)
}
fn code(self, _vm: &VirtualMachine) -> PyCodeRef {
@@ -92,7 +92,8 @@ pub fn init(context: &PyContext) {
let builtin_function_or_method_type = &context.types.builtin_function_or_method_type;
extend_class!(context, builtin_function_or_method_type, {
"__get__" => context.new_rustfunc(bind_method)
"__get__" => context.new_rustfunc(bind_method),
"__call__" => context.new_rustfunc(PyFunctionRef::call),
});
}

View File

@@ -28,14 +28,13 @@ pub type PyReadOnlyPropertyRef = PyRef<PyReadOnlyProperty>;
#[pyimpl]
impl PyReadOnlyProperty {
#[pymethod(name = "__get__")]
fn get(
zelf: PyRef<Self>,
obj: PyObjectRef,
_owner: OptionalArg<PyClassRef>,
vm: &VirtualMachine,
) -> PyResult {
if obj.is(vm.ctx.none.as_object()) {
Ok(zelf.into_object())
fn get(zelf: PyRef<Self>, obj: PyObjectRef, cls: PyClassRef, vm: &VirtualMachine) -> PyResult {
if vm.is_none(&obj) {
if cls.is(&vm.ctx.types.type_type) {
vm.invoke(&zelf.getter, cls.into_object())
} else {
Ok(zelf.into_object())
}
} else {
vm.invoke(&zelf.getter, obj)
}

View File

@@ -20,6 +20,7 @@ use super::objweakref::PyWeak;
#[derive(Debug)]
pub struct PyClass {
pub name: String,
pub bases: Vec<PyClassRef>,
pub mro: Vec<PyClassRef>,
pub subclasses: RefCell<Vec<PyWeak>>,
pub attributes: RefCell<PyAttributes>,
@@ -94,6 +95,11 @@ impl PyClassRef {
Err(vm.new_attribute_error("read-only attribute".to_string()))
}
fn bases(self, vm: &VirtualMachine) -> PyObjectRef {
vm.ctx
.new_tuple(self.bases.iter().map(|x| x.as_object().clone()).collect())
}
fn dir(self, vm: &VirtualMachine) -> PyList {
let attributes = get_attributes(self);
let attributes: Vec<PyObjectRef> = attributes
@@ -241,6 +247,7 @@ pub fn init(ctx: &PyContext) {
.add_getter(PyClassRef::mro)
.add_setter(PyClassRef::set_mro)
.create(),
"__bases__" => ctx.new_property(PyClassRef::bases),
"__name__" => ctx.new_property(PyClassRef::name),
"__repr__" => ctx.new_rustfunc(PyClassRef::repr),
"__qualname__" => ctx.new_property(PyClassRef::qualname),
@@ -290,8 +297,12 @@ fn type_new_slot(metatype: PyClassRef, args: PyFuncArgs, vm: &VirtualMachine) ->
let (name, bases, dict): (PyStringRef, PyIterable<PyClassRef>, PyDictRef) = args.bind(vm)?;
let mut bases: Vec<PyClassRef> = bases.iter(vm)?.collect::<Result<Vec<_>, _>>()?;
bases.push(vm.ctx.object());
let bases: Vec<PyClassRef> = bases.iter(vm)?.collect::<Result<Vec<_>, _>>()?;
let bases = if bases.is_empty() {
vec![vm.ctx.object()]
} else {
bases
};
let attributes = dict.to_attributes();
@@ -446,13 +457,10 @@ fn linearise_mro(mut bases: Vec<Vec<PyClassRef>>) -> Option<Vec<PyClassRef>> {
if (&bases).iter().all(Vec::is_empty) {
break;
}
match take_next_base(bases) {
Some((head, new_bases)) => {
result.push(head);
bases = new_bases;
}
None => return None,
}
let (head, new_bases) = take_next_base(bases)?;
result.push(head);
bases = new_bases;
}
Some(result)
}
@@ -468,6 +476,7 @@ pub fn new(
let new_type = PyObject {
payload: PyClass {
name: String::from(name),
bases,
mro,
subclasses: RefCell::default(),
attributes: RefCell::new(dict),
@@ -477,13 +486,16 @@ pub fn new(
typ,
}
.into_ref();
for base in bases {
let new_type: PyClassRef = new_type.downcast().unwrap();
for base in &new_type.bases {
base.subclasses
.borrow_mut()
.push(PyWeak::downgrade(&new_type));
.push(PyWeak::downgrade(new_type.as_object()));
}
Ok(new_type.downcast().unwrap())
Ok(new_type)
}
#[cfg(test)]

View File

@@ -232,6 +232,7 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) {
dict: None,
payload: PyClass {
name: String::from("object"),
bases: vec![],
mro: vec![],
subclasses: RefCell::default(),
attributes: RefCell::new(PyAttributes::new()),
@@ -245,6 +246,7 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) {
dict: None,
payload: PyClass {
name: String::from("type"),
bases: vec![object_type.clone().downcast().unwrap()],
mro: vec![object_type.clone().downcast().unwrap()],
subclasses: RefCell::default(),
attributes: RefCell::new(PyAttributes::new()),