@@ -57,13 +57,31 @@ impl GetDescriptor for PyProperty {
5757 } else if let Some ( getter) = zelf. getter . read ( ) . as_ref ( ) {
5858 getter. call ( ( obj, ) , vm)
5959 } else {
60- Err ( vm. new_attribute_error ( "property has no getter" . to_string ( ) ) )
60+ let error_msg = zelf. format_property_error ( & obj, "getter" , vm) ?;
61+ Err ( vm. new_attribute_error ( error_msg) )
6162 }
6263 }
6364}
6465
6566#[ pyclass( with( Constructor , Initializer , GetDescriptor ) , flags( BASETYPE ) ) ]
6667impl PyProperty {
68+ // Helper method to get property name
69+ fn get_property_name ( & self , vm : & VirtualMachine ) -> Option < PyObjectRef > {
70+ // First check if name was set via __set_name__
71+ if let Some ( name) = self . name . read ( ) . as_ref ( ) {
72+ return Some ( name. clone ( ) ) ;
73+ }
74+
75+ // Otherwise try to get __name__ from getter
76+ if let Some ( getter) = self . getter . read ( ) . as_ref ( ) {
77+ if let Ok ( name) = getter. get_attr ( "__name__" , vm) {
78+ return Some ( name) ;
79+ }
80+ }
81+
82+ None
83+ }
84+
6785 // Descriptor methods
6886
6987 #[ pyslot]
@@ -77,16 +95,18 @@ impl PyProperty {
7795 match value {
7896 PySetterValue :: Assign ( value) => {
7997 if let Some ( setter) = zelf. setter . read ( ) . as_ref ( ) {
80- setter. call ( ( obj, value) , vm) . map ( drop)
98+ setter. call ( ( obj. clone ( ) , value) , vm) . map ( drop)
8199 } else {
82- Err ( vm. new_attribute_error ( "property has no setter" . to_owned ( ) ) )
100+ let error_msg = zelf. format_property_error ( & obj, "setter" , vm) ?;
101+ Err ( vm. new_attribute_error ( error_msg) )
83102 }
84103 }
85104 PySetterValue :: Delete => {
86105 if let Some ( deleter) = zelf. deleter . read ( ) . as_ref ( ) {
87- deleter. call ( ( obj, ) , vm) . map ( drop)
106+ deleter. call ( ( obj. clone ( ) , ) , vm) . map ( drop)
88107 } else {
89- Err ( vm. new_attribute_error ( "property has no deleter" . to_owned ( ) ) )
108+ let error_msg = zelf. format_property_error ( & obj, "deleter" , vm) ?;
109+ Err ( vm. new_attribute_error ( error_msg) )
90110 }
91111 }
92112 }
@@ -256,33 +276,32 @@ impl PyProperty {
256276
257277 #[ pygetset( magic) ]
258278 fn isabstractmethod ( & self , vm : & VirtualMachine ) -> PyResult {
259- // Check getter first
279+ // Helper to check if a method is abstract
280+ let is_abstract = |method : & PyObjectRef | -> PyResult < bool > {
281+ match method. get_attr ( "__isabstractmethod__" , vm) {
282+ Ok ( isabstract) => isabstract. try_to_bool ( vm) ,
283+ Err ( _) => Ok ( false ) ,
284+ }
285+ } ;
286+
287+ // Check getter
260288 if let Some ( getter) = self . getter . read ( ) . as_ref ( ) {
261- if let Ok ( isabstract) = getter. get_attr ( "__isabstractmethod__" , vm) {
262- let is_true = isabstract. try_to_bool ( vm) ?;
263- if is_true {
264- return Ok ( vm. ctx . new_bool ( true ) . into ( ) ) ;
265- }
289+ if is_abstract ( getter) ? {
290+ return Ok ( vm. ctx . new_bool ( true ) . into ( ) ) ;
266291 }
267292 }
268293
269294 // Check setter
270295 if let Some ( setter) = self . setter . read ( ) . as_ref ( ) {
271- if let Ok ( isabstract) = setter. get_attr ( "__isabstractmethod__" , vm) {
272- let is_true = isabstract. try_to_bool ( vm) ?;
273- if is_true {
274- return Ok ( vm. ctx . new_bool ( true ) . into ( ) ) ;
275- }
296+ if is_abstract ( setter) ? {
297+ return Ok ( vm. ctx . new_bool ( true ) . into ( ) ) ;
276298 }
277299 }
278300
279301 // Check deleter
280302 if let Some ( deleter) = self . deleter . read ( ) . as_ref ( ) {
281- if let Ok ( isabstract) = deleter. get_attr ( "__isabstractmethod__" , vm) {
282- let is_true = isabstract. try_to_bool ( vm) ?;
283- if is_true {
284- return Ok ( vm. ctx . new_bool ( true ) . into ( ) ) ;
285- }
303+ if is_abstract ( deleter) ? {
304+ return Ok ( vm. ctx . new_bool ( true ) . into ( ) ) ;
286305 }
287306 }
288307
@@ -296,6 +315,33 @@ impl PyProperty {
296315 }
297316 Ok ( ( ) )
298317 }
318+
319+ // Helper method to format property error messages
320+ #[ cold]
321+ fn format_property_error (
322+ & self ,
323+ obj : & PyObjectRef ,
324+ error_type : & str ,
325+ vm : & VirtualMachine ,
326+ ) -> PyResult < String > {
327+ let prop_name = self . get_property_name ( vm) ;
328+ let obj_type = obj. class ( ) ;
329+ let qualname = obj_type. qualname ( vm) ;
330+
331+ match prop_name {
332+ Some ( name) => Ok ( format ! (
333+ "property {} of {} object has no {}" ,
334+ name. repr( vm) ?,
335+ qualname. repr( vm) ?,
336+ error_type
337+ ) ) ,
338+ None => Ok ( format ! (
339+ "property of {} object has no {}" ,
340+ qualname. repr( vm) ?,
341+ error_type
342+ ) ) ,
343+ }
344+ }
299345}
300346
301347impl Constructor for PyProperty {
0 commit comments