Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Lib/test/test_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ def test_gh_115618(self):
self.assertIsNone(prop.fdel)
self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_property_name(self):
def getter(self):
return 42
Expand Down
44 changes: 35 additions & 9 deletions vm/src/builtins/property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,30 @@ impl GetDescriptor for PyProperty {
#[pyclass(with(Constructor, Initializer, GetDescriptor), flags(BASETYPE))]
impl PyProperty {
// Helper method to get property name
fn get_property_name(&self, vm: &VirtualMachine) -> Option<PyObjectRef> {
// Returns the name if available, None if not found, or propagates errors
fn get_property_name(&self, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>> {
// First check if name was set via __set_name__
if let Some(name) = self.name.read().as_ref() {
return Some(name.clone());
return Ok(Some(name.clone()));
}

// Otherwise try to get __name__ from getter
if let Some(getter) = self.getter.read().as_ref()
&& let Ok(name) = getter.get_attr("__name__", vm)
{
return Some(name);
if let Some(getter) = self.getter.read().as_ref() {
match getter.get_attr("__name__", vm) {
Ok(name) => Ok(Some(name)),
Err(e) => {
// If it's an AttributeError from the getter, return None
// Otherwise, propagate the original exception (e.g., RuntimeError)
if e.class().is(vm.ctx.exceptions.attribute_error) {
Ok(None)
} else {
Err(e)
}
}
}
} else {
Ok(None)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if let Some(getter) = self.getter.read().as_ref() {
match getter.get_attr("__name__", vm) {
Ok(name) => Ok(Some(name)),
Err(e) => {
// If it's an AttributeError from the getter, return None
// Otherwise, propagate the original exception (e.g., RuntimeError)
if e.class().is(vm.ctx.exceptions.attribute_error) {
Ok(None)
} else {
Err(e)
}
}
}
} else {
Ok(None)
let Some(getter) = self.getter.read().as_ref() {
return Ok(None);
};
match getter.get_attr("__name__", vm) {
Ok(name) => Ok(Some(name)),
Err(e) => {
// If it's an AttributeError from the getter, return None
// Otherwise, propagate the original exception (e.g., RuntimeError)
if e.class().is(vm.ctx.exceptions.attribute_error) {
Ok(None)
} else {
Err(e)
}
}

}

None
}

// Descriptor methods
Expand Down Expand Up @@ -143,6 +153,22 @@ impl PyProperty {
self.deleter.read().clone()
}

#[pygetset(name = "__name__")]
fn name_getter(&self, vm: &VirtualMachine) -> PyResult {
// Use get_property_name helper to get the name
match self.get_property_name(vm)? {
Some(name) => Ok(name),
None => Err(
vm.new_attribute_error("'property' object has no attribute '__name__'".to_owned())
),
}
}

#[pygetset(name = "__name__", setter)]
fn name_setter(&self, value: PyObjectRef) {
*self.name.write() = Some(value);
}

fn doc_getter(&self) -> Option<PyObjectRef> {
self.doc.read().clone()
}
Expand Down Expand Up @@ -288,7 +314,7 @@ impl PyProperty {
error_type: &str,
vm: &VirtualMachine,
) -> PyResult<String> {
let prop_name = self.get_property_name(vm);
let prop_name = self.get_property_name(vm)?;
let obj_type = obj.class();
let qualname = obj_type.__qualname__(vm);

Expand Down
Loading