@@ -1061,6 +1061,22 @@ pub(crate) fn get_text_signature_from_internal_doc<'a>(
10611061 find_signature ( name, internal_doc) . and_then ( get_signature)
10621062}
10631063
1064+ // _PyType_GetDocFromInternalDoc in CPython
1065+ fn get_doc_from_internal_doc < ' a > ( name : & str , internal_doc : & ' a str ) -> & ' a str {
1066+ // Similar to CPython's _PyType_DocWithoutSignature
1067+ // If the doc starts with the type name and a '(', it's a signature
1068+ if let Some ( doc_without_sig) = find_signature ( name, internal_doc) {
1069+ // Find where the signature ends
1070+ if let Some ( sig_end_pos) = doc_without_sig. find ( SIGNATURE_END_MARKER ) {
1071+ let after_sig = & doc_without_sig[ sig_end_pos + SIGNATURE_END_MARKER . len ( ) ..] ;
1072+ // Return the documentation after the signature, or empty string if none
1073+ return after_sig;
1074+ }
1075+ }
1076+ // If no signature found, return the whole doc
1077+ internal_doc
1078+ }
1079+
10641080impl GetAttr for PyType {
10651081 fn getattro ( zelf : & Py < Self > , name_str : & Py < PyStr > , vm : & VirtualMachine ) -> PyResult {
10661082 #[ cold]
@@ -1122,6 +1138,55 @@ impl Py<PyType> {
11221138 PyTuple :: new_unchecked ( elements. into_boxed_slice ( ) )
11231139 }
11241140
1141+ #[ pygetset( magic) ]
1142+ fn doc ( & self , vm : & VirtualMachine ) -> PyResult {
1143+ // Similar to CPython's type_get_doc
1144+ // For non-heap types (static types), check if there's an internal doc
1145+ if !self . slots . flags . has_feature ( PyTypeFlags :: HEAPTYPE ) {
1146+ if let Some ( internal_doc) = self . slots . doc {
1147+ // Process internal doc, removing signature if present
1148+ let doc_str = get_doc_from_internal_doc ( & self . name ( ) , internal_doc) ;
1149+ return Ok ( vm. ctx . new_str ( doc_str) . into ( ) ) ;
1150+ }
1151+ }
1152+
1153+ // Check if there's a __doc__ in the type's dict
1154+ if let Some ( doc_attr) = self . get_attr ( vm. ctx . intern_str ( "__doc__" ) ) {
1155+ // If it's a descriptor, call its __get__ method
1156+ let descr_get = doc_attr
1157+ . class ( )
1158+ . mro_find_map ( |cls| cls. slots . descr_get . load ( ) ) ;
1159+ if let Some ( descr_get) = descr_get {
1160+ descr_get ( doc_attr, None , Some ( self . to_owned ( ) . into ( ) ) , vm)
1161+ } else {
1162+ Ok ( doc_attr)
1163+ }
1164+ } else {
1165+ Ok ( vm. ctx . none ( ) )
1166+ }
1167+ }
1168+
1169+ #[ pygetset( magic, setter) ]
1170+ fn set_doc ( & self , value : PySetterValue , vm : & VirtualMachine ) -> PyResult < ( ) > {
1171+ // Similar to CPython's type_set_doc
1172+ let value = value. ok_or_else ( || {
1173+ vm. new_type_error ( format ! (
1174+ "cannot delete '__doc__' attribute of type '{}'" ,
1175+ self . name( )
1176+ ) )
1177+ } ) ?;
1178+
1179+ // Check if we can set this special type attribute
1180+ self . check_set_special_type_attr ( & value, identifier ! ( vm, __doc__) , vm) ?;
1181+
1182+ // Set the __doc__ in the type's dict
1183+ self . attributes
1184+ . write ( )
1185+ . insert ( identifier ! ( vm, __doc__) , value) ;
1186+
1187+ Ok ( ( ) )
1188+ }
1189+
11251190 #[ pymethod( magic) ]
11261191 fn dir ( & self ) -> PyList {
11271192 let attributes: Vec < PyObjectRef > = self
0 commit comments