forked from Rust-related/RustPython
Merge pull request #1447 from RustPython/coolreader18/type-attrs
Fix properties on `type`
This commit is contained in:
@@ -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...
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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()),
|
||||
|
||||
Reference in New Issue
Block a user