@@ -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