@@ -1446,63 +1446,85 @@ impl Representable for PyType {
14461446 }
14471447}
14481448
1449- fn find_base_dict_descr ( cls : & Py < PyType > , vm : & VirtualMachine ) -> Option < PyObjectRef > {
1450- let dict_attr = identifier ! ( vm, __dict__) ;
1451-
1452- // First check if the class itself has a user-defined __dict__ property
1453- if let Some ( descr) = cls. attributes . read ( ) . get ( & dict_attr) {
1454- // If it's a property (user-defined), return it
1455- if descr. class ( ) . is ( vm. ctx . types . property_type ) {
1456- return Some ( descr. clone ( ) ) ;
1449+ // Equivalent to CPython's get_builtin_base_with_dict
1450+ fn get_builtin_base_with_dict ( typ : & Py < PyType > , vm : & VirtualMachine ) -> Option < PyTypeRef > {
1451+ let mut current = Some ( typ. to_owned ( ) ) ;
1452+ while let Some ( t) = current {
1453+ // In CPython: type->tp_dictoffset != 0 && !(type->tp_flags & Py_TPFLAGS_HEAPTYPE)
1454+ // Special case: type itself is a builtin with dict support
1455+ if t. is ( vm. ctx . types . type_type ) {
1456+ return Some ( t) ;
1457+ }
1458+ // We check HAS_DICT flag (equivalent to tp_dictoffset != 0) and HEAPTYPE
1459+ if t. slots . flags . contains ( PyTypeFlags :: HAS_DICT )
1460+ && !t. slots . flags . contains ( PyTypeFlags :: HEAPTYPE )
1461+ {
1462+ return Some ( t) ;
14571463 }
1458- // Skip getset descriptors in the current class to avoid recursion
1464+ current = t . __base__ ( ) ;
14591465 }
1466+ None
1467+ }
14601468
1461- // Then check bases (like original implementation)
1462- cls. iter_base_chain ( ) . skip ( 1 ) . find_map ( |cls| {
1463- // TODO: should actually be some translation of:
1464- // cls.slot_dictoffset != 0 && !cls.flags.contains(HEAPTYPE)
1465- if cls. is ( vm. ctx . types . type_type ) {
1466- cls. get_attr ( dict_attr)
1467- } else {
1468- None
1469- }
1470- } )
1469+ // Equivalent to CPython's get_dict_descriptor
1470+ fn get_dict_descriptor ( base : & Py < PyType > , vm : & VirtualMachine ) -> Option < PyObjectRef > {
1471+ let dict_attr = identifier ! ( vm, __dict__) ;
1472+ // Use _PyType_Lookup (which is lookup_ref in RustPython)
1473+ base. lookup_ref ( & dict_attr, vm)
14711474}
14721475
14731476fn subtype_get_dict ( obj : PyObjectRef , vm : & VirtualMachine ) -> PyResult {
1474- // TODO: obj.class().as_pyref() need to be supported
1475- let ret = match find_base_dict_descr ( obj. class ( ) , vm) {
1476- Some ( descr) => vm. call_get_descriptor ( & descr, obj) . unwrap_or_else ( || {
1477+ let base = get_builtin_base_with_dict ( obj. class ( ) , vm) ;
1478+
1479+ if let Some ( base_type) = base {
1480+ if let Some ( descr) = get_dict_descriptor ( & base_type, vm) {
1481+ // Call the descriptor's tp_descr_get
1482+ vm. call_get_descriptor ( & descr, obj. clone ( ) )
1483+ . unwrap_or_else ( || {
1484+ Err ( vm. new_type_error ( format ! (
1485+ "this __dict__ descriptor does not support '{}' objects" ,
1486+ obj. class( ) . name( )
1487+ ) ) )
1488+ } )
1489+ } else {
14771490 Err ( vm. new_type_error ( format ! (
14781491 "this __dict__ descriptor does not support '{}' objects" ,
1479- descr . class( )
1492+ obj . class( ) . name ( )
14801493 ) ) )
1481- } ) ?,
1482- None => object:: object_get_dict ( obj, vm) ?. into ( ) ,
1483- } ;
1484- Ok ( ret)
1494+ }
1495+ } else {
1496+ // PyObject_GenericGetDict
1497+ object:: object_get_dict ( obj, vm) . map ( Into :: into)
1498+ }
14851499}
14861500
14871501fn subtype_set_dict ( obj : PyObjectRef , value : PyObjectRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
1488- let cls = obj. class ( ) ;
1489- match find_base_dict_descr ( cls, vm) {
1490- Some ( descr) => {
1502+ // Following CPython's subtype_setdict exactly
1503+ let base = get_builtin_base_with_dict ( obj. class ( ) , vm) ;
1504+
1505+ if let Some ( base_type) = base {
1506+ if let Some ( descr) = get_dict_descriptor ( & base_type, vm) {
1507+ // Call the descriptor's tp_descr_set
14911508 let descr_set = descr
14921509 . class ( )
14931510 . mro_find_map ( |cls| cls. slots . descr_set . load ( ) )
14941511 . ok_or_else ( || {
14951512 vm. new_type_error ( format ! (
14961513 "this __dict__ descriptor does not support '{}' objects" ,
1497- cls . name( )
1514+ obj . class ( ) . name( )
14981515 ) )
14991516 } ) ?;
15001517 descr_set ( & descr, obj, PySetterValue :: Assign ( value) , vm)
1518+ } else {
1519+ Err ( vm. new_type_error ( format ! (
1520+ "this __dict__ descriptor does not support '{}' objects" ,
1521+ obj. class( ) . name( )
1522+ ) ) )
15011523 }
1502- None => {
1503- object :: object_set_dict ( obj , value . try_into_value ( vm ) ? , vm ) ? ;
1504- Ok ( ( ) )
1505- }
1524+ } else {
1525+ // PyObject_GenericSetDict
1526+ object :: object_set_dict ( obj , value . try_into_value ( vm ) ? , vm ) ? ;
1527+ Ok ( ( ) )
15061528 }
15071529}
15081530
0 commit comments