mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-17 01:51:39 +09:00
@@ -337,17 +337,16 @@ impl Class {
|
||||
};
|
||||
if name == "pymethod" {
|
||||
self.add_item(Self::extract_method(sig, meta)?, meta_span)?;
|
||||
attr_idxs.push(i);
|
||||
} else if name == "pyclassmethod" {
|
||||
self.add_item(Self::extract_classmethod(sig, meta)?, meta_span)?;
|
||||
attr_idxs.push(i);
|
||||
} else if name == "pyproperty" {
|
||||
self.add_item(Self::extract_property(sig, meta)?, meta_span)?;
|
||||
attr_idxs.push(i);
|
||||
} else if name == "pyslot" {
|
||||
self.add_item(Self::extract_slot(sig, meta)?, meta_span)?;
|
||||
attr_idxs.push(i);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
attr_idxs.push(i);
|
||||
}
|
||||
let mut i = 0;
|
||||
let mut attr_idxs = &*attr_idxs;
|
||||
@@ -406,8 +405,8 @@ fn extract_impl_items(mut items: Vec<ItemSig>) -> Result<TokenStream2, Diagnosti
|
||||
let properties = properties
|
||||
.into_iter()
|
||||
.map(|(name, prop)| {
|
||||
let getter = match prop.0 {
|
||||
Some(getter) => getter,
|
||||
let getter_func = match prop.0 {
|
||||
Some(func) => func,
|
||||
None => {
|
||||
push_err_span!(
|
||||
diagnostics,
|
||||
@@ -418,14 +417,17 @@ fn extract_impl_items(mut items: Vec<ItemSig>) -> Result<TokenStream2, Diagnosti
|
||||
return TokenStream2::new();
|
||||
}
|
||||
};
|
||||
let add_setter = prop.1.map(|setter| quote!(.add_setter(Self::#setter)));
|
||||
let (new, setter) = match prop.1 {
|
||||
Some(func) => (quote! { with_get_set }, quote! { , &Self::#func }),
|
||||
None => (quote! { with_get }, quote! { }),
|
||||
};
|
||||
let str_name = name.to_string();
|
||||
quote! {
|
||||
class.set_str_attr(
|
||||
#name,
|
||||
::rustpython_vm::obj::objproperty::PropertyBuilder::new(ctx)
|
||||
.add_getter(Self::#getter)
|
||||
#add_setter
|
||||
.create(),
|
||||
::rustpython_vm::pyobject::PyObject::new(
|
||||
::rustpython_vm::obj::objgetset::PyGetSet::#new(#str_name.into(), &Self::#getter_func #setter),
|
||||
ctx.getset_type(), None)
|
||||
);
|
||||
}
|
||||
})
|
||||
@@ -453,8 +455,13 @@ fn extract_impl_items(mut items: Vec<ItemSig>) -> Result<TokenStream2, Diagnosti
|
||||
slot_ident,
|
||||
item_ident,
|
||||
} => {
|
||||
let transform = if vec!["new", "call"].contains(&slot_ident.to_string().as_str()) {
|
||||
quote! { ::rustpython_vm::function::IntoPyNativeFunc::into_func }
|
||||
} else {
|
||||
quote! { Box::new }
|
||||
};
|
||||
let into_func = quote_spanned! {item_ident.span()=>
|
||||
::rustpython_vm::function::IntoPyNativeFunc::into_func(Self::#item_ident)
|
||||
#transform(Self::#item_ident)
|
||||
};
|
||||
Some(quote! {
|
||||
(*class.slots.borrow_mut()).#slot_ident = Some(#into_func);
|
||||
@@ -685,7 +692,8 @@ pub fn impl_pystruct_sequence(attr: AttributeArgs, item: Item) -> Result<TokenSt
|
||||
let property = quote! {
|
||||
class.set_str_attr(
|
||||
#field_name_str,
|
||||
ctx.new_property(
|
||||
ctx.new_readonly_getset(
|
||||
#field_name_str,
|
||||
|zelf: &::rustpython_vm::obj::objtuple::PyTuple,
|
||||
_vm: &::rustpython_vm::VirtualMachine| {
|
||||
zelf.fast_getitem(#idx)
|
||||
|
||||
@@ -86,3 +86,7 @@ class C(B, BB):
|
||||
pass
|
||||
|
||||
assert C.mro() == [C, B, A, BB, AA, object]
|
||||
|
||||
|
||||
assert type(Exception.args).__name__ == 'getset_descriptor'
|
||||
assert type(None).__bool__(None) is False
|
||||
|
||||
@@ -80,7 +80,7 @@ impl PyBaseException {
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__traceback__")]
|
||||
fn get_traceback(&self, _vm: &VirtualMachine) -> Option<PyTracebackRef> {
|
||||
fn get_traceback(&self) -> Option<PyTracebackRef> {
|
||||
self.traceback.borrow().clone()
|
||||
}
|
||||
|
||||
@@ -95,8 +95,13 @@ impl PyBaseException {
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__cause__", setter)]
|
||||
fn setter_cause(&self, cause: Option<PyBaseExceptionRef>, _vm: &VirtualMachine) {
|
||||
fn setter_cause(
|
||||
&self,
|
||||
cause: Option<PyBaseExceptionRef>,
|
||||
_vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
self.cause.replace(cause);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__context__")]
|
||||
@@ -105,8 +110,13 @@ impl PyBaseException {
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__context__", setter)]
|
||||
fn setter_context(&self, context: Option<PyBaseExceptionRef>, _vm: &VirtualMachine) {
|
||||
fn setter_context(
|
||||
&self,
|
||||
context: Option<PyBaseExceptionRef>,
|
||||
_vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
self.context.replace(context);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__suppress_context__")]
|
||||
@@ -634,20 +644,16 @@ pub fn init(ctx: &PyContext) {
|
||||
PyBaseException::extend_class(ctx, &excs.base_exception_type);
|
||||
|
||||
extend_class!(ctx, &excs.syntax_error, {
|
||||
"msg" => ctx.new_property(make_arg_getter(0)),
|
||||
"filename" => ctx.new_property(make_arg_getter(1)),
|
||||
"lineno" => ctx.new_property(make_arg_getter(2)),
|
||||
"offset" => ctx.new_property(make_arg_getter(3)),
|
||||
"text" => ctx.new_property(make_arg_getter(4)),
|
||||
"msg" => ctx.new_readonly_getset("msg", make_arg_getter(0)),
|
||||
});
|
||||
|
||||
extend_class!(ctx, &excs.import_error, {
|
||||
"__init__" => ctx.new_method(import_error_init),
|
||||
"msg" => ctx.new_property(make_arg_getter(0)),
|
||||
"msg" => ctx.new_readonly_getset("msg", make_arg_getter(0)),
|
||||
});
|
||||
|
||||
extend_class!(ctx, &excs.stop_iteration, {
|
||||
"value" => ctx.new_property(make_arg_getter(0)),
|
||||
"value" => ctx.new_readonly_getset("value", make_arg_getter(0)),
|
||||
});
|
||||
|
||||
extend_class!(ctx, &excs.key_error, {
|
||||
@@ -655,27 +661,27 @@ pub fn init(ctx: &PyContext) {
|
||||
});
|
||||
|
||||
extend_class!(ctx, &excs.unicode_decode_error, {
|
||||
"encoding" => ctx.new_property(make_arg_getter(0)),
|
||||
"object" => ctx.new_property(make_arg_getter(1)),
|
||||
"start" => ctx.new_property(make_arg_getter(2)),
|
||||
"end" => ctx.new_property(make_arg_getter(3)),
|
||||
"reason" => ctx.new_property(make_arg_getter(4)),
|
||||
"encoding" => ctx.new_readonly_getset("encoding", make_arg_getter(0)),
|
||||
"object" => ctx.new_readonly_getset("object", make_arg_getter(1)),
|
||||
"start" => ctx.new_readonly_getset("start", make_arg_getter(2)),
|
||||
"end" => ctx.new_readonly_getset("end", make_arg_getter(3)),
|
||||
"reason" => ctx.new_readonly_getset("reason", make_arg_getter(4)),
|
||||
});
|
||||
|
||||
extend_class!(ctx, &excs.unicode_encode_error, {
|
||||
"encoding" => ctx.new_property(make_arg_getter(0)),
|
||||
"object" => ctx.new_property(make_arg_getter(1)),
|
||||
"start" => ctx.new_property(make_arg_getter(2)),
|
||||
"end" => ctx.new_property(make_arg_getter(3)),
|
||||
"reason" => ctx.new_property(make_arg_getter(4)),
|
||||
"encoding" => ctx.new_readonly_getset("encoding", make_arg_getter(0)),
|
||||
"object" => ctx.new_readonly_getset("object", make_arg_getter(1)),
|
||||
"start" => ctx.new_readonly_getset("start", make_arg_getter(2)),
|
||||
"end" => ctx.new_readonly_getset("end", make_arg_getter(3)),
|
||||
"reason" => ctx.new_readonly_getset("reason", make_arg_getter(4)),
|
||||
});
|
||||
|
||||
extend_class!(ctx, &excs.unicode_translate_error, {
|
||||
"encoding" => ctx.new_property(none_getter),
|
||||
"object" => ctx.new_property(make_arg_getter(0)),
|
||||
"start" => ctx.new_property(make_arg_getter(1)),
|
||||
"end" => ctx.new_property(make_arg_getter(2)),
|
||||
"reason" => ctx.new_property(make_arg_getter(3)),
|
||||
"encoding" => ctx.new_readonly_getset("encoding", none_getter),
|
||||
"object" => ctx.new_readonly_getset("object", make_arg_getter(0)),
|
||||
"start" => ctx.new_readonly_getset("start", make_arg_getter(1)),
|
||||
"end" => ctx.new_readonly_getset("end", make_arg_getter(2)),
|
||||
"reason" => ctx.new_readonly_getset("reason", make_arg_getter(3)),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ pub mod objfloat;
|
||||
pub mod objframe;
|
||||
pub mod objfunction;
|
||||
pub mod objgenerator;
|
||||
pub mod objgetset;
|
||||
pub mod objint;
|
||||
pub mod objiter;
|
||||
pub mod objlist;
|
||||
|
||||
@@ -3,9 +3,9 @@ use std::fmt;
|
||||
use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc};
|
||||
use crate::obj::objtype::PyClassRef;
|
||||
use crate::pyobject::{
|
||||
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
|
||||
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyResult, PyValue, TypeProtocol,
|
||||
};
|
||||
use crate::slots::{PyBuiltinCallable, PyBuiltinDescriptor};
|
||||
use crate::slots::{SlotCall, SlotDescriptor};
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
#[pyclass]
|
||||
@@ -35,13 +35,13 @@ impl PyBuiltinFunction {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyBuiltinCallable for PyBuiltinFunction {
|
||||
impl SlotCall for PyBuiltinFunction {
|
||||
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
(self.value)(vm, args)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinCallable))]
|
||||
#[pyimpl(with(SlotCall))]
|
||||
impl PyBuiltinFunction {}
|
||||
|
||||
#[pyclass]
|
||||
@@ -73,13 +73,17 @@ impl PyBuiltinMethod {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyBuiltinDescriptor for PyBuiltinMethod {
|
||||
fn get(
|
||||
zelf: PyRef<Self>,
|
||||
obj: PyObjectRef,
|
||||
cls: OptionalArg<PyObjectRef>,
|
||||
impl SlotDescriptor for PyBuiltinMethod {
|
||||
fn descr_get(
|
||||
vm: &VirtualMachine,
|
||||
zelf: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
cls: OptionalArg<PyObjectRef>,
|
||||
) -> PyResult {
|
||||
let (zelf, obj) = match Self::_check(zelf, obj, vm) {
|
||||
Ok(obj) => obj,
|
||||
Err(result) => return result,
|
||||
};
|
||||
if obj.is(&vm.get_none()) && !Self::_cls_is(&cls, &obj.class()) {
|
||||
Ok(zelf.into_object())
|
||||
} else {
|
||||
@@ -88,13 +92,13 @@ impl PyBuiltinDescriptor for PyBuiltinMethod {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyBuiltinCallable for PyBuiltinMethod {
|
||||
impl SlotCall for PyBuiltinMethod {
|
||||
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
(self.function.value)(vm, args)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinDescriptor, PyBuiltinCallable))]
|
||||
#[pyimpl(with(SlotDescriptor, SlotCall))]
|
||||
impl PyBuiltinMethod {}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::function::OptionalArg;
|
||||
use crate::pyobject::{
|
||||
PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
|
||||
};
|
||||
use crate::slots::PyBuiltinDescriptor;
|
||||
use crate::slots::SlotDescriptor;
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
/// classmethod(function) -> method
|
||||
@@ -47,19 +47,20 @@ impl PyValue for PyClassMethod {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyBuiltinDescriptor for PyClassMethod {
|
||||
fn get(
|
||||
zelf: PyRef<Self>,
|
||||
obj: PyObjectRef,
|
||||
cls: OptionalArg<PyObjectRef>,
|
||||
impl SlotDescriptor for PyClassMethod {
|
||||
fn descr_get(
|
||||
vm: &VirtualMachine,
|
||||
zelf: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
cls: OptionalArg<PyObjectRef>,
|
||||
) -> PyResult {
|
||||
let (zelf, obj) = Self::_unwrap(zelf, obj, vm)?;
|
||||
let cls = cls.unwrap_or_else(|| obj.class().into_object());
|
||||
Ok(vm.ctx.new_bound_method(zelf.callable.clone(), cls))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinDescriptor), flags(BASETYPE))]
|
||||
#[pyimpl(with(SlotDescriptor), flags(BASETYPE))]
|
||||
impl PyClassMethod {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
|
||||
@@ -92,17 +92,17 @@ impl PyCodeRef {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
extend_class!(context, &context.types.code_type, {
|
||||
pub fn init(ctx: &PyContext) {
|
||||
extend_class!(ctx, &ctx.types.code_type, {
|
||||
(slot new) => PyCodeRef::new,
|
||||
"__repr__" => context.new_method(PyCodeRef::repr),
|
||||
"__repr__" => ctx.new_method(PyCodeRef::repr),
|
||||
|
||||
"co_argcount" => context.new_property(PyCodeRef::co_argcount),
|
||||
"co_consts" => context.new_property(PyCodeRef::co_consts),
|
||||
"co_filename" => context.new_property(PyCodeRef::co_filename),
|
||||
"co_firstlineno" => context.new_property(PyCodeRef::co_firstlineno),
|
||||
"co_kwonlyargcount" => context.new_property(PyCodeRef::co_kwonlyargcount),
|
||||
"co_name" => context.new_property(PyCodeRef::co_name),
|
||||
"co_flags" => context.new_property(PyCodeRef::co_flags),
|
||||
"co_argcount" => ctx.new_readonly_getset("co_argcount", PyCodeRef::co_argcount),
|
||||
"co_consts" => ctx.new_readonly_getset("co_consts", PyCodeRef::co_consts),
|
||||
"co_filename" => ctx.new_readonly_getset("co_filename", PyCodeRef::co_filename),
|
||||
"co_firstlineno" => ctx.new_readonly_getset("co_firstlineno", PyCodeRef::co_firstlineno),
|
||||
"co_kwonlyargcount" => ctx.new_readonly_getset("co_kwonlyargcount", PyCodeRef::co_kwonlyargcount),
|
||||
"co_name" => ctx.new_readonly_getset("co_name", PyCodeRef::co_name),
|
||||
"co_flags" => ctx.new_readonly_getset("co_flags", PyCodeRef::co_flags),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -498,12 +498,12 @@ impl PyFloat {
|
||||
pyhash::hash_float(self.value)
|
||||
}
|
||||
|
||||
#[pyproperty(name = "real")]
|
||||
#[pyproperty]
|
||||
fn real(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyFloatRef {
|
||||
zelf
|
||||
}
|
||||
|
||||
#[pyproperty(name = "imag")]
|
||||
#[pyproperty]
|
||||
fn imag(&self, _vm: &VirtualMachine) -> f64 {
|
||||
0.0f64
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::pyobject::{
|
||||
TypeProtocol,
|
||||
};
|
||||
use crate::scope::Scope;
|
||||
use crate::slots::{PyBuiltinCallable, PyBuiltinDescriptor};
|
||||
use crate::slots::{SlotCall, SlotDescriptor};
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
pub type PyFunctionRef = PyRef<PyFunction>;
|
||||
@@ -27,13 +27,14 @@ pub struct PyFunction {
|
||||
kw_only_defaults: Option<PyDictRef>,
|
||||
}
|
||||
|
||||
impl PyBuiltinDescriptor for PyFunction {
|
||||
fn get(
|
||||
zelf: PyRef<Self>,
|
||||
obj: PyObjectRef,
|
||||
cls: OptionalArg<PyObjectRef>,
|
||||
impl SlotDescriptor for PyFunction {
|
||||
fn descr_get(
|
||||
vm: &VirtualMachine,
|
||||
zelf: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
cls: OptionalArg<PyObjectRef>,
|
||||
) -> PyResult {
|
||||
let (zelf, obj) = Self::_unwrap(zelf, obj, vm)?;
|
||||
if obj.is(&vm.get_none()) && !Self::_cls_is(&cls, &obj.class()) {
|
||||
Ok(zelf.into_object())
|
||||
} else {
|
||||
@@ -240,7 +241,7 @@ impl PyValue for PyFunction {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
#[pyimpl(with(SlotDescriptor))]
|
||||
impl PyFunction {
|
||||
#[pyslot]
|
||||
#[pymethod(magic)]
|
||||
@@ -248,17 +249,17 @@ impl PyFunction {
|
||||
self.invoke(args, vm)
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__code__")]
|
||||
#[pyproperty(magic)]
|
||||
fn code(&self, _vm: &VirtualMachine) -> PyCodeRef {
|
||||
self.code.clone()
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__defaults__")]
|
||||
#[pyproperty(magic)]
|
||||
fn defaults(&self, _vm: &VirtualMachine) -> Option<PyTupleRef> {
|
||||
self.defaults.clone()
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__kwdefaults__")]
|
||||
#[pyproperty(magic)]
|
||||
fn kwdefaults(&self, _vm: &VirtualMachine) -> Option<PyDictRef> {
|
||||
self.kw_only_defaults.clone()
|
||||
}
|
||||
@@ -272,7 +273,7 @@ pub struct PyBoundMethod {
|
||||
pub function: PyObjectRef,
|
||||
}
|
||||
|
||||
impl PyBuiltinCallable for PyBoundMethod {
|
||||
impl SlotCall for PyBoundMethod {
|
||||
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let args = args.insert(self.object.clone());
|
||||
vm.invoke(&self.function, args)
|
||||
@@ -285,7 +286,7 @@ impl PyBoundMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinCallable))]
|
||||
#[pyimpl(with(SlotCall))]
|
||||
impl PyBoundMethod {
|
||||
#[pymethod(magic)]
|
||||
fn getattribute(&self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
|
||||
249
vm/src/obj/objgetset.rs
Normal file
249
vm/src/obj/objgetset.rs
Normal file
@@ -0,0 +1,249 @@
|
||||
/*! Python `attribute` descriptor class. (PyGetSet)
|
||||
|
||||
*/
|
||||
use super::objtype::PyClassRef;
|
||||
use crate::function::{OptionalArg, OwnedParam, RefParam};
|
||||
use crate::pyobject::{
|
||||
IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
|
||||
};
|
||||
use crate::slots::SlotDescriptor;
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
pub type PyGetterFunc = Box<dyn Fn(&VirtualMachine, PyObjectRef) -> PyResult>;
|
||||
pub type PySetterFunc = Box<dyn Fn(&VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult<()>>;
|
||||
|
||||
pub trait IntoPyGetterFunc<T> {
|
||||
fn into_getter(self) -> PyGetterFunc;
|
||||
}
|
||||
|
||||
impl<F, T, R> IntoPyGetterFunc<(OwnedParam<T>, R, VirtualMachine)> for F
|
||||
where
|
||||
F: Fn(T, &VirtualMachine) -> R + 'static,
|
||||
T: TryFromObject,
|
||||
R: IntoPyObject,
|
||||
{
|
||||
fn into_getter(self) -> PyGetterFunc {
|
||||
Box::new(move |vm, obj| {
|
||||
let obj = T::try_from_object(vm, obj)?;
|
||||
(self)(obj, vm).into_pyobject(vm)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, S, R> IntoPyGetterFunc<(RefParam<S>, R, VirtualMachine)> for F
|
||||
where
|
||||
F: Fn(&S, &VirtualMachine) -> R + 'static,
|
||||
S: PyValue,
|
||||
R: IntoPyObject,
|
||||
{
|
||||
fn into_getter(self) -> PyGetterFunc {
|
||||
Box::new(move |vm, obj| {
|
||||
let zelf = PyRef::<S>::try_from_object(vm, obj)?;
|
||||
(self)(&zelf, vm).into_pyobject(vm)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T, R> IntoPyGetterFunc<(OwnedParam<T>, R)> for F
|
||||
where
|
||||
F: Fn(T) -> R + 'static,
|
||||
T: TryFromObject,
|
||||
R: IntoPyObject,
|
||||
{
|
||||
fn into_getter(self) -> PyGetterFunc {
|
||||
IntoPyGetterFunc::into_getter(move |obj, _vm: &VirtualMachine| (self)(obj))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, S, R> IntoPyGetterFunc<(RefParam<S>, R)> for F
|
||||
where
|
||||
F: Fn(&S) -> R + 'static,
|
||||
S: PyValue,
|
||||
R: IntoPyObject,
|
||||
{
|
||||
fn into_getter(self) -> PyGetterFunc {
|
||||
IntoPyGetterFunc::into_getter(move |zelf: &S, _vm: &VirtualMachine| (self)(zelf))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoPyNoResult {
|
||||
fn into_noresult(self) -> PyResult<()>;
|
||||
}
|
||||
|
||||
impl IntoPyNoResult for () {
|
||||
fn into_noresult(self) -> PyResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPyNoResult for PyResult<()> {
|
||||
fn into_noresult(self) -> PyResult<()> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoPySetterFunc<T> {
|
||||
fn into_setter(self) -> PySetterFunc;
|
||||
}
|
||||
|
||||
impl<F, T, V, R> IntoPySetterFunc<(OwnedParam<T>, V, R, VirtualMachine)> for F
|
||||
where
|
||||
F: Fn(T, V, &VirtualMachine) -> R + 'static,
|
||||
T: TryFromObject,
|
||||
V: TryFromObject,
|
||||
R: IntoPyNoResult,
|
||||
{
|
||||
fn into_setter(self) -> PySetterFunc {
|
||||
Box::new(move |vm, obj, value| {
|
||||
let obj = T::try_from_object(vm, obj)?;
|
||||
let value = V::try_from_object(vm, value)?;
|
||||
(self)(obj, value, vm).into_noresult()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, S, V, R> IntoPySetterFunc<(RefParam<S>, V, R, VirtualMachine)> for F
|
||||
where
|
||||
F: Fn(&S, V, &VirtualMachine) -> R + 'static,
|
||||
S: PyValue,
|
||||
V: TryFromObject,
|
||||
R: IntoPyNoResult,
|
||||
{
|
||||
fn into_setter(self) -> PySetterFunc {
|
||||
Box::new(move |vm, obj, value| {
|
||||
let zelf = PyRef::<S>::try_from_object(vm, obj)?;
|
||||
let value = V::try_from_object(vm, value)?;
|
||||
(self)(&zelf, value, vm).into_noresult()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T, V, R> IntoPySetterFunc<(OwnedParam<T>, V, R)> for F
|
||||
where
|
||||
F: Fn(T, V) -> R + 'static,
|
||||
T: TryFromObject,
|
||||
V: TryFromObject,
|
||||
R: IntoPyNoResult,
|
||||
{
|
||||
fn into_setter(self) -> PySetterFunc {
|
||||
IntoPySetterFunc::into_setter(move |obj, v, _vm: &VirtualMachine| (self)(obj, v))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, S, V, R> IntoPySetterFunc<(RefParam<S>, V, R)> for F
|
||||
where
|
||||
F: Fn(&S, V) -> R + 'static,
|
||||
S: PyValue,
|
||||
V: TryFromObject,
|
||||
R: IntoPyNoResult,
|
||||
{
|
||||
fn into_setter(self) -> PySetterFunc {
|
||||
IntoPySetterFunc::into_setter(move |zelf: &S, v, _vm: &VirtualMachine| (self)(zelf, v))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
pub struct PyGetSet {
|
||||
name: String,
|
||||
getter: Option<PyGetterFunc>,
|
||||
setter: Option<PySetterFunc>,
|
||||
// doc: Option<String>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PyGetSet {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"PyGetSet {{ name: {}, getter: {}, setter: {} }}",
|
||||
self.name,
|
||||
if self.getter.is_some() {
|
||||
"Some"
|
||||
} else {
|
||||
"None"
|
||||
},
|
||||
if self.setter.is_some() {
|
||||
"Some"
|
||||
} else {
|
||||
"None"
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyValue for PyGetSet {
|
||||
fn class(vm: &VirtualMachine) -> PyClassRef {
|
||||
vm.ctx.getset_type()
|
||||
}
|
||||
}
|
||||
|
||||
pub type PyGetSetRef = PyRef<PyGetSet>;
|
||||
|
||||
impl SlotDescriptor for PyGetSet {
|
||||
fn descr_get(
|
||||
vm: &VirtualMachine,
|
||||
zelf: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
_cls: OptionalArg<PyObjectRef>,
|
||||
) -> PyResult {
|
||||
let (zelf, obj) = match Self::_check(zelf, obj, vm) {
|
||||
Ok(obj) => obj,
|
||||
Err(result) => return result,
|
||||
};
|
||||
if let Some(ref f) = zelf.getter {
|
||||
f(vm, obj)
|
||||
} else {
|
||||
Err(vm.new_attribute_error(format!(
|
||||
"attribute '{}' of '{}' objects is not readable",
|
||||
zelf.name,
|
||||
Self::class(vm).name
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PyGetSet {
|
||||
pub fn with_get<G, X>(name: String, getter: G) -> Self
|
||||
where
|
||||
G: IntoPyGetterFunc<X>,
|
||||
{
|
||||
Self {
|
||||
name,
|
||||
getter: Some(getter.into_getter()),
|
||||
setter: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_get_set<G, S, X, Y>(name: String, getter: G, setter: S) -> Self
|
||||
where
|
||||
G: IntoPyGetterFunc<X>,
|
||||
S: IntoPySetterFunc<Y>,
|
||||
{
|
||||
Self {
|
||||
name,
|
||||
getter: Some(getter.into_getter()),
|
||||
setter: Some(setter.into_setter()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(SlotDescriptor))]
|
||||
impl PyGetSet {
|
||||
// Descriptor methods
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn set(&self, obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if let Some(ref f) = self.setter {
|
||||
f(vm, obj, value)
|
||||
} else {
|
||||
Err(vm.new_attribute_error(format!(
|
||||
"attribute '{}' of '{}' objects is not writable",
|
||||
self.name,
|
||||
Self::class(vm).name
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init(context: &PyContext) {
|
||||
PyGetSet::extend_class(context, &context.types.getset_type);
|
||||
}
|
||||
@@ -626,6 +626,7 @@ impl PyInt {
|
||||
}
|
||||
#[pyproperty]
|
||||
fn real(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
// subclasses must return int here
|
||||
vm.ctx.new_bigint(&self.value)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use super::objproperty::PyPropertyRef;
|
||||
use super::objstr::PyStringRef;
|
||||
use super::objtype::PyClassRef;
|
||||
use crate::pyobject::{
|
||||
IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
|
||||
TypeProtocol,
|
||||
IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
|
||||
};
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
@@ -52,66 +49,6 @@ impl PyNone {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[pymethod(name = "__getattribute__")]
|
||||
fn get_attribute(zelf: PyRef<Self>, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm_trace!("None.__getattribute__({:?}, {:?})", self, name);
|
||||
let cls = zelf.class();
|
||||
|
||||
// Properties use a comparision with None to determine if they are either invoked by am
|
||||
// instance binding or a class binding. But if the object itself is None then this detection
|
||||
// won't work. Instead we call a special function on property that bypasses this check, as
|
||||
// we are invoking it as a instance binding.
|
||||
//
|
||||
// In CPython they instead call the slot tp_descr_get with NULL to indicates it's an
|
||||
// instance binding.
|
||||
// https://github.com/python/cpython/blob/master/Objects/typeobject.c#L3281
|
||||
fn call_descriptor(
|
||||
descriptor: PyObjectRef,
|
||||
get_func: PyObjectRef,
|
||||
obj: PyObjectRef,
|
||||
cls: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
if let Ok(property) = PyPropertyRef::try_from_object(vm, descriptor.clone()) {
|
||||
property.instance_binding_get(obj, vm)
|
||||
} else {
|
||||
vm.invoke(&get_func, vec![descriptor, obj, cls])
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(attr) = cls.get_attr(name.as_str()) {
|
||||
let attr_class = attr.class();
|
||||
if attr_class.has_attr("__set__") {
|
||||
if let Some(get_func) = attr_class.get_attr("__get__") {
|
||||
return call_descriptor(
|
||||
attr,
|
||||
get_func,
|
||||
zelf.into_object(),
|
||||
cls.into_object(),
|
||||
vm,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// None has no attributes and cannot have attributes set on it.
|
||||
// if let Some(obj_attr) = zelf.as_object().get_attr(name.as_str()) {
|
||||
// Ok(obj_attr)
|
||||
// } else
|
||||
if let Some(attr) = cls.get_attr(name.as_str()) {
|
||||
let attr_class = attr.class();
|
||||
if let Some(get_func) = attr_class.get_attr("__get__") {
|
||||
call_descriptor(attr, get_func, zelf.into_object(), cls.into_object(), vm)
|
||||
} else {
|
||||
Ok(attr)
|
||||
}
|
||||
} else if let Some(getter) = cls.get_attr("__getattr__") {
|
||||
vm.invoke(&getter, vec![zelf.into_object(), name.into_object()])
|
||||
} else {
|
||||
Err(vm.new_attribute_error(format!("{} has no attribute '{}'", zelf.as_object(), name)))
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__eq__")]
|
||||
fn eq(&self, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
if vm.is_none(&rhs) {
|
||||
|
||||
@@ -175,12 +175,12 @@ impl PyBaseObject {
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__class__")]
|
||||
fn _class(obj: PyObjectRef, _vm: &VirtualMachine) -> PyObjectRef {
|
||||
fn get_class(obj: PyObjectRef, _vm: &VirtualMachine) -> PyObjectRef {
|
||||
obj.class().into_object()
|
||||
}
|
||||
|
||||
#[pyproperty(name = "__class__", setter)]
|
||||
fn _set_class(instance: PyObjectRef, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
fn set_class(instance: PyObjectRef, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let type_repr = vm.to_pystr(&instance.class())?;
|
||||
Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr)))
|
||||
}
|
||||
|
||||
@@ -4,51 +4,13 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use super::objtype::PyClassRef;
|
||||
use crate::function::{IntoPyNativeFunc, OptionalArg};
|
||||
use crate::function::OptionalArg;
|
||||
use crate::pyobject::{
|
||||
IdProtocol, PyClassImpl, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue,
|
||||
TypeProtocol,
|
||||
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
|
||||
};
|
||||
use crate::slots::PyBuiltinDescriptor;
|
||||
use crate::slots::SlotDescriptor;
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
// Read-only property, doesn't have __set__ or __delete__
|
||||
#[pyclass]
|
||||
#[derive(Debug)]
|
||||
pub struct PyReadOnlyProperty {
|
||||
getter: PyObjectRef,
|
||||
}
|
||||
|
||||
impl PyValue for PyReadOnlyProperty {
|
||||
fn class(vm: &VirtualMachine) -> PyClassRef {
|
||||
vm.ctx.readonly_property_type()
|
||||
}
|
||||
}
|
||||
|
||||
pub type PyReadOnlyPropertyRef = PyRef<PyReadOnlyProperty>;
|
||||
|
||||
impl PyBuiltinDescriptor for PyReadOnlyProperty {
|
||||
fn get(
|
||||
zelf: PyRef<Self>,
|
||||
obj: PyObjectRef,
|
||||
cls: OptionalArg<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
if vm.is_none(&obj) {
|
||||
if Self::_cls_is(&cls, &vm.ctx.types.type_type) {
|
||||
vm.invoke(&zelf.getter, cls.unwrap())
|
||||
} else {
|
||||
Ok(zelf.into_object())
|
||||
}
|
||||
} else {
|
||||
vm.invoke(&zelf.getter, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
impl PyReadOnlyProperty {}
|
||||
|
||||
/// Property attribute.
|
||||
///
|
||||
/// fget
|
||||
@@ -110,26 +72,28 @@ struct PropertyArgs {
|
||||
doc: Option<PyObjectRef>,
|
||||
}
|
||||
|
||||
impl PyBuiltinDescriptor for PyProperty {
|
||||
fn get(
|
||||
zelf: PyRef<Self>,
|
||||
obj: PyObjectRef,
|
||||
_cls: OptionalArg<PyObjectRef>,
|
||||
impl SlotDescriptor for PyProperty {
|
||||
#[allow(clippy::collapsible_if)]
|
||||
fn descr_get(
|
||||
vm: &VirtualMachine,
|
||||
zelf: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
_cls: OptionalArg<PyObjectRef>,
|
||||
) -> PyResult {
|
||||
if let Some(getter) = zelf.getter.as_ref() {
|
||||
if obj.is(vm.ctx.none.as_object()) {
|
||||
Ok(zelf.into_object())
|
||||
} else {
|
||||
vm.invoke(&getter, obj)
|
||||
}
|
||||
let (zelf, obj) = Self::_unwrap(zelf, obj, vm)?;
|
||||
if vm.is_none(&obj) {
|
||||
Ok(zelf.into_object())
|
||||
} else {
|
||||
Err(vm.new_attribute_error("unreadable attribute".to_owned()))
|
||||
if let Some(getter) = zelf.getter.as_ref() {
|
||||
vm.invoke(&getter, obj)
|
||||
} else {
|
||||
Err(vm.new_attribute_error("unreadable attribute".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinDescriptor), flags(BASETYPE))]
|
||||
#[pyimpl(with(SlotDescriptor), flags(BASETYPE))]
|
||||
impl PyProperty {
|
||||
#[pyslot]
|
||||
fn tp_new(cls: PyClassRef, args: PropertyArgs, vm: &VirtualMachine) -> PyResult<PyPropertyRef> {
|
||||
@@ -144,15 +108,6 @@ impl PyProperty {
|
||||
|
||||
// Descriptor methods
|
||||
|
||||
// specialised version that doesn't check for None
|
||||
pub(crate) fn instance_binding_get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
if let Some(ref getter) = self.getter.as_ref() {
|
||||
vm.invoke(getter, obj)
|
||||
} else {
|
||||
Err(vm.new_attribute_error("unreadable attribute".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "__set__")]
|
||||
fn set(&self, obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
if let Some(ref setter) = self.setter.as_ref() {
|
||||
@@ -253,81 +208,12 @@ fn py_none_to_option(vm: &VirtualMachine, value: &PyObjectRef) -> Option<PyObjec
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PropertyBuilder<'a> {
|
||||
ctx: &'a PyContext,
|
||||
getter: Option<PyObjectRef>,
|
||||
setter: Option<PyObjectRef>,
|
||||
}
|
||||
|
||||
pub trait PropertySetterResult {}
|
||||
|
||||
impl PropertySetterResult for PyResult<()> {}
|
||||
impl PropertySetterResult for () {}
|
||||
|
||||
impl<'a> PropertyBuilder<'a> {
|
||||
pub fn new(ctx: &'a PyContext) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
getter: None,
|
||||
setter: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_getter<I, V, VM, F: IntoPyNativeFunc<I, V, VM>>(self, func: F) -> Self {
|
||||
let func = self.ctx.new_method(func);
|
||||
Self {
|
||||
ctx: self.ctx,
|
||||
getter: Some(func),
|
||||
setter: self.setter,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_setter<I, V, VM, F: IntoPyNativeFunc<(I, V), impl PropertySetterResult, VM>>(
|
||||
self,
|
||||
func: F,
|
||||
) -> Self {
|
||||
let func = self.ctx.new_method(func);
|
||||
Self {
|
||||
ctx: self.ctx,
|
||||
getter: self.getter,
|
||||
setter: Some(func),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(self) -> PyObjectRef {
|
||||
if self.setter.is_some() {
|
||||
let payload = PyProperty {
|
||||
getter: self.getter.clone(),
|
||||
setter: self.setter.clone(),
|
||||
deleter: None,
|
||||
doc: RefCell::new(None),
|
||||
};
|
||||
|
||||
PyObject::new(payload, self.ctx.property_type(), None)
|
||||
} else {
|
||||
let payload = PyReadOnlyProperty {
|
||||
getter: self.getter.expect(
|
||||
"One of add_getter/add_setter must be called when constructing a property",
|
||||
),
|
||||
};
|
||||
|
||||
PyObject::new(payload, self.ctx.readonly_property_type(), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
PyReadOnlyProperty::extend_class(context, &context.types.readonly_property_type);
|
||||
|
||||
pub(crate) fn init(context: &PyContext) {
|
||||
PyProperty::extend_class(context, &context.types.property_type);
|
||||
|
||||
// This is a bit unfortunate, but this instance attribute overlaps with the
|
||||
// class __doc__ string..
|
||||
extend_class!(context, &context.types.property_type, {
|
||||
"__doc__" =>
|
||||
PropertyBuilder::new(context)
|
||||
.add_getter(PyProperty::doc_getter)
|
||||
.add_setter(PyProperty::doc_setter)
|
||||
.create(),
|
||||
"__doc__" => context.new_getset("__doc__", PyProperty::doc_getter, PyProperty::doc_setter),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::objtype::PyClassRef;
|
||||
use crate::function::OptionalArg;
|
||||
use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
|
||||
use crate::slots::PyBuiltinDescriptor;
|
||||
use crate::slots::SlotDescriptor;
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
#[pyclass(name = "staticmethod")]
|
||||
@@ -17,18 +17,19 @@ impl PyValue for PyStaticMethod {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyBuiltinDescriptor for PyStaticMethod {
|
||||
fn get(
|
||||
zelf: PyRef<Self>,
|
||||
_obj: PyObjectRef,
|
||||
impl SlotDescriptor for PyStaticMethod {
|
||||
fn descr_get(
|
||||
vm: &VirtualMachine,
|
||||
zelf: PyObjectRef,
|
||||
_obj: Option<PyObjectRef>,
|
||||
_cls: OptionalArg<PyObjectRef>,
|
||||
_vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let zelf = Self::_zelf(zelf, vm)?;
|
||||
Ok(zelf.callable.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinDescriptor), flags(BASETYPE))]
|
||||
#[pyimpl(with(SlotDescriptor), flags(BASETYPE))]
|
||||
impl PyStaticMethod {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
|
||||
@@ -63,7 +63,7 @@ impl PySuper {
|
||||
// This is a classmethod
|
||||
return Ok(item);
|
||||
}
|
||||
return vm.call_get_descriptor(item, inst.clone());
|
||||
return vm.call_if_get_descriptor(item, inst.clone());
|
||||
}
|
||||
}
|
||||
Err(vm.new_attribute_error(format!(
|
||||
|
||||
@@ -5,11 +5,10 @@ use std::fmt;
|
||||
use super::objdict::PyDictRef;
|
||||
use super::objlist::PyList;
|
||||
use super::objmappingproxy::PyMappingProxy;
|
||||
use super::objproperty::PropertyBuilder;
|
||||
use super::objstr::PyStringRef;
|
||||
use super::objtuple::PyTuple;
|
||||
use super::objweakref::PyWeak;
|
||||
use crate::function::PyFuncArgs;
|
||||
use crate::function::{OptionalArg, PyFuncArgs};
|
||||
use crate::pyobject::{
|
||||
IdProtocol, PyAttributes, PyClassImpl, PyContext, PyIterable, PyObject, PyObjectRef, PyRef,
|
||||
PyResult, PyValue, TypeProtocol,
|
||||
@@ -80,16 +79,13 @@ impl PyClassRef {
|
||||
}
|
||||
}
|
||||
|
||||
fn _mro(self, _vm: &VirtualMachine) -> PyTuple {
|
||||
#[pyproperty(name = "__mro__")]
|
||||
fn get_mro(self, _vm: &VirtualMachine) -> PyTuple {
|
||||
let elements: Vec<PyObjectRef> =
|
||||
_mro(&self).iter().map(|x| x.as_object().clone()).collect();
|
||||
PyTuple::from(elements)
|
||||
}
|
||||
|
||||
fn _set_mro(self, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
Err(vm.new_attribute_error("read-only attribute".to_owned()))
|
||||
}
|
||||
|
||||
#[pyproperty(magic)]
|
||||
fn bases(self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx
|
||||
@@ -145,6 +141,13 @@ impl PyClassRef {
|
||||
.unwrap_or_else(|| vm.ctx.new_str("builtins".to_owned()))
|
||||
}
|
||||
|
||||
#[pyproperty(magic, setter)]
|
||||
fn set_module(self, value: PyObjectRef) {
|
||||
self.attributes
|
||||
.borrow_mut()
|
||||
.insert("__module__".to_owned(), value);
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &VirtualMachine) -> PyDictRef {
|
||||
vm.ctx.new_dict()
|
||||
@@ -170,7 +173,11 @@ impl PyClassRef {
|
||||
|
||||
if let Some(attr) = self.get_attr(&name) {
|
||||
let attr_class = attr.class();
|
||||
if let Some(ref descriptor) = attr_class.get_attr("__get__") {
|
||||
let slots = attr_class.slots.borrow();
|
||||
if let Some(ref descr_get) = slots.descr_get {
|
||||
return descr_get(vm, attr, None, OptionalArg::Present(self.into_object()));
|
||||
} else if let Some(ref descriptor) = attr_class.get_attr("__get__") {
|
||||
// TODO: is this nessessary?
|
||||
return vm.invoke(descriptor, vec![attr, vm.get_none(), self.into_object()]);
|
||||
}
|
||||
}
|
||||
@@ -178,7 +185,7 @@ impl PyClassRef {
|
||||
if let Some(cls_attr) = self.get_attr(&name) {
|
||||
Ok(cls_attr)
|
||||
} else if let Some(attr) = mcl.get_attr(&name) {
|
||||
vm.call_get_descriptor(attr, self.into_object())
|
||||
vm.call_if_get_descriptor(attr, self.into_object())
|
||||
} else if let Some(ref getter) = self.get_attr("__getattr__") {
|
||||
vm.invoke(getter, vec![mcl.into_object(), name_ref.into_object()])
|
||||
} else {
|
||||
@@ -327,6 +334,18 @@ impl PyClassRef {
|
||||
}
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
#[pyproperty(magic)]
|
||||
fn dict(self) -> PyMappingProxy {
|
||||
PyMappingProxy::new(self)
|
||||
}
|
||||
|
||||
#[pyproperty(magic, setter)]
|
||||
fn set_dict(self, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
Err(vm.new_not_implemented_error(
|
||||
"Setting __dict__ attribute on a type isn't yet implemented".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -335,18 +354,6 @@ impl PyClassRef {
|
||||
|
||||
pub(crate) fn init(ctx: &PyContext) {
|
||||
PyClassRef::extend_class(ctx, &ctx.types.type_type);
|
||||
extend_class!(&ctx, &ctx.types.type_type, {
|
||||
"__dict__" =>
|
||||
PropertyBuilder::new(ctx)
|
||||
.add_getter(type_dict)
|
||||
.add_setter(type_dict_setter)
|
||||
.create(),
|
||||
"__mro__" =>
|
||||
PropertyBuilder::new(ctx)
|
||||
.add_getter(PyClassRef::_mro)
|
||||
.add_setter(PyClassRef::_set_mro)
|
||||
.create(),
|
||||
});
|
||||
}
|
||||
|
||||
fn _mro(cls: &PyClassRef) -> Vec<PyClassRef> {
|
||||
@@ -398,20 +405,6 @@ pub fn type_new(
|
||||
new(vm, args.insert(cls.into_object()))
|
||||
}
|
||||
|
||||
fn type_dict(class: PyClassRef, _vm: &VirtualMachine) -> PyMappingProxy {
|
||||
PyMappingProxy::new(class)
|
||||
}
|
||||
|
||||
fn type_dict_setter(
|
||||
_instance: PyClassRef,
|
||||
_value: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
Err(vm.new_not_implemented_error(
|
||||
"Setting __dict__ attribute on a type isn't yet implemented".to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
impl PyClassRef {
|
||||
/// This is the internal get_attr implementation for fast lookup on a class.
|
||||
pub fn get_attr(&self, attr_name: &str) -> Option<PyObjectRef> {
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::function::{OptionalArg, PyFuncArgs};
|
||||
use crate::pyobject::{
|
||||
PyClassImpl, PyContext, PyObject, PyObjectPayload, PyObjectRef, PyRef, PyResult, PyValue,
|
||||
};
|
||||
use crate::slots::PyBuiltinCallable;
|
||||
use crate::slots::SlotCall;
|
||||
use crate::vm::VirtualMachine;
|
||||
|
||||
use std::rc::{Rc, Weak};
|
||||
@@ -34,14 +34,14 @@ impl PyValue for PyWeak {
|
||||
|
||||
pub type PyWeakRef = PyRef<PyWeak>;
|
||||
|
||||
impl PyBuiltinCallable for PyWeak {
|
||||
impl SlotCall for PyWeak {
|
||||
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
args.bind::<()>(vm)?;
|
||||
Ok(self.referent.upgrade().unwrap_or_else(|| vm.get_none()))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinCallable), flags(BASETYPE))]
|
||||
#[pyimpl(with(SlotCall), flags(BASETYPE))]
|
||||
impl PyWeak {
|
||||
// TODO callbacks
|
||||
#[pyslot]
|
||||
|
||||
@@ -25,13 +25,13 @@ use crate::obj::objcomplex::PyComplex;
|
||||
use crate::obj::objdict::{PyDict, PyDictRef};
|
||||
use crate::obj::objfloat::PyFloat;
|
||||
use crate::obj::objfunction::{PyBoundMethod, PyFunction};
|
||||
use crate::obj::objgetset::{IntoPyGetterFunc, IntoPySetterFunc, PyGetSet};
|
||||
use crate::obj::objint::{PyInt, PyIntRef};
|
||||
use crate::obj::objiter;
|
||||
use crate::obj::objlist::PyList;
|
||||
use crate::obj::objnamespace::PyNamespace;
|
||||
use crate::obj::objnone::{PyNone, PyNoneRef};
|
||||
use crate::obj::objobject;
|
||||
use crate::obj::objproperty::PropertyBuilder;
|
||||
use crate::obj::objset::PySet;
|
||||
use crate::obj::objstr;
|
||||
use crate::obj::objtuple::{PyTuple, PyTupleRef};
|
||||
@@ -330,6 +330,10 @@ impl PyContext {
|
||||
self.types.readonly_property_type.clone()
|
||||
}
|
||||
|
||||
pub fn getset_type(&self) -> PyClassRef {
|
||||
self.types.getset_type.clone()
|
||||
}
|
||||
|
||||
pub fn classmethod_type(&self) -> PyClassRef {
|
||||
self.types.classmethod_type.clone()
|
||||
}
|
||||
@@ -499,11 +503,23 @@ impl PyContext {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_property<F, I, V, VM>(&self, f: F) -> PyObjectRef
|
||||
pub fn new_readonly_getset<F, T>(&self, name: impl Into<String>, f: F) -> PyObjectRef
|
||||
where
|
||||
F: IntoPyNativeFunc<I, V, VM>,
|
||||
F: IntoPyGetterFunc<T>,
|
||||
{
|
||||
PropertyBuilder::new(self).add_getter(f).create()
|
||||
PyObject::new(PyGetSet::with_get(name.into(), f), self.getset_type(), None)
|
||||
}
|
||||
|
||||
pub fn new_getset<G, S, T, U>(&self, name: impl Into<String>, g: G, s: S) -> PyObjectRef
|
||||
where
|
||||
G: IntoPyGetterFunc<T>,
|
||||
S: IntoPySetterFunc<U>,
|
||||
{
|
||||
PyObject::new(
|
||||
PyGetSet::with_get_set(name.into(), g, s),
|
||||
self.getset_type(),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyCodeRef {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc};
|
||||
use crate::pyobject::{IdProtocol, PyObjectRef, PyRef, PyResult, PyValue};
|
||||
use crate::pyobject::{IdProtocol, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject};
|
||||
use crate::VirtualMachine;
|
||||
|
||||
bitflags! {
|
||||
@@ -28,7 +28,7 @@ pub struct PyClassSlots {
|
||||
pub flags: PyTpFlags,
|
||||
pub new: Option<PyNativeFunc>,
|
||||
pub call: Option<PyNativeFunc>,
|
||||
pub descr_get: Option<PyNativeFunc>,
|
||||
pub descr_get: Option<PyDescrGetFunc>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PyClassSlots {
|
||||
@@ -38,22 +38,73 @@ impl std::fmt::Debug for PyClassSlots {
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
pub trait PyBuiltinCallable: PyValue {
|
||||
pub trait SlotCall: PyValue {
|
||||
#[pymethod(magic)]
|
||||
#[pyslot]
|
||||
fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult;
|
||||
}
|
||||
|
||||
pub type PyDescrGetFunc = Box<
|
||||
dyn Fn(&VirtualMachine, PyObjectRef, Option<PyObjectRef>, OptionalArg<PyObjectRef>) -> PyResult,
|
||||
>;
|
||||
|
||||
#[pyimpl]
|
||||
pub trait PyBuiltinDescriptor: PyValue {
|
||||
pub trait SlotDescriptor: PyValue {
|
||||
#[pyslot]
|
||||
fn descr_get(
|
||||
vm: &VirtualMachine,
|
||||
zelf: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
cls: OptionalArg<PyObjectRef>,
|
||||
) -> PyResult;
|
||||
|
||||
#[pymethod(magic)]
|
||||
#[pyslot(descr_get)]
|
||||
fn get(
|
||||
zelf: PyRef<Self>,
|
||||
zelf: PyObjectRef,
|
||||
obj: PyObjectRef,
|
||||
cls: OptionalArg<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult;
|
||||
) -> PyResult {
|
||||
Self::descr_get(vm, zelf, Some(obj), cls)
|
||||
}
|
||||
|
||||
fn _zelf(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
PyRef::<Self>::try_from_object(vm, zelf)
|
||||
}
|
||||
|
||||
fn _unwrap(
|
||||
zelf: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<(PyRef<Self>, PyObjectRef)> {
|
||||
let zelf = Self::_zelf(zelf, vm)?;
|
||||
let obj = obj.unwrap_or_else(|| vm.get_none());
|
||||
Ok((zelf, obj))
|
||||
}
|
||||
|
||||
fn _check(
|
||||
zelf: PyObjectRef,
|
||||
obj: Option<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> Result<(PyRef<Self>, PyObjectRef), PyResult> {
|
||||
// CPython descr_check
|
||||
if let Some(obj) = obj {
|
||||
// if (!PyObject_TypeCheck(obj, descr->d_type)) {
|
||||
// PyErr_Format(PyExc_TypeError,
|
||||
// "descriptor '%V' for '%.100s' objects "
|
||||
// "doesn't apply to a '%.100s' object",
|
||||
// descr_name((PyDescrObject *)descr), "?",
|
||||
// descr->d_type->tp_name,
|
||||
// obj->ob_type->tp_name);
|
||||
// *pres = NULL;
|
||||
// return 1;
|
||||
// } else {
|
||||
Ok((Self::_zelf(zelf, vm).unwrap(), obj))
|
||||
// }
|
||||
} else {
|
||||
Err(Ok(zelf))
|
||||
}
|
||||
}
|
||||
|
||||
fn _cls_is<T>(cls: &OptionalArg<PyObjectRef>, other: &T) -> bool
|
||||
where
|
||||
|
||||
@@ -564,7 +564,7 @@ mod fileio {
|
||||
vm.set_attr(&file_io, "name", name)?;
|
||||
vm.set_attr(&file_io, "__fileno", vm.new_int(file_no))?;
|
||||
vm.set_attr(&file_io, "closefd", vm.new_bool(false))?;
|
||||
vm.set_attr(&file_io, "closed", vm.new_bool(false))?;
|
||||
vm.set_attr(&file_io, "__closed", vm.new_bool(false))?;
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
|
||||
@@ -665,7 +665,7 @@ mod fileio {
|
||||
winapi::um::handleapi::CloseHandle(raw_handle as _);
|
||||
}
|
||||
vm.set_attr(&instance, "closefd", vm.new_bool(true))?;
|
||||
vm.set_attr(&instance, "closed", vm.new_bool(true))?;
|
||||
vm.set_attr(&instance, "__closed", vm.new_bool(true))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -676,7 +676,7 @@ mod fileio {
|
||||
libc::close(raw_fd as _);
|
||||
}
|
||||
vm.set_attr(&instance, "closefd", vm.new_bool(true))?;
|
||||
vm.set_attr(&instance, "closed", vm.new_bool(true))?;
|
||||
vm.set_attr(&instance, "__closed", vm.new_bool(true))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -952,7 +952,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
"readable" => ctx.new_method(io_base_readable),
|
||||
"writable" => ctx.new_method(io_base_writable),
|
||||
"flush" => ctx.new_method(io_base_flush),
|
||||
"closed" => ctx.new_property(io_base_closed),
|
||||
"closed" => ctx.new_readonly_getset("closed", io_base_closed),
|
||||
"__closed" => ctx.new_bool(false),
|
||||
"close" => ctx.new_method(io_base_close),
|
||||
"readline" => ctx.new_method(io_base_readline),
|
||||
@@ -1017,7 +1017,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
"tell" => ctx.new_method(PyStringIORef::tell),
|
||||
"readline" => ctx.new_method(PyStringIORef::readline),
|
||||
"truncate" => ctx.new_method(PyStringIORef::truncate),
|
||||
"closed" => ctx.new_property(PyStringIORef::closed),
|
||||
"closed" => ctx.new_readonly_getset("closed", PyStringIORef::closed),
|
||||
"close" => ctx.new_method(PyStringIORef::close),
|
||||
});
|
||||
|
||||
@@ -1033,7 +1033,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
"tell" => ctx.new_method(PyBytesIORef::tell),
|
||||
"readline" => ctx.new_method(PyBytesIORef::readline),
|
||||
"truncate" => ctx.new_method(PyBytesIORef::truncate),
|
||||
"closed" => ctx.new_property(PyBytesIORef::closed),
|
||||
"closed" => ctx.new_readonly_getset("closed", PyBytesIORef::closed),
|
||||
"close" => ctx.new_method(PyBytesIORef::close),
|
||||
});
|
||||
|
||||
|
||||
@@ -1235,8 +1235,8 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
ScandirIterator::extend_class(ctx, &scandir_iter);
|
||||
|
||||
let dir_entry = py_class!(ctx, "DirEntry", ctx.object(), {
|
||||
"name" => ctx.new_property(DirEntryRef::name),
|
||||
"path" => ctx.new_property(DirEntryRef::path),
|
||||
"name" => ctx.new_readonly_getset("name", DirEntryRef::name),
|
||||
"path" => ctx.new_readonly_getset("path", DirEntryRef::path),
|
||||
"is_dir" => ctx.new_method(DirEntryRef::is_dir),
|
||||
"is_file" => ctx.new_method(DirEntryRef::is_file),
|
||||
"is_symlink" => ctx.new_method(DirEntryRef::is_symlink),
|
||||
|
||||
@@ -68,13 +68,13 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
let ctx = &vm.ctx;
|
||||
|
||||
let passwd_type = py_class!(ctx, "struct_passwd", ctx.object(), {
|
||||
"pw_name" => ctx.new_property(PasswdRef::pw_name),
|
||||
"pw_passwd" => ctx.new_property(PasswdRef::pw_passwd),
|
||||
"pw_uid" => ctx.new_property(PasswdRef::pw_uid),
|
||||
"pw_gid" => ctx.new_property(PasswdRef::pw_gid),
|
||||
"pw_gecos" => ctx.new_property(PasswdRef::pw_gecos),
|
||||
"pw_dir" => ctx.new_property(PasswdRef::pw_dir),
|
||||
"pw_shell" => ctx.new_property(PasswdRef::pw_shell),
|
||||
"pw_name" => ctx.new_readonly_getset("pw_name", PasswdRef::pw_name),
|
||||
"pw_passwd" => ctx.new_readonly_getset("pw_passwd", PasswdRef::pw_passwd),
|
||||
"pw_uid" => ctx.new_readonly_getset("pw_uid", PasswdRef::pw_uid),
|
||||
"pw_gid" => ctx.new_readonly_getset("pw_gid", PasswdRef::pw_gid),
|
||||
"pw_gecos" => ctx.new_readonly_getset("pw_gecos", PasswdRef::pw_gecos),
|
||||
"pw_dir" => ctx.new_readonly_getset("pw_dir", PasswdRef::pw_dir),
|
||||
"pw_shell" => ctx.new_readonly_getset("pw_shell", PasswdRef::pw_shell),
|
||||
});
|
||||
|
||||
py_module!(vm, "pwd", {
|
||||
|
||||
@@ -253,18 +253,18 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
let popen = py_class!(ctx, "Popen", ctx.object(), {
|
||||
(slot new) => PopenRef::new,
|
||||
"poll" => ctx.new_method(PopenRef::poll),
|
||||
"returncode" => ctx.new_property(PopenRef::return_code),
|
||||
"returncode" => ctx.new_readonly_getset("returncode", PopenRef::return_code),
|
||||
"wait" => ctx.new_method(PopenRef::wait),
|
||||
"stdin" => ctx.new_property(PopenRef::stdin),
|
||||
"stdout" => ctx.new_property(PopenRef::stdout),
|
||||
"stderr" => ctx.new_property(PopenRef::stderr),
|
||||
"stdin" => ctx.new_readonly_getset("stdin", PopenRef::stdin),
|
||||
"stdout" => ctx.new_readonly_getset("stdout", PopenRef::stdout),
|
||||
"stderr" => ctx.new_readonly_getset("stderr", PopenRef::stderr),
|
||||
"terminate" => ctx.new_method(PopenRef::terminate),
|
||||
"kill" => ctx.new_method(PopenRef::kill),
|
||||
"communicate" => ctx.new_method(PopenRef::communicate),
|
||||
"pid" => ctx.new_property(PopenRef::pid),
|
||||
"pid" => ctx.new_readonly_getset("pid", PopenRef::pid),
|
||||
"__enter__" => ctx.new_method(PopenRef::enter),
|
||||
"__exit__" => ctx.new_method(PopenRef::exit),
|
||||
"args" => ctx.new_property(PopenRef::args),
|
||||
"args" => ctx.new_readonly_getset("args", PopenRef::args),
|
||||
});
|
||||
|
||||
py_module!(vm, "_subprocess", {
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::obj::objfloat;
|
||||
use crate::obj::objframe;
|
||||
use crate::obj::objfunction;
|
||||
use crate::obj::objgenerator;
|
||||
use crate::obj::objgetset;
|
||||
use crate::obj::objint;
|
||||
use crate::obj::objiter;
|
||||
use crate::obj::objlist;
|
||||
@@ -94,6 +95,7 @@ pub struct TypeZoo {
|
||||
pub method_descriptor_type: PyClassRef,
|
||||
pub property_type: PyClassRef,
|
||||
pub readonly_property_type: PyClassRef,
|
||||
pub getset_type: PyClassRef,
|
||||
pub module_type: PyClassRef,
|
||||
pub namespace_type: PyClassRef,
|
||||
pub bound_method_type: PyClassRef,
|
||||
@@ -125,6 +127,7 @@ impl TypeZoo {
|
||||
let method_descriptor_type = create_type("method_descriptor", &type_type, &object_type);
|
||||
let property_type = create_type("property", &type_type, &object_type);
|
||||
let readonly_property_type = create_type("readonly_property", &type_type, &object_type);
|
||||
let getset_type = create_type("getset_descriptor", &type_type, &object_type);
|
||||
let super_type = create_type("super", &type_type, &object_type);
|
||||
let weakref_type = create_type("ref", &type_type, &object_type);
|
||||
let weakproxy_type = create_type("weakproxy", &type_type, &object_type);
|
||||
@@ -220,6 +223,7 @@ impl TypeZoo {
|
||||
mappingproxy_type,
|
||||
property_type,
|
||||
readonly_property_type,
|
||||
getset_type,
|
||||
generator_type,
|
||||
module_type,
|
||||
namespace_type,
|
||||
@@ -381,6 +385,7 @@ pub fn initialize_types(context: &PyContext) {
|
||||
objbytes::init(&context);
|
||||
objbytearray::init(&context);
|
||||
objproperty::init(&context);
|
||||
objgetset::init(&context);
|
||||
objmemory::init(&context);
|
||||
objstr::init(&context);
|
||||
objrange::init(&context);
|
||||
|
||||
45
vm/src/vm.rs
45
vm/src/vm.rs
@@ -24,7 +24,7 @@ use crate::bytecode;
|
||||
use crate::exceptions::{PyBaseException, PyBaseExceptionRef};
|
||||
use crate::frame::{ExecutionResult, Frame, FrameRef};
|
||||
use crate::frozen;
|
||||
use crate::function::PyFuncArgs;
|
||||
use crate::function::{OptionalArg, PyFuncArgs};
|
||||
use crate::import;
|
||||
use crate::obj::objbool;
|
||||
use crate::obj::objcode::{PyCode, PyCodeRef};
|
||||
@@ -618,18 +618,28 @@ impl VirtualMachine {
|
||||
objbool::boolval(self, ret)
|
||||
}
|
||||
|
||||
pub fn call_get_descriptor(&self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult {
|
||||
let attr_class = attr.class();
|
||||
let slots = attr_class.slots.borrow();
|
||||
if let Some(descr_get) = slots.borrow().descr_get.as_ref() {
|
||||
pub fn call_get_descriptor(&self, descr: PyObjectRef, obj: PyObjectRef) -> Option<PyResult> {
|
||||
let descr_class = descr.class();
|
||||
let slots = descr_class.slots.borrow();
|
||||
Some(if let Some(descr_get) = slots.borrow().descr_get.as_ref() {
|
||||
let cls = obj.class();
|
||||
descr_get(self, vec![attr, obj.clone(), cls.into_object()].into())
|
||||
} else if let Some(ref descriptor) = attr_class.get_attr("__get__") {
|
||||
descr_get(
|
||||
self,
|
||||
descr,
|
||||
Some(obj.clone()),
|
||||
OptionalArg::Present(cls.into_object()),
|
||||
)
|
||||
} else if let Some(ref descriptor) = descr_class.get_attr("__get__") {
|
||||
let cls = obj.class();
|
||||
self.invoke(descriptor, vec![attr, obj.clone(), cls.into_object()])
|
||||
self.invoke(descriptor, vec![descr, obj.clone(), cls.into_object()])
|
||||
} else {
|
||||
Ok(attr)
|
||||
}
|
||||
return None;
|
||||
})
|
||||
}
|
||||
|
||||
pub fn call_if_get_descriptor(&self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult {
|
||||
self.call_get_descriptor(attr.clone(), obj)
|
||||
.unwrap_or(Ok(attr))
|
||||
}
|
||||
|
||||
pub fn call_method<T>(&self, obj: &PyObjectRef, method_name: &str, args: T) -> PyResult
|
||||
@@ -649,7 +659,7 @@ impl VirtualMachine {
|
||||
method_name,
|
||||
func
|
||||
);
|
||||
let wrapped = self.call_get_descriptor(func, obj.clone())?;
|
||||
let wrapped = self.call_if_get_descriptor(func, obj.clone())?;
|
||||
self.invoke(&wrapped, args)
|
||||
}
|
||||
None => Err(self.new_type_error(format!("Unsupported method: {}", method_name))),
|
||||
@@ -782,7 +792,7 @@ impl VirtualMachine {
|
||||
{
|
||||
let cls = obj.class();
|
||||
match cls.get_attr(method_name) {
|
||||
Some(method) => self.call_get_descriptor(method, obj.clone()),
|
||||
Some(method) => self.call_if_get_descriptor(method, obj.clone()),
|
||||
None => Err(self.new_type_error(err_msg())),
|
||||
}
|
||||
}
|
||||
@@ -791,7 +801,7 @@ impl VirtualMachine {
|
||||
pub fn get_method(&self, obj: PyObjectRef, method_name: &str) -> Option<PyResult> {
|
||||
let cls = obj.class();
|
||||
let method = cls.get_attr(method_name)?;
|
||||
Some(self.call_get_descriptor(method, obj.clone()))
|
||||
Some(self.call_if_get_descriptor(method, obj.clone()))
|
||||
}
|
||||
|
||||
/// Calls a method on `obj` passing `arg`, if the method exists.
|
||||
@@ -843,6 +853,7 @@ impl VirtualMachine {
|
||||
})
|
||||
}
|
||||
|
||||
/// CPython _PyObject_GenericGetAttrWithDict
|
||||
pub fn generic_getattribute(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
@@ -854,10 +865,8 @@ impl VirtualMachine {
|
||||
if let Some(attr) = cls.get_attr(&name) {
|
||||
let attr_class = attr.class();
|
||||
if attr_class.has_attr("__set__") {
|
||||
if let Some(descriptor) = attr_class.get_attr("__get__") {
|
||||
return self
|
||||
.invoke(&descriptor, vec![attr, obj, cls.into_object()])
|
||||
.map(Some);
|
||||
if let Some(r) = self.call_get_descriptor(attr, obj.clone()) {
|
||||
return r.map(Some);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -871,7 +880,7 @@ impl VirtualMachine {
|
||||
if let Some(obj_attr) = attr {
|
||||
Ok(Some(obj_attr))
|
||||
} else if let Some(attr) = cls.get_attr(&name) {
|
||||
self.call_get_descriptor(attr, obj).map(Some)
|
||||
self.call_if_get_descriptor(attr, obj).map(Some)
|
||||
} else if let Some(getter) = cls.get_attr("__getattr__") {
|
||||
self.invoke(&getter, vec![obj, name_str.into_object()])
|
||||
.map(Some)
|
||||
|
||||
Reference in New Issue
Block a user