diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index b7bdff2c2..b5894ce9b 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -692,7 +692,8 @@ pub fn impl_pystruct_sequence(attr: AttributeArgs, item: Item) -> Result 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, { @@ -665,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)), }); } diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index cfb634c8b..b84e50c0e 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -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), }); } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index cc945f96b..7bc683bc2 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -4,52 +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::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; - -impl SlotDescriptor for PyReadOnlyProperty { - fn descr_get( - vm: &VirtualMachine, - zelf: PyObjectRef, - obj: Option, - cls: OptionalArg, - ) -> PyResult { - let (zelf, obj) = Self::_unwrap(zelf, obj, vm)?; - 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(SlotDescriptor))] -impl PyReadOnlyProperty {} - /// Property attribute. /// /// fget @@ -255,81 +216,12 @@ fn py_none_to_option(vm: &VirtualMachine, value: &PyObjectRef) -> Option { - ctx: &'a PyContext, - getter: Option, - setter: Option, -} - -impl<'a> PropertyBuilder<'a> { - pub fn new(ctx: &'a PyContext) -> Self { - Self { - ctx, - getter: None, - setter: None, - } - } - - pub fn add_getter>(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 super::objgetset::IntoPyNoResult, 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), }); } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 7d5cafaea..4a4a6b6d3 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -5,7 +5,6 @@ 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; @@ -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 = _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 @@ -338,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(), + )) + } } /* @@ -346,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 { @@ -409,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 { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 555d480bd..b42be1ba8 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -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}; @@ -503,11 +503,23 @@ impl PyContext { ) } - pub fn new_property(&self, f: F) -> PyObjectRef + pub fn new_readonly_getset(&self, name: impl Into, f: F) -> PyObjectRef where - F: IntoPyNativeFunc, + F: IntoPyGetterFunc, { - PropertyBuilder::new(self).add_getter(f).create() + PyObject::new(PyGetSet::with_get(name.into(), f), self.getset_type(), None) + } + + pub fn new_getset(&self, name: impl Into, g: G, s: S) -> PyObjectRef + where + G: IntoPyGetterFunc, + S: IntoPySetterFunc, + { + PyObject::new( + PyGetSet::with_get_set(name.into(), g, s), + self.getset_type(), + None, + ) } pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyCodeRef { diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 378fe5f4f..cc43860e2 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -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), }); diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index a6be3e526..b96ec766d 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -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), diff --git a/vm/src/stdlib/pwd.rs b/vm/src/stdlib/pwd.rs index ecff167b8..0ad3942a4 100644 --- a/vm/src/stdlib/pwd.rs +++ b/vm/src/stdlib/pwd.rs @@ -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", { diff --git a/vm/src/stdlib/subprocess.rs b/vm/src/stdlib/subprocess.rs index 78e8bfafc..35ae5115a 100644 --- a/vm/src/stdlib/subprocess.rs +++ b/vm/src/stdlib/subprocess.rs @@ -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", {