Merge pull request #1738 from youknowone/getset

Add getset_descriptor
This commit is contained in:
Noah
2020-02-05 19:33:53 -06:00
committed by GitHub
26 changed files with 549 additions and 376 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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)),
});
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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(

View File

@@ -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),
});
}

View File

@@ -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
}

View File

@@ -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
View 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);
}

View File

@@ -626,6 +626,7 @@ impl PyInt {
}
#[pyproperty]
fn real(&self, vm: &VirtualMachine) -> PyObjectRef {
// subclasses must return int here
vm.ctx.new_bigint(&self.value)
}

View File

@@ -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) {

View File

@@ -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)))
}

View File

@@ -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),
});
}

View File

@@ -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(

View File

@@ -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!(

View File

@@ -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> {

View File

@@ -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]

View File

@@ -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 {

View File

@@ -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

View File

@@ -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),
});

View File

@@ -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),

View File

@@ -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", {

View File

@@ -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", {

View File

@@ -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);

View File

@@ -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)