@@ -15,10 +15,15 @@ use crate::attributes::{
1515} ; 
1616use  crate :: combine_errors:: CombineErrors ; 
1717#[ cfg( feature = "experimental-inspect" ) ]  
18- use  crate :: introspection:: { class_introspection_code,  introspection_id_const} ; 
18+ use  crate :: introspection:: { 
19+     class_introspection_code,  function_introspection_code,  introspection_id_const, 
20+     unique_element_id, 
21+ } ; 
1922use  crate :: konst:: { ConstAttributes ,  ConstSpec } ; 
2023use  crate :: method:: { FnArg ,  FnSpec ,  PyArg ,  RegularArg } ; 
2124use  crate :: pyfunction:: ConstructorAttribute ; 
25+ #[ cfg( feature = "experimental-inspect" ) ]  
26+ use  crate :: pyfunction:: FunctionSignature ; 
2227use  crate :: pyimpl:: { gen_py_const,  get_cfg_attributes,  PyClassMethodsType } ; 
2328use  crate :: pymethod:: { 
2429    impl_py_class_attribute,  impl_py_getter_def,  impl_py_setter_def,  MethodAndMethodDef , 
@@ -859,7 +864,20 @@ fn implement_py_formatting(
859864            fmt_impl
860865        } 
861866    } ; 
862-     let  fmt_slot = generate_protocol_slot ( ty,  & mut  fmt_impl,  & __STR__,  "__str__" ,  ctx) . unwrap ( ) ; 
867+     let  fmt_slot = generate_protocol_slot ( 
868+         ty, 
869+         & mut  fmt_impl, 
870+         & __STR__, 
871+         "__str__" , 
872+         #[ cfg( feature = "experimental-inspect" ) ]  
873+         FunctionIntrospectionData  { 
874+             names :  & [ "__str__" ] , 
875+             arguments :  Vec :: new ( ) , 
876+             returns :  parse_quote !  {  :: std:: string:: String  } , 
877+         } , 
878+         ctx, 
879+     ) 
880+     . unwrap ( ) ; 
863881    ( fmt_impl,  fmt_slot) 
864882} 
865883
@@ -935,8 +953,7 @@ fn impl_simple_enum(
935953                } 
936954            } 
937955        } ; 
938-         let  repr_slot =
939-             generate_default_protocol_slot ( & ty,  & mut  repr_impl,  & __REPR__,  ctx) . unwrap ( ) ; 
956+         let  repr_slot = generate_default_protocol_slot ( & ty,  & mut  repr_impl,  & __REPR__,  ctx) ?; 
940957        ( repr_impl,  repr_slot) 
941958    } ; 
942959
@@ -958,12 +975,18 @@ fn impl_simple_enum(
958975                } 
959976            } 
960977        } ; 
961-         let  int_slot = generate_default_protocol_slot ( & ty,  & mut  int_impl,  & __INT__,  ctx) . unwrap ( ) ; 
978+         let  int_slot = generate_default_protocol_slot ( & ty,  & mut  int_impl,  & __INT__,  ctx) ? ; 
962979        ( int_impl,  int_slot) 
963980    } ; 
964981
965-     let  ( default_richcmp,  default_richcmp_slot)  =
966-         pyclass_richcmp_simple_enum ( & args. options ,  & ty,  repr_type,  ctx) ?; 
982+     let  ( default_richcmp,  default_richcmp_slot)  = pyclass_richcmp_simple_enum ( 
983+         & args. options , 
984+         & ty, 
985+         repr_type, 
986+         #[ cfg( feature = "experimental-inspect" ) ]  
987+         & get_class_python_name ( cls,  args) . to_string ( ) , 
988+         ctx, 
989+     ) ?; 
967990    let  ( default_hash,  default_hash_slot)  = pyclass_hash ( & args. options ,  & ty,  ctx) ?; 
968991
969992    let  mut  default_slots = vec ! [ default_repr_slot,  default_int_slot] ; 
@@ -1070,12 +1093,18 @@ fn impl_complex_enum(
10701093                    } 
10711094                } 
10721095            } ) ; 
1073- 
1096+         let  output_type = if  cfg ! ( feature = "experimental-inspect" )  { 
1097+             let  full_name = get_class_python_module_and_name ( cls,  & args) ; 
1098+             quote !  {  const  OUTPUT_TYPE :  & ' static  str  = #full_name;  } 
1099+         }  else  { 
1100+             quote !  { } 
1101+         } ; 
10741102        quote !  { 
10751103            impl <' py> #pyo3_path:: conversion:: IntoPyObject <' py> for  #cls { 
10761104                type  Target  = Self ; 
10771105                type  Output  = #pyo3_path:: Bound <' py,  <Self  as  #pyo3_path:: conversion:: IntoPyObject <' py>>:: Target >; 
10781106                type  Error  = #pyo3_path:: PyErr ; 
1107+                 #output_type
10791108
10801109                fn  into_pyobject( self ,  py:  #pyo3_path:: Python <' py>)  -> :: std:: result:: Result <
10811110                    <Self  as  #pyo3_path:: conversion:: IntoPyObject >:: Output , 
@@ -1462,20 +1491,59 @@ fn gen_complex_enum_variant_class_ident(enum_: &syn::Ident, variant: &syn::Ident
14621491    format_ident ! ( "{}_{}" ,  enum_,  variant) 
14631492} 
14641493
1494+ #[ cfg( feature = "experimental-inspect" ) ]  
1495+ struct  FunctionIntrospectionData < ' a >  { 
1496+     names :  & ' a  [ & ' a  str ] , 
1497+     arguments :  Vec < FnArg < ' a > > , 
1498+     returns :  syn:: Type , 
1499+ } 
1500+ 
14651501fn  generate_protocol_slot ( 
14661502    cls :  & syn:: Type , 
14671503    method :  & mut  syn:: ImplItemFn , 
14681504    slot :  & SlotDef , 
14691505    name :  & str , 
1506+     #[ cfg( feature = "experimental-inspect" ) ]   introspection_data :  FunctionIntrospectionData < ' _ > , 
14701507    ctx :  & Ctx , 
14711508)  -> syn:: Result < MethodAndSlotDef >  { 
14721509    let  spec = FnSpec :: parse ( 
14731510        & mut  method. sig , 
14741511        & mut  Vec :: new ( ) , 
14751512        PyFunctionOptions :: default ( ) , 
1476-     ) 
1477-     . unwrap ( ) ; 
1478-     slot. generate_type_slot ( & syn:: parse_quote!( #cls) ,  & spec,  name,  ctx) 
1513+     ) ?; 
1514+     #[ cfg_attr( not( feature = "experimental-inspect" ) ,  allow( unused_mut) ) ]  
1515+     let  mut  def = slot. generate_type_slot ( & syn:: parse_quote!( #cls) ,  & spec,  name,  ctx) ?; 
1516+     #[ cfg( feature = "experimental-inspect" ) ]  
1517+     { 
1518+         // We generate introspection data 
1519+         let  associated_method = def. associated_method ; 
1520+         let  signature = FunctionSignature :: from_arguments ( introspection_data. arguments ) ; 
1521+         let  returns = introspection_data. returns ; 
1522+         let  introspection = introspection_data
1523+             . names 
1524+             . iter ( ) 
1525+             . map ( |name| { 
1526+                 function_introspection_code ( 
1527+                     & ctx. pyo3_path , 
1528+                     None , 
1529+                     name, 
1530+                     & signature, 
1531+                     Some ( "self" ) , 
1532+                     parse_quote ! ( -> #returns) , 
1533+                     [ ] , 
1534+                     Some ( cls) , 
1535+                 ) 
1536+             } ) 
1537+             . collect :: < Vec < _ > > ( ) ; 
1538+         let  const_name = format_ident ! ( "_{}" ,  unique_element_id( ) ) ;  // We need an explicit name here 
1539+         def. associated_method  = quote !  { 
1540+             #associated_method
1541+             const  #const_name:  ( )  = { 
1542+                 #( #introspection) * 
1543+             } ; 
1544+         } ; 
1545+     } 
1546+     Ok ( def) 
14791547} 
14801548
14811549fn  generate_default_protocol_slot ( 
@@ -1488,8 +1556,7 @@ fn generate_default_protocol_slot(
14881556        & mut  method. sig , 
14891557        & mut  Vec :: new ( ) , 
14901558        PyFunctionOptions :: default ( ) , 
1491-     ) 
1492-     . unwrap ( ) ; 
1559+     ) ?; 
14931560    let  name = spec. name . to_string ( ) ; 
14941561    slot. generate_type_slot ( 
14951562        & syn:: parse_quote!( #cls) , 
@@ -1913,10 +1980,10 @@ fn pyclass_richcmp_simple_enum(
19131980    options :  & PyClassPyO3Options , 
19141981    cls :  & syn:: Type , 
19151982    repr_type :  & syn:: Ident , 
1983+     #[ cfg( feature = "experimental-inspect" ) ]   class_name :  & str , 
19161984    ctx :  & Ctx , 
19171985)  -> Result < ( Option < syn:: ImplItemFn > ,  Option < MethodAndSlotDef > ) >  { 
19181986    let  Ctx  {  pyo3_path,  .. }  = ctx; 
1919- 
19201987    if  let  Some ( eq_int)  = options. eq_int  { 
19211988        ensure_spanned ! ( options. eq. is_some( ) ,  eq_int. span( )  => "The `eq_int` option requires the `eq` option." ) ; 
19221989    } 
@@ -1967,9 +2034,36 @@ fn pyclass_richcmp_simple_enum(
19672034        } 
19682035    } ; 
19692036    let  richcmp_slot = if  options. eq . is_some ( )  { 
1970-         generate_protocol_slot ( cls,  & mut  richcmp_impl,  & __RICHCMP__,  "__richcmp__" ,  ctx) . unwrap ( ) 
2037+         generate_protocol_slot ( 
2038+             cls, 
2039+             & mut  richcmp_impl, 
2040+             & __RICHCMP__, 
2041+             "__richcmp__" , 
2042+             #[ cfg( feature = "experimental-inspect" ) ]  
2043+             FunctionIntrospectionData  { 
2044+                 names :  & [ "__eq__" ,  "__ne__" ] , 
2045+                 arguments :  vec ! [ FnArg :: Regular ( RegularArg  { 
2046+                     name:  Cow :: Owned ( format_ident!( "other" ) ) , 
2047+                     // we need to set a type, let's pick something small, it is overridden by annotation anyway 
2048+                     ty:  & parse_quote!( !) , 
2049+                     from_py_with:  None , 
2050+                     default_value:  None , 
2051+                     option_wrapped_type:  None , 
2052+                     annotation:  Some ( match  ( options. eq. is_some( ) ,  options. eq_int. is_some( ) )  { 
2053+                         ( true ,  true )  => { 
2054+                             format!( "{class_name} | int" ) 
2055+                         } 
2056+                         ( true ,  false )  => class_name. into( ) , 
2057+                         ( false ,  true )  => "int" . into( ) , 
2058+                         ( false ,  false )  => unreachable!( ) , 
2059+                     } ) , 
2060+                 } ) ] , 
2061+                 returns :  parse_quote !  {  :: std:: primitive:: bool  } , 
2062+             } , 
2063+             ctx, 
2064+         ) ?
19712065    }  else  { 
1972-         generate_default_protocol_slot ( cls,  & mut  richcmp_impl,  & __RICHCMP__,  ctx) . unwrap ( ) 
2066+         generate_default_protocol_slot ( cls,  & mut  richcmp_impl,  & __RICHCMP__,  ctx) ? 
19732067    } ; 
19742068    Ok ( ( Some ( richcmp_impl) ,  Some ( richcmp_slot) ) ) 
19752069} 
@@ -2004,9 +2098,30 @@ fn pyclass_richcmp(
20042098                } 
20052099            } 
20062100        } ; 
2007-         let  richcmp_slot =
2008-             generate_protocol_slot ( cls,  & mut  richcmp_impl,  & __RICHCMP__,  "__richcmp__" ,  ctx) 
2009-                 . unwrap ( ) ; 
2101+         let  richcmp_slot = generate_protocol_slot ( 
2102+             cls, 
2103+             & mut  richcmp_impl, 
2104+             & __RICHCMP__, 
2105+             "__richcmp__" , 
2106+             #[ cfg( feature = "experimental-inspect" ) ]  
2107+             FunctionIntrospectionData  { 
2108+                 names :  if  options. ord . is_some ( )  { 
2109+                     & [ "__eq__" ,  "__ne__" ,  "__lt__" ,  "__le__" ,  "__gt__" ,  "__ge__" ] 
2110+                 }  else  { 
2111+                     & [ "__eq__" ,  "__ne__" ] 
2112+                 } , 
2113+                 arguments :  vec ! [ FnArg :: Regular ( RegularArg  { 
2114+                     name:  Cow :: Owned ( format_ident!( "other" ) ) , 
2115+                     ty:  & parse_quote!( & #cls) , 
2116+                     from_py_with:  None , 
2117+                     default_value:  None , 
2118+                     option_wrapped_type:  None , 
2119+                     annotation:  None , 
2120+                 } ) ] , 
2121+                 returns :  parse_quote !  {  :: std:: primitive:: bool  } , 
2122+             } , 
2123+             ctx, 
2124+         ) ?; 
20102125        Ok ( ( Some ( richcmp_impl) ,  Some ( richcmp_slot) ) ) 
20112126    }  else  { 
20122127        Ok ( ( None ,  None ) ) 
@@ -2033,8 +2148,19 @@ fn pyclass_hash(
20332148                    :: std:: hash:: Hasher :: finish( & s) 
20342149                } 
20352150            } ; 
2036-             let  hash_slot =
2037-                 generate_protocol_slot ( cls,  & mut  hash_impl,  & __HASH__,  "__hash__" ,  ctx) . unwrap ( ) ; 
2151+             let  hash_slot = generate_protocol_slot ( 
2152+                 cls, 
2153+                 & mut  hash_impl, 
2154+                 & __HASH__, 
2155+                 "__hash__" , 
2156+                 #[ cfg( feature = "experimental-inspect" ) ]  
2157+                 FunctionIntrospectionData  { 
2158+                     names :  & [ "__hash__" ] , 
2159+                     arguments :  Vec :: new ( ) , 
2160+                     returns :  parse_quote !  {  :: std:: primitive:: u64  } , 
2161+                 } , 
2162+                 ctx, 
2163+             ) ?; 
20382164            Ok ( ( Some ( hash_impl) ,  Some ( hash_slot) ) ) 
20392165        } 
20402166        None  => Ok ( ( None ,  None ) ) , 
@@ -2462,7 +2588,7 @@ impl<'a> PyClassImplsBuilder<'a> {
24622588        let  cls = self . cls ; 
24632589        let  Ctx  {  pyo3_path,  .. }  = ctx; 
24642590
2465-         self . attr . options . freelist . as_ref ( ) . map_or ( quote ! { } ,  |freelist| { 
2591+         self . attr . options . freelist . as_ref ( ) . map_or ( quote !   { } ,  |freelist| { 
24662592            let  freelist = & freelist. value ; 
24672593            quote !  { 
24682594                impl  #pyo3_path:: impl_:: pyclass:: PyClassWithFreeList  for  #cls { 
0 commit comments