From 0d7ed0eb4fc89ba3748842c3bf579100187aab21 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 24 Jun 2025 10:32:46 +0900 Subject: [PATCH 1/5] property getter_doc --- Lib/test/test_property.py | 10 --- vm/src/builtins/property.rs | 136 +++++++++++++++++++++++++++++------- 2 files changed, 112 insertions(+), 34 deletions(-) diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py index 5312925d93..d2da5827fb 100644 --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -100,32 +100,24 @@ def test_property_decorator_subclass(self): self.assertRaises(PropertySet, setattr, sub, "spam", None) self.assertRaises(PropertyDel, delattr, sub, "spam") - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_property_decorator_subclass_doc(self): sub = SubClass() self.assertEqual(sub.__class__.spam.__doc__, "SubClass.getter") - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_property_decorator_baseclass_doc(self): base = BaseClass() self.assertEqual(base.__class__.spam.__doc__, "BaseClass.getter") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_property_decorator_doc(self): base = PropertyDocBase() sub = PropertyDocSub() self.assertEqual(base.__class__.spam.__doc__, "spam spam spam") self.assertEqual(sub.__class__.spam.__doc__, "spam spam spam") - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_property_getter_doc_override(self): @@ -169,8 +161,6 @@ def test_property_builtin_doc_writable(self): p.__doc__ = 'extended' self.assertEqual(p.__doc__, 'extended') - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_property_decorator_doc_writable(self): diff --git a/vm/src/builtins/property.rs b/vm/src/builtins/property.rs index 1ea0c61d14..008c198aff 100644 --- a/vm/src/builtins/property.rs +++ b/vm/src/builtins/property.rs @@ -10,6 +10,7 @@ use crate::{ function::{FuncArgs, PySetterValue}, types::{Constructor, GetDescriptor, Initializer}, }; +use std::sync::atomic::{AtomicBool, Ordering}; #[pyclass(module = false, name = "property", traverse)] #[derive(Debug)] @@ -19,6 +20,8 @@ pub struct PyProperty { deleter: PyRwLock>, doc: PyRwLock>, name: PyRwLock>, + #[pytraverse(skip)] + getter_doc: std::sync::atomic::AtomicBool, } impl PyPayload for PyProperty { @@ -149,14 +152,38 @@ impl PyProperty { getter: Option, vm: &VirtualMachine, ) -> PyResult> { - PyProperty { - getter: PyRwLock::new(getter.or_else(|| zelf.fget())), - setter: PyRwLock::new(zelf.fset()), - deleter: PyRwLock::new(zelf.fdel()), - doc: PyRwLock::new(None), - name: PyRwLock::new(None), + let new_getter = getter.or_else(|| zelf.fget()); + + // Determine doc based on getter_doc flag + let doc = if zelf.getter_doc.load(Ordering::Relaxed) && new_getter.is_some() { + // If the original property uses getter doc and we have a new getter, + // pass Py_None to let __init__ get the doc from the new getter + Some(vm.ctx.none()) + } else { + // Otherwise use the existing doc + zelf.doc_getter() + }; + + // Create property args + let args = PropertyArgs { + fget: new_getter, + fset: zelf.fset(), + fdel: zelf.fdel(), + doc, + name: None, + }; + + // Create new property using py_new and init + let new_prop = PyProperty::py_new(zelf.class().to_owned(), FuncArgs::default(), vm)?; + let new_prop_ref = new_prop.downcast::().unwrap(); + PyProperty::init(new_prop_ref.clone(), args, vm)?; + + // Copy the name if it exists + if let Some(name) = zelf.name.read().clone() { + *new_prop_ref.name.write() = Some(name); } - .into_ref_with_type(vm, zelf.class().to_owned()) + + Ok(new_prop_ref) } #[pymethod] @@ -165,14 +192,32 @@ impl PyProperty { setter: Option, vm: &VirtualMachine, ) -> PyResult> { - PyProperty { - getter: PyRwLock::new(zelf.fget()), - setter: PyRwLock::new(setter.or_else(|| zelf.fset())), - deleter: PyRwLock::new(zelf.fdel()), - doc: PyRwLock::new(None), - name: PyRwLock::new(None), + // For setter, we need to preserve doc handling from the original property + let doc = if zelf.getter_doc.load(Ordering::Relaxed) { + // If original used getter_doc, pass None to let init get doc from getter + Some(vm.ctx.none()) + } else { + zelf.doc_getter() + }; + + let args = PropertyArgs { + fget: zelf.fget(), + fset: setter.or_else(|| zelf.fset()), + fdel: zelf.fdel(), + doc, + name: None, + }; + + let new_prop = PyProperty::py_new(zelf.class().to_owned(), FuncArgs::default(), vm)?; + let new_prop_ref = new_prop.downcast::().unwrap(); + PyProperty::init(new_prop_ref.clone(), args, vm)?; + + // Copy the name if it exists + if let Some(name) = zelf.name.read().clone() { + *new_prop_ref.name.write() = Some(name); } - .into_ref_with_type(vm, zelf.class().to_owned()) + + Ok(new_prop_ref) } #[pymethod] @@ -181,14 +226,32 @@ impl PyProperty { deleter: Option, vm: &VirtualMachine, ) -> PyResult> { - PyProperty { - getter: PyRwLock::new(zelf.fget()), - setter: PyRwLock::new(zelf.fset()), - deleter: PyRwLock::new(deleter.or_else(|| zelf.fdel())), - doc: PyRwLock::new(None), - name: PyRwLock::new(None), + // For deleter, we need to preserve doc handling from the original property + let doc = if zelf.getter_doc.load(Ordering::Relaxed) { + // If original used getter_doc, pass None to let init get doc from getter + Some(vm.ctx.none()) + } else { + zelf.doc_getter() + }; + + let args = PropertyArgs { + fget: zelf.fget(), + fset: zelf.fset(), + fdel: deleter.or_else(|| zelf.fdel()), + doc, + name: None, + }; + + let new_prop = PyProperty::py_new(zelf.class().to_owned(), FuncArgs::default(), vm)?; + let new_prop_ref = new_prop.downcast::().unwrap(); + PyProperty::init(new_prop_ref.clone(), args, vm)?; + + // Copy the name if it exists + if let Some(name) = zelf.name.read().clone() { + *new_prop_ref.name.write() = Some(name); } - .into_ref_with_type(vm, zelf.class().to_owned()) + + Ok(new_prop_ref) } #[pygetset(magic)] @@ -228,6 +291,7 @@ impl Constructor for PyProperty { deleter: PyRwLock::new(None), doc: PyRwLock::new(None), name: PyRwLock::new(None), + getter_doc: AtomicBool::new(false), } .into_ref_with_type(vm, cls) .map(Into::into) @@ -237,13 +301,37 @@ impl Constructor for PyProperty { impl Initializer for PyProperty { type Args = PropertyArgs; - fn init(zelf: PyRef, args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> { - *zelf.getter.write() = args.fget; + fn init(zelf: PyRef, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> { + *zelf.getter.write() = args.fget.clone(); *zelf.setter.write() = args.fset; *zelf.deleter.write() = args.fdel; - *zelf.doc.write() = args.doc; *zelf.name.write() = args.name.map(|a| a.as_object().to_owned()); + // Set doc and getter_doc flag + let mut getter_doc = false; + + // Helper to get doc from getter + let get_getter_doc = |fget: &PyObjectRef| -> Option { + fget.get_attr("__doc__", vm) + .ok() + .filter(|doc| !vm.is_none(doc)) + }; + + let doc = match args.doc { + Some(doc) if !vm.is_none(&doc) => Some(doc), + _ => { + // No explicit doc or doc is None, try to get from getter + args.fget.as_ref().and_then(|fget| { + get_getter_doc(fget).inspect(|_| { + getter_doc = true; + }) + }) + } + }; + + *zelf.doc.write() = doc; + zelf.getter_doc.store(getter_doc, Ordering::Relaxed); + Ok(()) } } From 6e4e9e9a8f70f1a6b5a9898d59867785ae365fb1 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 24 Jun 2025 11:13:46 +0900 Subject: [PATCH 2/5] Fix property.isabstractmethod --- Lib/test/test_property.py | 8 ----- vm/src/builtins/property.rs | 67 ++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py index d2da5827fb..1af33a8af1 100644 --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -128,8 +128,6 @@ def test_property_getter_doc_override(self): self.assertEqual(newgetter.spam, 8) self.assertEqual(newgetter.__class__.spam.__doc__, "new docstring") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_property___isabstractmethod__descriptor(self): for val in (True, False, [], [1], '', '1'): class C(object): @@ -258,8 +256,6 @@ def spam(self): else: raise Exception("AttributeError not raised") - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_docstring_copy(self): @@ -272,8 +268,6 @@ def spam(self): Foo.spam.__doc__, "spam wrapped in property subclass") - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_property_setter_copies_getter_docstring(self): @@ -307,8 +301,6 @@ def spam(self, value): FooSub.spam.__doc__, "spam wrapped in property subclass") - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_property_new_getter_new_docstring(self): diff --git a/vm/src/builtins/property.rs b/vm/src/builtins/property.rs index 008c198aff..c49d31c5fe 100644 --- a/vm/src/builtins/property.rs +++ b/vm/src/builtins/property.rs @@ -255,21 +255,38 @@ impl PyProperty { } #[pygetset(magic)] - fn isabstractmethod(&self, vm: &VirtualMachine) -> PyObjectRef { - let getter_abstract = match self.getter.read().to_owned() { - Some(getter) => getter - .get_attr("__isabstractmethod__", vm) - .unwrap_or_else(|_| vm.ctx.new_bool(false).into()), - _ => vm.ctx.new_bool(false).into(), - }; - let setter_abstract = match self.setter.read().to_owned() { - Some(setter) => setter - .get_attr("__isabstractmethod__", vm) - .unwrap_or_else(|_| vm.ctx.new_bool(false).into()), - _ => vm.ctx.new_bool(false).into(), - }; - vm._or(&setter_abstract, &getter_abstract) - .unwrap_or_else(|_| vm.ctx.new_bool(false).into()) + fn isabstractmethod(&self, vm: &VirtualMachine) -> PyResult { + // Check getter first + if let Some(getter) = self.getter.read().as_ref() { + if let Ok(isabstract) = getter.get_attr("__isabstractmethod__", vm) { + let is_true = isabstract.try_to_bool(vm)?; + if is_true { + return Ok(vm.ctx.new_bool(true).into()); + } + } + } + + // Check setter + if let Some(setter) = self.setter.read().as_ref() { + if let Ok(isabstract) = setter.get_attr("__isabstractmethod__", vm) { + let is_true = isabstract.try_to_bool(vm)?; + if is_true { + return Ok(vm.ctx.new_bool(true).into()); + } + } + } + + // Check deleter + if let Some(deleter) = self.deleter.read().as_ref() { + if let Ok(isabstract) = deleter.get_attr("__isabstractmethod__", vm) { + let is_true = isabstract.try_to_bool(vm)?; + if is_true { + return Ok(vm.ctx.new_bool(true).into()); + } + } + } + + Ok(vm.ctx.new_bool(false).into()) } #[pygetset(magic, setter)] @@ -329,7 +346,25 @@ impl Initializer for PyProperty { } }; - *zelf.doc.write() = doc; + // Check if this is a property subclass + let is_exact_property = zelf.class().is(vm.ctx.types.property_type); + + if is_exact_property { + // For exact property type, store doc in the field + *zelf.doc.write() = doc; + } else { + // For property subclass, set __doc__ as an attribute + let doc_to_set = doc.unwrap_or_else(|| vm.ctx.none()); + match zelf.as_object().set_attr("__doc__", doc_to_set, vm) { + Ok(()) => {} + Err(e) if !getter_doc && e.class().is(vm.ctx.exceptions.attribute_error) => { + // Silently ignore AttributeError for backwards compatibility + // (only when not using getter_doc) + } + Err(e) => return Err(e), + } + } + zelf.getter_doc.store(getter_doc, Ordering::Relaxed); Ok(()) From fbc87f4332c6d64771991abe93156e8112aa96cd Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 24 Jun 2025 11:46:05 +0900 Subject: [PATCH 3/5] Fix property error messages --- Lib/test/test_property.py | 6 --- vm/src/builtins/property.rs | 88 ++++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 27 deletions(-) diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py index 1af33a8af1..8411e903b1 100644 --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -340,20 +340,14 @@ def _format_exc_msg(self, msg): def setUpClass(cls): cls.obj = cls.cls() - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_get_property(self): with self.assertRaisesRegex(AttributeError, self._format_exc_msg("has no getter")): self.obj.foo - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_set_property(self): with self.assertRaisesRegex(AttributeError, self._format_exc_msg("has no setter")): self.obj.foo = None - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_del_property(self): with self.assertRaisesRegex(AttributeError, self._format_exc_msg("has no deleter")): del self.obj.foo diff --git a/vm/src/builtins/property.rs b/vm/src/builtins/property.rs index c49d31c5fe..e1662e260d 100644 --- a/vm/src/builtins/property.rs +++ b/vm/src/builtins/property.rs @@ -57,13 +57,31 @@ impl GetDescriptor for PyProperty { } else if let Some(getter) = zelf.getter.read().as_ref() { getter.call((obj,), vm) } else { - Err(vm.new_attribute_error("property has no getter".to_string())) + let error_msg = zelf.format_property_error(&obj, "getter", vm)?; + Err(vm.new_attribute_error(error_msg)) } } } #[pyclass(with(Constructor, Initializer, GetDescriptor), flags(BASETYPE))] impl PyProperty { + // Helper method to get property name + fn get_property_name(&self, vm: &VirtualMachine) -> Option { + // First check if name was set via __set_name__ + if let Some(name) = self.name.read().as_ref() { + return Some(name.clone()); + } + + // Otherwise try to get __name__ from getter + if let Some(getter) = self.getter.read().as_ref() { + if let Ok(name) = getter.get_attr("__name__", vm) { + return Some(name); + } + } + + None + } + // Descriptor methods #[pyslot] @@ -77,16 +95,18 @@ impl PyProperty { match value { PySetterValue::Assign(value) => { if let Some(setter) = zelf.setter.read().as_ref() { - setter.call((obj, value), vm).map(drop) + setter.call((obj.clone(), value), vm).map(drop) } else { - Err(vm.new_attribute_error("property has no setter".to_owned())) + let error_msg = zelf.format_property_error(&obj, "setter", vm)?; + Err(vm.new_attribute_error(error_msg)) } } PySetterValue::Delete => { if let Some(deleter) = zelf.deleter.read().as_ref() { - deleter.call((obj,), vm).map(drop) + deleter.call((obj.clone(),), vm).map(drop) } else { - Err(vm.new_attribute_error("property has no deleter".to_owned())) + let error_msg = zelf.format_property_error(&obj, "deleter", vm)?; + Err(vm.new_attribute_error(error_msg)) } } } @@ -256,33 +276,32 @@ impl PyProperty { #[pygetset(magic)] fn isabstractmethod(&self, vm: &VirtualMachine) -> PyResult { - // Check getter first + // Helper to check if a method is abstract + let is_abstract = |method: &PyObjectRef| -> PyResult { + match method.get_attr("__isabstractmethod__", vm) { + Ok(isabstract) => isabstract.try_to_bool(vm), + Err(_) => Ok(false), + } + }; + + // Check getter if let Some(getter) = self.getter.read().as_ref() { - if let Ok(isabstract) = getter.get_attr("__isabstractmethod__", vm) { - let is_true = isabstract.try_to_bool(vm)?; - if is_true { - return Ok(vm.ctx.new_bool(true).into()); - } + if is_abstract(getter)? { + return Ok(vm.ctx.new_bool(true).into()); } } // Check setter if let Some(setter) = self.setter.read().as_ref() { - if let Ok(isabstract) = setter.get_attr("__isabstractmethod__", vm) { - let is_true = isabstract.try_to_bool(vm)?; - if is_true { - return Ok(vm.ctx.new_bool(true).into()); - } + if is_abstract(setter)? { + return Ok(vm.ctx.new_bool(true).into()); } } // Check deleter if let Some(deleter) = self.deleter.read().as_ref() { - if let Ok(isabstract) = deleter.get_attr("__isabstractmethod__", vm) { - let is_true = isabstract.try_to_bool(vm)?; - if is_true { - return Ok(vm.ctx.new_bool(true).into()); - } + if is_abstract(deleter)? { + return Ok(vm.ctx.new_bool(true).into()); } } @@ -296,6 +315,33 @@ impl PyProperty { } Ok(()) } + + // Helper method to format property error messages + #[cold] + fn format_property_error( + &self, + obj: &PyObjectRef, + error_type: &str, + vm: &VirtualMachine, + ) -> PyResult { + let prop_name = self.get_property_name(vm); + let obj_type = obj.class(); + let qualname = obj_type.qualname(vm); + + match prop_name { + Some(name) => Ok(format!( + "property {} of {} object has no {}", + name.repr(vm)?, + qualname.repr(vm)?, + error_type + )), + None => Ok(format!( + "property of {} object has no {}", + qualname.repr(vm)?, + error_type + )), + } + } } impl Constructor for PyProperty { From db7dd4fdb4c8061858ce803afdae1ba9dad4803c Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 24 Jun 2025 12:04:08 +0900 Subject: [PATCH 4/5] refactor getter/setter/deleter --- vm/src/builtins/property.rs | 100 +++++++++++------------------------- 1 file changed, 31 insertions(+), 69 deletions(-) diff --git a/vm/src/builtins/property.rs b/vm/src/builtins/property.rs index e1662e260d..a5e8f12587 100644 --- a/vm/src/builtins/property.rs +++ b/vm/src/builtins/property.rs @@ -95,7 +95,7 @@ impl PyProperty { match value { PySetterValue::Assign(value) => { if let Some(setter) = zelf.setter.read().as_ref() { - setter.call((obj.clone(), value), vm).map(drop) + setter.call((obj, value), vm).map(drop) } else { let error_msg = zelf.format_property_error(&obj, "setter", vm)?; Err(vm.new_attribute_error(error_msg)) @@ -103,7 +103,7 @@ impl PyProperty { } PySetterValue::Delete => { if let Some(deleter) = zelf.deleter.read().as_ref() { - deleter.call((obj.clone(),), vm).map(drop) + deleter.call((obj,), vm).map(drop) } else { let error_msg = zelf.format_property_error(&obj, "deleter", vm)?; Err(vm.new_attribute_error(error_msg)) @@ -166,29 +166,33 @@ impl PyProperty { // Python builder functions - #[pymethod] - fn getter( + // Helper method to create a new property with updated attributes + fn clone_property_with( zelf: PyRef, - getter: Option, + new_getter: Option, + new_setter: Option, + new_deleter: Option, vm: &VirtualMachine, ) -> PyResult> { - let new_getter = getter.or_else(|| zelf.fget()); - - // Determine doc based on getter_doc flag + // Determine doc based on getter_doc flag and whether we're updating the getter let doc = if zelf.getter_doc.load(Ordering::Relaxed) && new_getter.is_some() { // If the original property uses getter doc and we have a new getter, // pass Py_None to let __init__ get the doc from the new getter Some(vm.ctx.none()) + } else if zelf.getter_doc.load(Ordering::Relaxed) { + // If original used getter_doc but we're not changing the getter, + // pass None to let init get doc from existing getter + Some(vm.ctx.none()) } else { // Otherwise use the existing doc zelf.doc_getter() }; - // Create property args + // Create property args with updated values let args = PropertyArgs { - fget: new_getter, - fset: zelf.fset(), - fdel: zelf.fdel(), + fget: new_getter.or_else(|| zelf.fget()), + fset: new_setter.or_else(|| zelf.fset()), + fdel: new_deleter.or_else(|| zelf.fdel()), doc, name: None, }; @@ -206,38 +210,22 @@ impl PyProperty { Ok(new_prop_ref) } + #[pymethod] + fn getter( + zelf: PyRef, + getter: Option, + vm: &VirtualMachine, + ) -> PyResult> { + Self::clone_property_with(zelf, getter, None, None, vm) + } + #[pymethod] fn setter( zelf: PyRef, setter: Option, vm: &VirtualMachine, ) -> PyResult> { - // For setter, we need to preserve doc handling from the original property - let doc = if zelf.getter_doc.load(Ordering::Relaxed) { - // If original used getter_doc, pass None to let init get doc from getter - Some(vm.ctx.none()) - } else { - zelf.doc_getter() - }; - - let args = PropertyArgs { - fget: zelf.fget(), - fset: setter.or_else(|| zelf.fset()), - fdel: zelf.fdel(), - doc, - name: None, - }; - - let new_prop = PyProperty::py_new(zelf.class().to_owned(), FuncArgs::default(), vm)?; - let new_prop_ref = new_prop.downcast::().unwrap(); - PyProperty::init(new_prop_ref.clone(), args, vm)?; - - // Copy the name if it exists - if let Some(name) = zelf.name.read().clone() { - *new_prop_ref.name.write() = Some(name); - } - - Ok(new_prop_ref) + Self::clone_property_with(zelf, None, setter, None, vm) } #[pymethod] @@ -246,32 +234,7 @@ impl PyProperty { deleter: Option, vm: &VirtualMachine, ) -> PyResult> { - // For deleter, we need to preserve doc handling from the original property - let doc = if zelf.getter_doc.load(Ordering::Relaxed) { - // If original used getter_doc, pass None to let init get doc from getter - Some(vm.ctx.none()) - } else { - zelf.doc_getter() - }; - - let args = PropertyArgs { - fget: zelf.fget(), - fset: zelf.fset(), - fdel: deleter.or_else(|| zelf.fdel()), - doc, - name: None, - }; - - let new_prop = PyProperty::py_new(zelf.class().to_owned(), FuncArgs::default(), vm)?; - let new_prop_ref = new_prop.downcast::().unwrap(); - PyProperty::init(new_prop_ref.clone(), args, vm)?; - - // Copy the name if it exists - if let Some(name) = zelf.name.read().clone() { - *new_prop_ref.name.write() = Some(name); - } - - Ok(new_prop_ref) + Self::clone_property_with(zelf, None, None, deleter, vm) } #[pygetset(magic)] @@ -365,11 +328,6 @@ impl Initializer for PyProperty { type Args = PropertyArgs; fn init(zelf: PyRef, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> { - *zelf.getter.write() = args.fget.clone(); - *zelf.setter.write() = args.fset; - *zelf.deleter.write() = args.fdel; - *zelf.name.write() = args.name.map(|a| a.as_object().to_owned()); - // Set doc and getter_doc flag let mut getter_doc = false; @@ -411,6 +369,10 @@ impl Initializer for PyProperty { } } + *zelf.getter.write() = args.fget; + *zelf.setter.write() = args.fset; + *zelf.deleter.write() = args.fdel; + *zelf.name.write() = args.name.map(|a| a.as_object().to_owned()); zelf.getter_doc.store(getter_doc, Ordering::Relaxed); Ok(()) From 8e12d9e5a62204031e5459ef17b5482be5ff419b Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 24 Jun 2025 12:37:43 +0900 Subject: [PATCH 5/5] fix property --- Lib/test/test_descr.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 3114527dd0..cb3d053b3b 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2356,8 +2356,6 @@ class D(object): else: self.fail("expected ZeroDivisionError from bad property") - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") def test_properties_doc_attrib(self): @@ -2384,8 +2382,6 @@ def test_testcapi_no_segfault(self): class X(object): p = property(_testcapi.test_with_docstring) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_properties_plus(self): class C(object): foo = property(doc="hello")