@@ -58,8 +58,10 @@ unsafe impl crate::object::Traverse for PyType {
5858 }
5959}
6060
61+ // PyHeapTypeObject in CPython
6162pub struct HeapTypeExt {
6263 pub name : PyRwLock < PyStrRef > ,
64+ pub qualname : PyRwLock < PyStrRef > ,
6365 pub slots : Option < PyTupleTyped < PyStrRef > > ,
6466 pub sequence_methods : PySequenceMethods ,
6567 pub mapping_methods : PyMappingMethods ,
@@ -143,6 +145,16 @@ impl PyPayload for PyType {
143145 }
144146}
145147
148+ fn downcast_qualname ( value : PyObjectRef , vm : & VirtualMachine ) -> PyResult < PyRef < PyStr > > {
149+ match value. downcast :: < PyStr > ( ) {
150+ Ok ( value) => Ok ( value) ,
151+ Err ( value) => Err ( vm. new_type_error ( format ! (
152+ "can only assign string to __qualname__, not '{}'" ,
153+ value. class( ) . name( )
154+ ) ) ) ,
155+ }
156+ }
157+
146158impl PyType {
147159 pub fn new_simple_heap (
148160 name : & str ,
@@ -171,7 +183,8 @@ impl PyType {
171183
172184 let name = ctx. new_str ( name) ;
173185 let heaptype_ext = HeapTypeExt {
174- name : PyRwLock :: new ( name) ,
186+ name : PyRwLock :: new ( name. clone ( ) ) ,
187+ qualname : PyRwLock :: new ( name) ,
175188 slots : None ,
176189 sequence_methods : PySequenceMethods :: default ( ) ,
177190 mapping_methods : PyMappingMethods :: default ( ) ,
@@ -577,19 +590,12 @@ impl PyType {
577590
578591 #[ pygetset]
579592 pub fn __qualname__ ( & self , vm : & VirtualMachine ) -> PyObjectRef {
580- self . attributes
581- . read ( )
582- . get ( identifier ! ( vm, __qualname__) )
583- . cloned ( )
584- // We need to exclude this method from going into recursion:
585- . and_then ( |found| {
586- if found. fast_isinstance ( vm. ctx . types . getset_type ) {
587- None
588- } else {
589- Some ( found)
590- }
591- } )
592- . unwrap_or_else ( || vm. ctx . new_str ( self . name ( ) . deref ( ) ) . into ( ) )
593+ if let Some ( ref heap_type) = self . heaptype_ext {
594+ heap_type. qualname . read ( ) . clone ( ) . into ( )
595+ } else {
596+ // For static types, return the name
597+ vm. ctx . new_str ( self . name ( ) . deref ( ) ) . into ( )
598+ }
593599 }
594600
595601 #[ pygetset( setter) ]
@@ -607,16 +613,14 @@ impl PyType {
607613 self . name( )
608614 ) )
609615 } ) ?;
610- if !value. class ( ) . fast_issubclass ( vm. ctx . types . str_type ) {
611- return Err ( vm. new_type_error ( format ! (
612- "can only assign string to {}.__qualname__, not '{}'" ,
613- self . name( ) ,
614- value. class( ) . name( )
615- ) ) ) ;
616- }
617- self . attributes
618- . write ( )
619- . insert ( identifier ! ( vm, __qualname__) , value) ;
616+
617+ let str_value = downcast_qualname ( value, vm) ?;
618+
619+ let heap_type = self
620+ . heaptype_ext
621+ . as_ref ( )
622+ . expect ( "HEAPTYPE should have heaptype_ext" ) ;
623+ * heap_type. qualname . write ( ) = str_value;
620624 Ok ( ( ) )
621625 }
622626
@@ -856,6 +860,14 @@ impl Constructor for PyType {
856860 ( metatype, base. to_owned ( ) , bases)
857861 } ;
858862
863+ let qualname = dict
864+ . pop_item ( identifier ! ( vm, __qualname__) . as_object ( ) , vm) ?
865+ . map ( |obj| downcast_qualname ( obj, vm) )
866+ . transpose ( ) ?
867+ . unwrap_or_else ( || {
868+ // If __qualname__ is not provided, we can use the name as default
869+ name. clone ( )
870+ } ) ;
859871 let mut attributes = dict. to_attributes ( vm) ;
860872
861873 if let Some ( f) = attributes. get_mut ( identifier ! ( vm, __init_subclass__) ) {
@@ -882,10 +894,6 @@ impl Constructor for PyType {
882894 }
883895 }
884896
885- attributes
886- . entry ( identifier ! ( vm, __qualname__) )
887- . or_insert_with ( || name. clone ( ) . into ( ) ) ;
888-
889897 if attributes. get ( identifier ! ( vm, __eq__) ) . is_some ( )
890898 && attributes. get ( identifier ! ( vm, __hash__) ) . is_none ( )
891899 {
@@ -952,6 +960,7 @@ impl Constructor for PyType {
952960 } ;
953961 let heaptype_ext = HeapTypeExt {
954962 name : PyRwLock :: new ( name) ,
963+ qualname : PyRwLock :: new ( qualname) ,
955964 slots : heaptype_slots. to_owned ( ) ,
956965 sequence_methods : PySequenceMethods :: default ( ) ,
957966 mapping_methods : PyMappingMethods :: default ( ) ,
0 commit comments